-rw-r--r-- | src/findkey.c | 20 | ||||
-rw-r--r-- | src/gdbm.h.in | 4 | ||||
-rw-r--r-- | src/gdbmdefs.h | 17 | ||||
-rw-r--r-- | src/gdbmerrno.c | 3 | ||||
-rw-r--r-- | src/gdbmtool.c | 27 | ||||
-rw-r--r-- | src/mmap.c | 22 | ||||
-rw-r--r-- | src/recover.c | 16 | ||||
-rw-r--r-- | src/systems.h | 1 |
8 files changed, 102 insertions, 8 deletions
diff --git a/src/findkey.c b/src/findkey.c index 7638b04..bd9fd83 100644 --- a/src/findkey.c +++ b/src/findkey.c | |||
@@ -13,41 +13,61 @@ | |||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. | 15 | GNU General Public License for more details. |
16 | 16 | ||
17 | You should have received a copy of the GNU General Public License | 17 | You should have received a copy of the GNU General Public License |
18 | along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ | 18 | along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ |
19 | 19 | ||
20 | /* Include system configuration before all else. */ | 20 | /* Include system configuration before all else. */ |
21 | #include "autoconf.h" | 21 | #include "autoconf.h" |
22 | 22 | ||
23 | #include "gdbmdefs.h" | 23 | #include "gdbmdefs.h" |
24 | 24 | ||
25 | int | ||
26 | gdbm_bucket_element_valid_p (GDBM_FILE dbf, int elem_loc) | ||
27 | { | ||
28 | return | ||
29 | elem_loc < dbf->header->bucket_elems | ||
30 | && dbf->bucket->h_table[elem_loc].hash_value != -1 | ||
31 | && dbf->bucket->h_table[elem_loc].key_size >= 0 | ||
32 | && off_t_sum_ok (dbf->bucket->h_table[elem_loc].data_pointer, | ||
33 | dbf->bucket->h_table[elem_loc].key_size) | ||
34 | && dbf->bucket->h_table[elem_loc].data_size >= 0 | ||
35 | && off_t_sum_ok (dbf->bucket->h_table[elem_loc].data_pointer | ||
36 | + dbf->bucket->h_table[elem_loc].key_size, | ||
37 | dbf->bucket->h_table[elem_loc].data_size); | ||
38 | } | ||
25 | 39 | ||
26 | /* Read the data found in bucket entry ELEM_LOC in file DBF and | 40 | /* Read the data found in bucket entry ELEM_LOC in file DBF and |
27 | return a pointer to it. Also, cache the read value. */ | 41 | return a pointer to it. Also, cache the read value. */ |
28 | 42 | ||
29 | char * | 43 | char * |
30 | _gdbm_read_entry (GDBM_FILE dbf, int elem_loc) | 44 | _gdbm_read_entry (GDBM_FILE dbf, int elem_loc) |
31 | { | 45 | { |
32 | int rc; | 46 | int rc; |
33 | int key_size; | 47 | int key_size; |
34 | int data_size; | 48 | int data_size; |
35 | off_t file_pos; | 49 | off_t file_pos; |
36 | data_cache_elem *data_ca; | 50 | data_cache_elem *data_ca; |
37 | 51 | ||
38 | /* Is it already in the cache? */ | 52 | /* Is it already in the cache? */ |
39 | if (dbf->cache_entry->ca_data.elem_loc == elem_loc) | 53 | if (dbf->cache_entry->ca_data.elem_loc == elem_loc) |
40 | return dbf->cache_entry->ca_data.dptr; | 54 | return dbf->cache_entry->ca_data.dptr; |
41 | 55 | ||
56 | if (!gdbm_bucket_element_valid_p (dbf, elem_loc)) | ||
57 | { | ||
58 | GDBM_SET_ERRNO (dbf, GDBM_BAD_HASH_TABLE, TRUE); | ||
59 | return NULL; | ||
60 | } | ||
61 | |||
42 | /* Set sizes and pointers. */ | 62 | /* Set sizes and pointers. */ |
43 | key_size = dbf->bucket->h_table[elem_loc].key_size; | 63 | key_size = dbf->bucket->h_table[elem_loc].key_size; |
44 | data_size = dbf->bucket->h_table[elem_loc].data_size; | 64 | data_size = dbf->bucket->h_table[elem_loc].data_size; |
45 | data_ca = &dbf->cache_entry->ca_data; | 65 | data_ca = &dbf->cache_entry->ca_data; |
46 | 66 | ||
47 | /* Set up the cache. */ | 67 | /* Set up the cache. */ |
48 | if (data_ca->dptr != NULL) free (data_ca->dptr); | 68 | if (data_ca->dptr != NULL) free (data_ca->dptr); |
49 | data_ca->key_size = key_size; | 69 | data_ca->key_size = key_size; |
50 | data_ca->data_size = data_size; | 70 | data_ca->data_size = data_size; |
51 | data_ca->elem_loc = elem_loc; | 71 | data_ca->elem_loc = elem_loc; |
52 | data_ca->hash_val = dbf->bucket->h_table[elem_loc].hash_value; | 72 | data_ca->hash_val = dbf->bucket->h_table[elem_loc].hash_value; |
53 | 73 | ||
diff --git a/src/gdbm.h.in b/src/gdbm.h.in index 61d5707..e576c69 100644 --- a/src/gdbm.h.in +++ b/src/gdbm.h.in | |||
@@ -143,24 +143,25 @@ typedef struct gdbm_recovery_s | |||
143 | void *data; | 143 | void *data; |
144 | 144 | ||
145 | size_t max_failed_keys; | 145 | size_t max_failed_keys; |
146 | size_t max_failed_buckets; | 146 | size_t max_failed_buckets; |
147 | size_t max_failures; | 147 | size_t max_failures; |
148 | 148 | ||
149 | /* Output members. | 149 | /* Output members. |
150 | The gdbm_recover function fills these before returning. */ | 150 | The gdbm_recover function fills these before returning. */ |
151 | size_t recovered_keys; | 151 | size_t recovered_keys; |
152 | size_t recovered_buckets; | 152 | size_t recovered_buckets; |
153 | size_t failed_keys; | 153 | size_t failed_keys; |
154 | size_t failed_buckets; | 154 | size_t failed_buckets; |
155 | size_t duplicate_keys; | ||
155 | char *backup_name; | 156 | char *backup_name; |
156 | } gdbm_recovery; | 157 | } gdbm_recovery; |
157 | 158 | ||
158 | #define GDBM_RCVR_DEFAULT 0x00 /* Default settings */ | 159 | #define GDBM_RCVR_DEFAULT 0x00 /* Default settings */ |
159 | #define GDBM_RCVR_ERRFUN 0x01 /* errfun is initialized */ | 160 | #define GDBM_RCVR_ERRFUN 0x01 /* errfun is initialized */ |
160 | #define GDBM_RCVR_MAX_FAILED_KEYS 0x02 /* max_failed_keys is initialized */ | 161 | #define GDBM_RCVR_MAX_FAILED_KEYS 0x02 /* max_failed_keys is initialized */ |
161 | #define GDBM_RCVR_MAX_FAILED_BUCKETS 0x04 /* max_failed_buckets is initialized */ | 162 | #define GDBM_RCVR_MAX_FAILED_BUCKETS 0x04 /* max_failed_buckets is initialized */ |
162 | #define GDBM_RCVR_MAX_FAILURES 0x08 /* max_failures is initialized */ | 163 | #define GDBM_RCVR_MAX_FAILURES 0x08 /* max_failures is initialized */ |
163 | #define GDBM_RCVR_BACKUP 0x10 /* Keep backup copy of the | 164 | #define GDBM_RCVR_BACKUP 0x10 /* Keep backup copy of the |
164 | original database on success */ | 165 | original database on success */ |
165 | #define GDBM_RCVR_FORCE 0x20 /* Force recovery by skipping the | 166 | #define GDBM_RCVR_FORCE 0x20 /* Force recovery by skipping the |
166 | check pass */ | 167 | check pass */ |
@@ -213,27 +214,28 @@ extern int gdbm_copy_meta (GDBM_FILE dst, GDBM_FILE src); | |||
213 | # define GDBM_BAD_OPEN_FLAGS 23 | 214 | # define GDBM_BAD_OPEN_FLAGS 23 |
214 | # define GDBM_FILE_STAT_ERROR 24 | 215 | # define GDBM_FILE_STAT_ERROR 24 |
215 | # define GDBM_FILE_EOF 25 | 216 | # define GDBM_FILE_EOF 25 |
216 | # define GDBM_NO_DBNAME 26 | 217 | # define GDBM_NO_DBNAME 26 |
217 | # define GDBM_ERR_FILE_OWNER 27 | 218 | # define GDBM_ERR_FILE_OWNER 27 |
218 | # define GDBM_ERR_FILE_MODE 28 | 219 | # define GDBM_ERR_FILE_MODE 28 |
219 | # define GDBM_NEED_RECOVERY 29 | 220 | # define GDBM_NEED_RECOVERY 29 |
220 | # define GDBM_BACKUP_FAILED 30 | 221 | # define GDBM_BACKUP_FAILED 30 |
221 | # define GDBM_DIR_OVERFLOW 31 | 222 | # define GDBM_DIR_OVERFLOW 31 |
222 | # define GDBM_BAD_BUCKET 32 | 223 | # define GDBM_BAD_BUCKET 32 |
223 | # define GDBM_BAD_HEADER 33 | 224 | # define GDBM_BAD_HEADER 33 |
224 | # define GDBM_BAD_AVAIL 34 | 225 | # define GDBM_BAD_AVAIL 34 |
226 | # define GDBM_BAD_HASH_TABLE 35 | ||
225 | 227 | ||
226 | # define _GDBM_MIN_ERRNO0 | 228 | # define _GDBM_MIN_ERRNO0 |
227 | # define _GDBM_MAX_ERRNOGDBM_BAD_AVAIL | 229 | # define _GDBM_MAX_ERRNOGDBM_BAD_HASH_TABLE |
228 | 230 | ||
229 | /* This one was never used and will be removed in the future */ | 231 | /* This one was never used and will be removed in the future */ |
230 | # define GDBM_UNKNOWN_UPDATE GDBM_UNKNOWN_ERROR | 232 | # define GDBM_UNKNOWN_UPDATE GDBM_UNKNOWN_ERROR |
231 | 233 | ||
232 | typedef int gdbm_error; | 234 | typedef int gdbm_error; |
233 | extern int *gdbm_errno_location (void); | 235 | extern int *gdbm_errno_location (void); |
234 | #define gdbm_errno (*gdbm_errno_location ()) | 236 | #define gdbm_errno (*gdbm_errno_location ()) |
235 | extern const char * const gdbm_errlist[]; | 237 | extern const char * const gdbm_errlist[]; |
236 | extern int const gdbm_syserr[]; | 238 | extern int const gdbm_syserr[]; |
237 | 239 | ||
238 | extern gdbm_error gdbm_last_errno (GDBM_FILE dbf); | 240 | extern gdbm_error gdbm_last_errno (GDBM_FILE dbf); |
239 | extern int gdbm_last_syserr (GDBM_FILE dbf); | 241 | extern int gdbm_last_syserr (GDBM_FILE dbf); |
diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h index 5305b0d..1bb519b 100644 --- a/src/gdbmdefs.h +++ b/src/gdbmdefs.h | |||
@@ -17,24 +17,40 @@ | |||
17 | You should have received a copy of the GNU General Public License | 17 | You should have received a copy of the GNU General Public License |
18 | along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ | 18 | along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ |
19 | 19 | ||
20 | #include "systems.h" | 20 | #include "systems.h" |
21 | #include "gdbmconst.h" | 21 | #include "gdbmconst.h" |
22 | #include "gdbm.h" | 22 | #include "gdbm.h" |
23 | #define DEFAULT_TEXT_DOMAIN PACKAGE | 23 | #define DEFAULT_TEXT_DOMAIN PACKAGE |
24 | #include "gettext.h" | 24 | #include "gettext.h" |
25 | 25 | ||
26 | #define _(s) gettext (s) | 26 | #define _(s) gettext (s) |
27 | #define N_(s) s | 27 | #define N_(s) s |
28 | 28 | ||
29 | /* The width in bits of the integer type or expression T. */ | ||
30 | #define TYPE_WIDTH(t) (sizeof (t) * CHAR_BIT) | ||
31 | |||
32 | #define SIGNED_TYPE_MAXIMUM(t) \ | ||
33 | ((t) ((((t) 1 << (TYPE_WIDTH (t) - 2)) - 1) * 2 + 1)) | ||
34 | |||
35 | /* Maximum value for off_t */ | ||
36 | #define OFF_T_MAX SIGNED_TYPE_MAXIMUM (off_t) | ||
37 | |||
38 | /* Return true if A can be added to B without integer overflow */ | ||
39 | static inline off_t | ||
40 | off_t_sum_ok (off_t a, off_t b) | ||
41 | { | ||
42 | return OFF_T_MAX - a >= b; | ||
43 | } | ||
44 | |||
29 | /* The type definitions are next. */ | 45 | /* The type definitions are next. */ |
30 | 46 | ||
31 | /* The available file space is stored in an "avail" table. The one with | 47 | /* The available file space is stored in an "avail" table. The one with |
32 | most activity is contained in the file header. (See below.) When that | 48 | most activity is contained in the file header. (See below.) When that |
33 | one filles up, it is split in half and half is pushed on an "avail | 49 | one filles up, it is split in half and half is pushed on an "avail |
34 | stack." When the active avail table is empty and the "avail stack" is | 50 | stack." When the active avail table is empty and the "avail stack" is |
35 | not empty, the top of the stack is popped into the active avail table. */ | 51 | not empty, the top of the stack is popped into the active avail table. */ |
36 | 52 | ||
37 | /* The following structure is the element of the avaliable table. */ | 53 | /* The following structure is the element of the avaliable table. */ |
38 | typedef struct | 54 | typedef struct |
39 | { | 55 | { |
40 | int av_size; /* The size of the available block. */ | 56 | int av_size; /* The size of the available block. */ |
@@ -84,24 +100,25 @@ typedef struct | |||
84 | key. */ | 100 | key. */ |
85 | 101 | ||
86 | typedef struct | 102 | typedef struct |
87 | { | 103 | { |
88 | int hash_value; /* The complete 31 bit value. */ | 104 | int hash_value; /* The complete 31 bit value. */ |
89 | char key_start[SMALL]; /* Up to the first SMALL bytes of the key. */ | 105 | char key_start[SMALL]; /* Up to the first SMALL bytes of the key. */ |
90 | off_t data_pointer; /* The file address of the key record. The | 106 | off_t data_pointer; /* The file address of the key record. The |
91 | data record directly follows the key. */ | 107 | data record directly follows the key. */ |
92 | int key_size; /* Size of key data in the file. */ | 108 | int key_size; /* Size of key data in the file. */ |
93 | int data_size; /* Size of associated data in the file. */ | 109 | int data_size; /* Size of associated data in the file. */ |
94 | } bucket_element; | 110 | } bucket_element; |
95 | 111 | ||
112 | extern int gdbm_bucket_element_valid_p (GDBM_FILE dbf, int elem_loc); | ||
96 | 113 | ||
97 | /* A bucket is a small hash table. This one consists of a number of | 114 | /* A bucket is a small hash table. This one consists of a number of |
98 | bucket elements plus some bookkeeping fields. The number of elements | 115 | bucket elements plus some bookkeeping fields. The number of elements |
99 | depends on the optimum blocksize for the storage device and on a | 116 | depends on the optimum blocksize for the storage device and on a |
100 | parameter given at file creation time. This bucket takes one block. | 117 | parameter given at file creation time. This bucket takes one block. |
101 | When one of these tables gets full, it is split into two hash buckets. | 118 | When one of these tables gets full, it is split into two hash buckets. |
102 | The contents are split between them by the use of the first few bits | 119 | The contents are split between them by the use of the first few bits |
103 | of the 31 bit hash function. The location in a bucket is the hash | 120 | of the 31 bit hash function. The location in a bucket is the hash |
104 | value modulo the size of the bucket. The in-memory images of the | 121 | value modulo the size of the bucket. The in-memory images of the |
105 | buckets are allocated by malloc using a calculated size depending of | 122 | buckets are allocated by malloc using a calculated size depending of |
106 | the file system buffer size. To speed up write, each bucket will have | 123 | the file system buffer size. To speed up write, each bucket will have |
107 | BUCKET_AVAIL avail elements with the bucket. */ | 124 | BUCKET_AVAIL avail elements with the bucket. */ |
diff --git a/src/gdbmerrno.c b/src/gdbmerrno.c index 896bf70..52cfe30 100644 --- a/src/gdbmerrno.c +++ b/src/gdbmerrno.c | |||
@@ -125,25 +125,26 @@ const char * const gdbm_errlist[_GDBM_MAX_ERRNO+1] = { | |||
125 | [GDBM_BAD_FILE_OFFSET] = N_("File header assumes wrong off_t size"), | 125 | [GDBM_BAD_FILE_OFFSET] = N_("File header assumes wrong off_t size"), |
126 | [GDBM_BAD_OPEN_FLAGS] = N_("Bad file flags"), | 126 | [GDBM_BAD_OPEN_FLAGS] = N_("Bad file flags"), |
127 | [GDBM_FILE_STAT_ERROR] = N_("Cannot stat file"), | 127 | [GDBM_FILE_STAT_ERROR] = N_("Cannot stat file"), |
128 | [GDBM_FILE_EOF] = N_("Unexpected end of file"), | 128 | [GDBM_FILE_EOF] = N_("Unexpected end of file"), |
129 | [GDBM_NO_DBNAME] = N_("Database name not given"), | 129 | [GDBM_NO_DBNAME] = N_("Database name not given"), |
130 | [GDBM_ERR_FILE_OWNER] = N_("Failed to restore file owner"), | 130 | [GDBM_ERR_FILE_OWNER] = N_("Failed to restore file owner"), |
131 | [GDBM_ERR_FILE_MODE] = N_("Failed to restore file mode"), | 131 | [GDBM_ERR_FILE_MODE] = N_("Failed to restore file mode"), |
132 | [GDBM_NEED_RECOVERY] = N_("Database needs recovery"), | 132 | [GDBM_NEED_RECOVERY] = N_("Database needs recovery"), |
133 | [GDBM_BACKUP_FAILED] = N_("Failed to create backup copy"), | 133 | [GDBM_BACKUP_FAILED] = N_("Failed to create backup copy"), |
134 | [GDBM_DIR_OVERFLOW] = N_("Bucket directory overflow"), | 134 | [GDBM_DIR_OVERFLOW] = N_("Bucket directory overflow"), |
135 | [GDBM_BAD_BUCKET] = N_("Malformed bucket header"), | 135 | [GDBM_BAD_BUCKET] = N_("Malformed bucket header"), |
136 | [GDBM_BAD_HEADER] = N_("Malformed database file header"), | 136 | [GDBM_BAD_HEADER] = N_("Malformed database file header"), |
137 | [GDBM_BAD_AVAIL] = N_("Malforemd avail_block") | 137 | [GDBM_BAD_AVAIL] = N_("Malformed avail_block"), |
138 | [GDBM_BAD_HASH_TABLE] = N_("Malformed hash table") | ||
138 | }; | 139 | }; |
139 | 140 | ||
140 | const char * | 141 | const char * |
141 | gdbm_strerror (gdbm_error error) | 142 | gdbm_strerror (gdbm_error error) |
142 | { | 143 | { |
143 | if (error < _GDBM_MIN_ERRNO || error > _GDBM_MAX_ERRNO) | 144 | if (error < _GDBM_MIN_ERRNO || error > _GDBM_MAX_ERRNO) |
144 | error = GDBM_UNKNOWN_ERROR; | 145 | error = GDBM_UNKNOWN_ERROR; |
145 | return gettext (gdbm_errlist[error]); | 146 | return gettext (gdbm_errlist[error]); |
146 | } | 147 | } |
147 | 148 | ||
148 | char const * | 149 | char const * |
149 | gdbm_db_strerror (GDBM_FILE dbf) | 150 | gdbm_db_strerror (GDBM_FILE dbf) |
diff --git a/src/gdbmtool.c b/src/gdbmtool.c index 33bdf93..9c6eebe 100644 --- a/src/gdbmtool.c +++ b/src/gdbmtool.c | |||
@@ -543,42 +543,47 @@ reorganize_handler (struct handler_param *param GDBM_ARG_UNUSED) | |||
543 | 543 | ||
544 | static void | 544 | static void |
545 | err_printer (void *data GDBM_ARG_UNUSED, char const *fmt, ...) | 545 | err_printer (void *data GDBM_ARG_UNUSED, char const *fmt, ...) |
546 | { | 546 | { |
547 | va_list ap; | 547 | va_list ap; |
548 | 548 | ||
549 | va_start (ap, fmt); | 549 | va_start (ap, fmt); |
550 | vfprintf (stderr, fmt, ap); | 550 | vfprintf (stderr, fmt, ap); |
551 | va_end (ap); | 551 | va_end (ap); |
552 | fprintf (stderr, "\n"); | 552 | fprintf (stderr, "\n"); |
553 | } | 553 | } |
554 | 554 | ||
555 | /* recover verbose backup max-failed-keys=N max-failed-buckets=N max-failures=N */ | 555 | /* recover sumamry verbose backup max-failed-keys=N max-failed-buckets=N max-failures=N */ |
556 | void | 556 | void |
557 | recover_handler (struct handler_param *param) | 557 | recover_handler (struct handler_param *param) |
558 | { | 558 | { |
559 | gdbm_recovery rcvr; | 559 | gdbm_recovery rcvr; |
560 | int flags = 0; | 560 | int flags = 0; |
561 | int rc; | 561 | int rc; |
562 | int i; | 562 | int i; |
563 | char *p; | 563 | char *p; |
564 | int summary = 0; | ||
564 | 565 | ||
565 | for (i = 1; i < param->argc; i++) | 566 | for (i = 0; i < param->argc; i++) |
566 | { | 567 | { |
567 | char *arg = PARAM_STRING (param, i); | 568 | char *arg = PARAM_STRING (param, i); |
568 | if (strcmp (arg, "verbose") == 0) | 569 | if (strcmp (arg, "verbose") == 0) |
569 | { | 570 | { |
570 | rcvr.errfun = err_printer; | 571 | rcvr.errfun = err_printer; |
571 | flags |= GDBM_RCVR_ERRFUN; | 572 | flags |= GDBM_RCVR_ERRFUN; |
572 | } | 573 | } |
574 | else if (strcmp (arg, "summary") == 0) | ||
575 | { | ||
576 | summary = 1; | ||
577 | } | ||
573 | else if (strcmp (arg, "backup") == 0) | 578 | else if (strcmp (arg, "backup") == 0) |
574 | { | 579 | { |
575 | rcvr.errfun = err_printer; | 580 | rcvr.errfun = err_printer; |
576 | flags |= GDBM_RCVR_BACKUP; | 581 | flags |= GDBM_RCVR_BACKUP; |
577 | } | 582 | } |
578 | else if (strncmp (arg, "max-failures=", 13) == 0) | 583 | else if (strncmp (arg, "max-failures=", 13) == 0) |
579 | { | 584 | { |
580 | rcvr.max_failures = strtoul (arg + 13, &p, 10); | 585 | rcvr.max_failures = strtoul (arg + 13, &p, 10); |
581 | if (*p) | 586 | if (*p) |
582 | { | 587 | { |
583 | printf (_("not a number (stopped near %s)\n"), p); | 588 | printf (_("not a number (stopped near %s)\n"), p); |
584 | return; | 589 | return; |
@@ -608,24 +613,37 @@ recover_handler (struct handler_param *param) | |||
608 | else | 613 | else |
609 | { | 614 | { |
610 | terror (_("unrecognized argument: %s"), arg); | 615 | terror (_("unrecognized argument: %s"), arg); |
611 | return; | 616 | return; |
612 | } | 617 | } |
613 | } | 618 | } |
614 | 619 | ||
615 | rc = gdbm_recover (gdbm_file, &rcvr, flags); | 620 | rc = gdbm_recover (gdbm_file, &rcvr, flags); |
616 | 621 | ||
617 | if (rc == 0) | 622 | if (rc == 0) |
618 | { | 623 | { |
619 | fprintf (param->fp, _("Recovery succeeded.\n")); | 624 | fprintf (param->fp, _("Recovery succeeded.\n")); |
625 | if (summary) | ||
626 | { | ||
627 | fprintf (param->fp, | ||
628 | _("Keys recovered: %lu, failed: %lu, duplicate: %lu\n"), | ||
629 | (unsigned long) rcvr.recovered_keys, | ||
630 | (unsigned long) rcvr.failed_keys, | ||
631 | (unsigned long) rcvr.duplicate_keys); | ||
632 | fprintf (param->fp, | ||
633 | _("Buckets recovered: %lu, failed: %lu\n"), | ||
634 | (unsigned long) rcvr.recovered_buckets, | ||
635 | (unsigned long) rcvr.failed_buckets); | ||
636 | } | ||
637 | |||
620 | if (rcvr.backup_name) | 638 | if (rcvr.backup_name) |
621 | { | 639 | { |
622 | fprintf (param->fp, | 640 | fprintf (param->fp, |
623 | _("Original database preserved in file %s"), | 641 | _("Original database preserved in file %s"), |
624 | rcvr.backup_name); | 642 | rcvr.backup_name); |
625 | free (rcvr.backup_name); | 643 | free (rcvr.backup_name); |
626 | } | 644 | } |
627 | fputc ('\n', param->fp); | 645 | fputc ('\n', param->fp); |
628 | } | 646 | } |
629 | else | 647 | else |
630 | { | 648 | { |
631 | fprintf (stderr, _("Recovery failed: %s"), gdbm_strerror (gdbm_errno)); | 649 | fprintf (stderr, _("Recovery failed: %s"), gdbm_strerror (gdbm_errno)); |
@@ -916,25 +934,25 @@ quit_handler (struct handler_param *param GDBM_ARG_UNUSED) | |||
916 | exit (EXIT_OK); | 934 | exit (EXIT_OK); |
917 | } | 935 | } |
918 | 936 | ||
919 | /* export FILE [truncate] - export to a flat file format */ | 937 | /* export FILE [truncate] - export to a flat file format */ |
920 | void | 938 | void |
921 | export_handler (struct handler_param *param) | 939 | export_handler (struct handler_param *param) |
922 | { | 940 | { |
923 | int format = GDBM_DUMP_FMT_ASCII; | 941 | int format = GDBM_DUMP_FMT_ASCII; |
924 | int flags = GDBM_WRCREAT; | 942 | int flags = GDBM_WRCREAT; |
925 | int i; | 943 | int i; |
926 | int filemode; | 944 | int filemode; |
927 | 945 | ||
928 | for (i = 1; i < param->argc; i++) | 946 | for (i = 0; i < param->argc; i++) |
929 | { | 947 | { |
930 | if (strcmp (PARAM_STRING (param, i), "truncate") == 0) | 948 | if (strcmp (PARAM_STRING (param, i), "truncate") == 0) |
931 | flags = GDBM_NEWDB; | 949 | flags = GDBM_NEWDB; |
932 | else if (strcmp (PARAM_STRING (param, i), "binary") == 0) | 950 | else if (strcmp (PARAM_STRING (param, i), "binary") == 0) |
933 | format = GDBM_DUMP_FMT_BINARY; | 951 | format = GDBM_DUMP_FMT_BINARY; |
934 | else if (strcmp (PARAM_STRING (param, i), "ascii") == 0) | 952 | else if (strcmp (PARAM_STRING (param, i), "ascii") == 0) |
935 | format = GDBM_DUMP_FMT_ASCII; | 953 | format = GDBM_DUMP_FMT_ASCII; |
936 | else | 954 | else |
937 | { | 955 | { |
938 | terror (_("unrecognized argument: %s"), PARAM_STRING (param, i)); | 956 | terror (_("unrecognized argument: %s"), PARAM_STRING (param, i)); |
939 | return; | 957 | return; |
940 | } | 958 | } |
@@ -950,25 +968,25 @@ export_handler (struct handler_param *param) | |||
950 | } | 968 | } |
951 | 969 | ||
952 | /* import FILE [replace] [nometa] - import from a flat file */ | 970 | /* import FILE [replace] [nometa] - import from a flat file */ |
953 | void | 971 | void |
954 | import_handler (struct handler_param *param) | 972 | import_handler (struct handler_param *param) |
955 | { | 973 | { |
956 | int flag = GDBM_INSERT; | 974 | int flag = GDBM_INSERT; |
957 | unsigned long err_line; | 975 | unsigned long err_line; |
958 | int meta_mask = 0; | 976 | int meta_mask = 0; |
959 | int i; | 977 | int i; |
960 | int rc; | 978 | int rc; |
961 | 979 | ||
962 | for (i = 1; i < param->argc; i++) | 980 | for (i = 0; i < param->argc; i++) |
963 | { | 981 | { |
964 | if (strcmp (PARAM_STRING (param, i), "replace") == 0) | 982 | if (strcmp (PARAM_STRING (param, i), "replace") == 0) |
965 | flag = GDBM_REPLACE; | 983 | flag = GDBM_REPLACE; |
966 | else if (strcmp (PARAM_STRING (param, i), "nometa") == 0) | 984 | else if (strcmp (PARAM_STRING (param, i), "nometa") == 0) |
967 | meta_mask = GDBM_META_MASK_MODE | GDBM_META_MASK_OWNER; | 985 | meta_mask = GDBM_META_MASK_MODE | GDBM_META_MASK_OWNER; |
968 | else | 986 | else |
969 | { | 987 | { |
970 | terror (_("unrecognized argument: %s"), | 988 | terror (_("unrecognized argument: %s"), |
971 | PARAM_STRING (param, i)); | 989 | PARAM_STRING (param, i)); |
972 | return; | 990 | return; |
973 | } | 991 | } |
974 | } | 992 | } |
@@ -1211,24 +1229,25 @@ struct command command_tab[] = { | |||
1211 | FALSE, | 1229 | FALSE, |
1212 | REPEAT_NEVER, | 1230 | REPEAT_NEVER, |
1213 | N_("firstkey") }, | 1231 | N_("firstkey") }, |
1214 | { S(reorganize), T_CMD, | 1232 | { S(reorganize), T_CMD, |
1215 | checkdb, reorganize_handler, NULL, | 1233 | checkdb, reorganize_handler, NULL, |
1216 | { { NULL } }, | 1234 | { { NULL } }, |
1217 | FALSE, | 1235 | FALSE, |
1218 | REPEAT_NEVER, | 1236 | REPEAT_NEVER, |
1219 | N_("reorganize") }, | 1237 | N_("reorganize") }, |
1220 | { S(recover), T_CMD, | 1238 | { S(recover), T_CMD, |
1221 | checkdb, recover_handler, NULL, | 1239 | checkdb, recover_handler, NULL, |
1222 | { { "[verbose]", GDBM_ARG_STRING }, | 1240 | { { "[verbose]", GDBM_ARG_STRING }, |
1241 | { "[summary]", GDBM_ARG_STRING }, | ||
1223 | { "[backup]", GDBM_ARG_STRING }, | 1242 | { "[backup]", GDBM_ARG_STRING }, |
1224 | { "[max-failed-keys=N]", GDBM_ARG_STRING }, | 1243 | { "[max-failed-keys=N]", GDBM_ARG_STRING }, |
1225 | { "[max-failed-buckets=N]", GDBM_ARG_STRING }, | 1244 | { "[max-failed-buckets=N]", GDBM_ARG_STRING }, |
1226 | { "[max-failures=N]", GDBM_ARG_STRING }, | 1245 | { "[max-failures=N]", GDBM_ARG_STRING }, |
1227 | { NULL } }, | 1246 | { NULL } }, |
1228 | FALSE, | 1247 | FALSE, |
1229 | REPEAT_NEVER, | 1248 | REPEAT_NEVER, |
1230 | N_("recover the database") }, | 1249 | N_("recover the database") }, |
1231 | { S(avail), T_CMD, | 1250 | { S(avail), T_CMD, |
1232 | avail_begin, avail_handler, NULL, | 1251 | avail_begin, avail_handler, NULL, |
1233 | { { NULL } }, | 1252 | { { NULL } }, |
1234 | FALSE, | 1253 | FALSE, |
@@ -35,26 +35,33 @@ | |||
35 | 35 | ||
36 | /* Translate current offset in the mapped region into the absolute position */ | 36 | /* Translate current offset in the mapped region into the absolute position */ |
37 | # define _GDBM_MMAPPED_POS(dbf) ((dbf)->mapped_off + (dbf)->mapped_pos) | 37 | # define _GDBM_MMAPPED_POS(dbf) ((dbf)->mapped_off + (dbf)->mapped_pos) |
38 | /* Return true if the absolute offset OFF lies within the currentlty mmapped | 38 | /* Return true if the absolute offset OFF lies within the currentlty mmapped |
39 | region */ | 39 | region */ |
40 | # define _GDBM_IN_MAPPED_REGION_P(dbf, off) \ | 40 | # define _GDBM_IN_MAPPED_REGION_P(dbf, off) \ |
41 | ((off) >= (dbf)->mapped_off \ | 41 | ((off) >= (dbf)->mapped_off \ |
42 | && ((off) - (dbf)->mapped_off) < (dbf)->mapped_size) | 42 | && ((off) - (dbf)->mapped_off) < (dbf)->mapped_size) |
43 | /* Return true if the current region needs to be remapped */ | 43 | /* Return true if the current region needs to be remapped */ |
44 | # define _GDBM_NEED_REMAP(dbf) \ | 44 | # define _GDBM_NEED_REMAP(dbf) \ |
45 | (!(dbf)->mapped_region || (dbf)->mapped_pos == (dbf)->mapped_size) | 45 | (!(dbf)->mapped_region || (dbf)->mapped_pos == (dbf)->mapped_size) |
46 | /* Return the sum of the currently mapped size and DELTA */ | 46 | /* Return the sum of the currently mapped size and DELTA */ |
47 | # define SUM_FILE_SIZE(dbf, delta) \ | 47 | static inline off_t |
48 | ((dbf)->mapped_off + (dbf)->mapped_size + (delta)) | 48 | SUM_FILE_SIZE (GDBM_FILE dbf, off_t delta) |
49 | { | ||
50 | if (delta >= 0 | ||
51 | && off_t_sum_ok (dbf->mapped_off, dbf->mapped_size) | ||
52 | && off_t_sum_ok (dbf->mapped_off + dbf->mapped_size, delta)) | ||
53 | return dbf->mapped_off + dbf->mapped_size + delta; | ||
54 | return -1; | ||
55 | } | ||
49 | 56 | ||
50 | /* Store the size of the GDBM file DBF in *PSIZE. | 57 | /* Store the size of the GDBM file DBF in *PSIZE. |
51 | Return 0 on success and -1 on failure. */ | 58 | Return 0 on success and -1 on failure. */ |
52 | int | 59 | int |
53 | _gdbm_file_size (GDBM_FILE dbf, off_t *psize) | 60 | _gdbm_file_size (GDBM_FILE dbf, off_t *psize) |
54 | { | 61 | { |
55 | struct stat sb; | 62 | struct stat sb; |
56 | if (fstat (dbf->desc, &sb)) | 63 | if (fstat (dbf->desc, &sb)) |
57 | { | 64 | { |
58 | GDBM_SET_ERRNO (dbf, GDBM_FILE_STAT_ERROR, FALSE); | 65 | GDBM_SET_ERRNO (dbf, GDBM_FILE_STAT_ERROR, FALSE); |
59 | return -1; | 66 | return -1; |
60 | } | 67 | } |
@@ -173,24 +180,35 @@ _gdbm_file_extend (GDBM_FILE dbf, off_t size) | |||
173 | truncated to the actual file size. | 180 | truncated to the actual file size. |
174 | 181 | ||
175 | The upper bound obtained that way is used as a *hint* to select | 182 | The upper bound obtained that way is used as a *hint* to select |
176 | the actual size of the mapped region. which can never exceed | 183 | the actual size of the mapped region. which can never exceed |
177 | dbf->mapped_size_max. | 184 | dbf->mapped_size_max. |
178 | 185 | ||
179 | The function returns 0 on success, -1 on failure. */ | 186 | The function returns 0 on success, -1 on failure. */ |
180 | int | 187 | int |
181 | _gdbm_mapped_remap (GDBM_FILE dbf, off_t size, int flag) | 188 | _gdbm_mapped_remap (GDBM_FILE dbf, off_t size, int flag) |
182 | { | 189 | { |
183 | off_t file_size, pos; | 190 | off_t file_size, pos; |
184 | 191 | ||
192 | if (size < 0) | ||
193 | { | ||
194 | errno = EINVAL; | ||
195 | GDBM_SET_ERRNO (dbf, GDBM_FILE_SEEK_ERROR, TRUE); | ||
196 | return -1; | ||
197 | } | ||
198 | |||
199 | if (size < dbf->mapped_size) | ||
200 | /* Nothing to do */ | ||
201 | return 0; | ||
202 | |||
185 | if (_gdbm_file_size (dbf, &file_size)) | 203 | if (_gdbm_file_size (dbf, &file_size)) |
186 | { | 204 | { |
187 | SAVE_ERRNO (_gdbm_mapped_unmap (dbf)); | 205 | SAVE_ERRNO (_gdbm_mapped_unmap (dbf)); |
188 | return -1; | 206 | return -1; |
189 | } | 207 | } |
190 | 208 | ||
191 | if (flag == _REMAP_END && size < file_size) | 209 | if (flag == _REMAP_END && size < file_size) |
192 | size = file_size; | 210 | size = file_size; |
193 | 211 | ||
194 | if (dbf->read_write) | 212 | if (dbf->read_write) |
195 | { | 213 | { |
196 | if (size > file_size) | 214 | if (size > file_size) |
diff --git a/src/recover.c b/src/recover.c index d6d4ff9..721c23f 100644 --- a/src/recover.c +++ b/src/recover.c | |||
@@ -303,37 +303,52 @@ run_recovery (GDBM_FILE dbf, GDBM_FILE new_dbf, gdbm_recovery *rcvr, int flags) | |||
303 | return -1; | 303 | return -1; |
304 | continue; | 304 | continue; |
305 | } | 305 | } |
306 | 306 | ||
307 | key.dptr = dptr; | 307 | key.dptr = dptr; |
308 | key.dsize = dbf->bucket->h_table[i].key_size; | 308 | key.dsize = dbf->bucket->h_table[i].key_size; |
309 | 309 | ||
310 | data.dptr = dptr + key.dsize; | 310 | data.dptr = dptr + key.dsize; |
311 | data.dsize = dbf->bucket->h_table[i].data_size; | 311 | data.dsize = dbf->bucket->h_table[i].data_size; |
312 | 312 | ||
313 | if (gdbm_store (new_dbf, key, data, GDBM_INSERT) != 0) | 313 | if (gdbm_store (new_dbf, key, data, GDBM_INSERT) != 0) |
314 | { | 314 | { |
315 | switch (gdbm_last_errno (new_dbf)) | ||
316 | { | ||
317 | case GDBM_CANNOT_REPLACE: | ||
318 | rcvr->duplicate_keys++; | ||
319 | if (flags & GDBM_RCVR_ERRFUN) | ||
320 | rcvr->errfun (rcvr->data, | ||
321 | _("ignoring duplicate key %d:%d (%lu:%d)"), | ||
322 | bucket_dir, i, | ||
323 | (unsigned long) dbf->bucket->h_table[i].data_pointer, | ||
324 | dbf->bucket->h_table[i].key_size | ||
325 | + dbf->bucket->h_table[i].data_size); | ||
326 | break; | ||
327 | |||
328 | default: | ||
315 | if (flags & GDBM_RCVR_ERRFUN) | 329 | if (flags & GDBM_RCVR_ERRFUN) |
316 | rcvr->errfun (rcvr->data, | 330 | rcvr->errfun (rcvr->data, |
317 | _("fatal: can't store element %d:%d (%lu:%d): %s"), | 331 | _("fatal: can't store element %d:%d (%lu:%d): %s"), |
318 | bucket_dir, i, | 332 | bucket_dir, i, |
319 | (unsigned long) dbf->bucket->h_table[i].data_pointer, | 333 | (unsigned long) dbf->bucket->h_table[i].data_pointer, |
320 | dbf->bucket->h_table[i].key_size | 334 | dbf->bucket->h_table[i].key_size |
321 | + dbf->bucket->h_table[i].data_size, | 335 | + dbf->bucket->h_table[i].data_size, |
322 | gdbm_db_strerror (new_dbf)); | 336 | gdbm_db_strerror (new_dbf)); |
323 | return -1; | 337 | return -1; |
324 | } | 338 | } |
325 | } | 339 | } |
326 | } | 340 | } |
327 | } | 341 | } |
342 | } | ||
328 | 343 | ||
329 | return 0; | 344 | return 0; |
330 | } | 345 | } |
331 | 346 | ||
332 | int | 347 | int |
333 | gdbm_recover (GDBM_FILE dbf, gdbm_recovery *rcvr, int flags) | 348 | gdbm_recover (GDBM_FILE dbf, gdbm_recovery *rcvr, int flags) |
334 | { | 349 | { |
335 | GDBM_FILE new_dbf; /* The new file. */ | 350 | GDBM_FILE new_dbf; /* The new file. */ |
336 | char *new_name; /* A temporary name. */ | 351 | char *new_name; /* A temporary name. */ |
337 | size_t len; | 352 | size_t len; |
338 | int fd; | 353 | int fd; |
339 | int rc; | 354 | int rc; |
@@ -347,24 +362,25 @@ gdbm_recover (GDBM_FILE dbf, gdbm_recovery *rcvr, int flags) | |||
347 | } | 362 | } |
348 | 363 | ||
349 | /* Initialize gdbm_recovery structure */ | 364 | /* Initialize gdbm_recovery structure */ |
350 | if (!rcvr) | 365 | if (!rcvr) |
351 | { | 366 | { |
352 | rcvr = &rs; | 367 | rcvr = &rs; |
353 | flags = 0; | 368 | flags = 0; |
354 | } | 369 | } |
355 | rcvr->recovered_keys = 0; | 370 | rcvr->recovered_keys = 0; |
356 | rcvr->recovered_buckets = 0; | 371 | rcvr->recovered_buckets = 0; |
357 | rcvr->failed_keys = 0; | 372 | rcvr->failed_keys = 0; |
358 | rcvr->failed_buckets = 0; | 373 | rcvr->failed_buckets = 0; |
374 | rcvr->duplicate_keys = 0; | ||
359 | rcvr->backup_name = NULL; | 375 | rcvr->backup_name = NULL; |
360 | 376 | ||
361 | rc = 0; | 377 | rc = 0; |
362 | if ((flags & GDBM_RCVR_FORCE) || check_db (dbf)) | 378 | if ((flags & GDBM_RCVR_FORCE) || check_db (dbf)) |
363 | { | 379 | { |
364 | len = strlen (dbf->name); | 380 | len = strlen (dbf->name); |
365 | new_name = malloc (len + sizeof (TMPSUF)); | 381 | new_name = malloc (len + sizeof (TMPSUF)); |
366 | if (!new_name) | 382 | if (!new_name) |
367 | { | 383 | { |
368 | GDBM_SET_ERRNO (NULL, GDBM_MALLOC_ERROR, FALSE); | 384 | GDBM_SET_ERRNO (NULL, GDBM_MALLOC_ERROR, FALSE); |
369 | return -1; | 385 | return -1; |
370 | } | 386 | } |
diff --git a/src/systems.h b/src/systems.h index 66955dd..c678573 100644 --- a/src/systems.h +++ b/src/systems.h | |||
@@ -24,24 +24,25 @@ | |||
24 | # include <sys/file.h> | 24 | # include <sys/file.h> |
25 | #endif | 25 | #endif |
26 | #include <sys/stat.h> | 26 | #include <sys/stat.h> |
27 | #include <stdlib.h> | 27 | #include <stdlib.h> |
28 | #if HAVE_STRING_H | 28 | #if HAVE_STRING_H |
29 | # include <string.h> | 29 | # include <string.h> |
30 | #else | 30 | #else |
31 | # include <strings.h> | 31 | # include <strings.h> |
32 | #endif | 32 | #endif |
33 | #include <unistd.h> | 33 | #include <unistd.h> |
34 | #include <fcntl.h> | 34 | #include <fcntl.h> |
35 | #include <errno.h> | 35 | #include <errno.h> |
36 | #include <limits.h> | ||
36 | 37 | ||
37 | #ifndef SEEK_SET | 38 | #ifndef SEEK_SET |
38 | # define SEEK_SET 0 | 39 | # define SEEK_SET 0 |
39 | #endif | 40 | #endif |
40 | 41 | ||
41 | #ifndef O_CLOEXEC | 42 | #ifndef O_CLOEXEC |
42 | # define O_CLOEXEC 0 | 43 | # define O_CLOEXEC 0 |
43 | #endif | 44 | #endif |
44 | 45 | ||
45 | /* Default block size. Some systems do not have blocksize in their | 46 | /* Default block size. Some systems do not have blocksize in their |
46 | stat record. This code uses the BSD blocksize from stat. */ | 47 | stat record. This code uses the BSD blocksize from stat. */ |
47 | 48 | ||