From d74b094cdcd25b5ce493640873d910a3b788fafe Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Wed, 5 Oct 2011 13:06:15 +0000 Subject: Fix bug #150. Tolerate I/O operations returning less bytes than expected. Retry I/O if possible. * src/fullio.c: New file. * src/Makefile.am (libgdbm_la_SOURCES): Add fullio.c * src/proto.h (_gdbm_full_read) (_gdbm_full_write): New protos. * src/gdbmerrno.c (gdbm_errlist): Add entry for GDBM_FILE_EOF. * src/bucket.c: Use _gdbm_full_{read|write}. * src/falloc.c: Likewise. * src/findkey.c: Likewise. * src/gdbmopen.c: Likewise. * src/gdbmstore.c: Likewise. * src/testgdbm.c: Likewise. * src/update.c: Likewise. --- ChangeLog | 21 +++++++++++++++++ src/Makefile.am | 1 + src/bucket.c | 19 ++++++++-------- src/falloc.c | 17 +++++++------- src/findkey.c | 7 +++--- src/fullio.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/gdbmerrno.c | 1 + src/gdbmopen.c | 42 +++++++++++++++++----------------- src/gdbmstore.c | 13 ++++++----- src/proto.h | 4 ++++ src/testgdbm.c | 16 +++++++++---- src/update.c | 19 ++++++++-------- 12 files changed, 169 insertions(+), 61 deletions(-) create mode 100644 src/fullio.c diff --git a/ChangeLog b/ChangeLog index b6f2995..a7ef7dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2011-10-05 Sergey Poznyakoff + + Fix bug #150. + + Tolerate I/O operations returning less bytes than expected. Retry I/O + if possible. + + * src/fullio.c: New file. + * src/Makefile.am (libgdbm_la_SOURCES): Add fullio.c + * src/proto.h (_gdbm_full_read) + (_gdbm_full_write): New protos. + * src/gdbmerrno.c (gdbm_errlist): Add entry for GDBM_FILE_EOF. + + * src/bucket.c: Use _gdbm_full_{read|write}. + * src/falloc.c: Likewise. + * src/findkey.c: Likewise. + * src/gdbmopen.c: Likewise. + * src/gdbmstore.c: Likewise. + * src/testgdbm.c: Likewise. + * src/update.c: Likewise. + 2011-10-05 Sergey Poznyakoff * src/gdbmopen.c (gdbm_open): Initialize memory used for file diff --git a/src/Makefile.am b/src/Makefile.am index db85972..15a5840 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,6 +53,7 @@ libgdbm_la_SOURCES = \ falloc.c\ findkey.c\ flatfile.c\ + fullio.c\ hash.c\ lock.c\ mmap.c\ diff --git a/src/bucket.c b/src/bucket.c index cda2389..c211f3c 100644 --- a/src/bucket.c +++ b/src/bucket.c @@ -52,8 +52,8 @@ _gdbm_new_bucket (GDBM_FILE dbf, hash_bucket *bucket, int bits) void _gdbm_get_bucket (GDBM_FILE dbf, int dir_index) { + int rc; off_t bucket_adr; /* The address of the correct hash bucket. */ - int num_bytes; /* The number of bytes read. */ off_t file_pos; /* The return address for lseek. */ int index; /* Loop index. */ @@ -95,10 +95,10 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index) file_pos = __lseek (dbf, bucket_adr, L_SET); if (file_pos != bucket_adr) _gdbm_fatal (dbf, _("lseek error")); - - num_bytes = __read (dbf, dbf->bucket, dbf->header->bucket_size); - if (num_bytes != dbf->header->bucket_size) - _gdbm_fatal (dbf, _("read error")); + + rc = _gdbm_full_read (dbf, dbf->bucket, dbf->header->bucket_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); } return; @@ -303,15 +303,16 @@ _gdbm_split_bucket (GDBM_FILE dbf, int next_insert) void _gdbm_write_bucket (GDBM_FILE dbf, cache_elem *ca_entry) { - int num_bytes; /* The return value for write. */ + int rc; off_t file_pos; /* The return value for lseek. */ file_pos = __lseek (dbf, ca_entry->ca_adr, L_SET); if (file_pos != ca_entry->ca_adr) _gdbm_fatal (dbf, _("lseek error")); - num_bytes = __write (dbf, ca_entry->ca_bucket, dbf->header->bucket_size); - if (num_bytes != dbf->header->bucket_size) - _gdbm_fatal (dbf, _("write error")); + rc = _gdbm_full_write (dbf, ca_entry->ca_bucket, dbf->header->bucket_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + ca_entry->ca_changed = FALSE; ca_entry->ca_data.hash_val = -1; ca_entry->ca_data.elem_loc = -1; diff --git a/src/falloc.c b/src/falloc.c index d0af4a6..6b2eca4 100644 --- a/src/falloc.c +++ b/src/falloc.c @@ -158,12 +158,12 @@ _gdbm_free (GDBM_FILE dbf, off_t file_adr, int num_bytes) static void pop_avail_block (GDBM_FILE dbf) { - int num_bytes; /* For use with the read system call. */ + int rc; off_t file_pos; /* For use with the lseek system call. */ avail_elem new_el; avail_block *new_blk; int index; - + if (dbf->header->avail.count == dbf->header->avail.size) { /* We're kind of stuck here, so we re-split the header in order to @@ -183,8 +183,9 @@ pop_avail_block (GDBM_FILE dbf) /* Read the block. */ file_pos = __lseek (dbf, new_el.av_adr, L_SET); if (file_pos != new_el.av_adr) _gdbm_fatal (dbf, _("lseek error")); - num_bytes = __read (dbf, new_blk, new_el.av_size); - if (num_bytes != new_el.av_size) _gdbm_fatal (dbf, _("read error")); + rc = _gdbm_full_read (dbf, new_blk, new_el.av_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); /* Add the elements from the new block to the header. */ index = 0; @@ -233,14 +234,13 @@ pop_avail_block (GDBM_FILE dbf) static void push_avail_block (GDBM_FILE dbf) { - int num_bytes; int av_size; off_t av_adr; int index; off_t file_pos; avail_block *temp; avail_elem new_loc; - + int rc; /* Caclulate the size of the split block. */ av_size = ( (dbf->header->avail.size * sizeof (avail_elem)) >> 1) @@ -280,8 +280,9 @@ push_avail_block (GDBM_FILE dbf) /* Update the disk. */ file_pos = __lseek (dbf, av_adr, L_SET); if (file_pos != av_adr) _gdbm_fatal (dbf, _("lseek error")); - num_bytes = __write (dbf, temp, av_size); - if (num_bytes != av_size) _gdbm_fatal (dbf, _("write error")); + rc = _gdbm_full_write (dbf, temp, av_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); free (temp); } diff --git a/src/findkey.c b/src/findkey.c index 7933c2f..a375cb4 100644 --- a/src/findkey.c +++ b/src/findkey.c @@ -29,7 +29,7 @@ char * _gdbm_read_entry (GDBM_FILE dbf, int elem_loc) { - int num_bytes; /* For seeking and reading. */ + int rc; int key_size; int data_size; off_t file_pos; @@ -61,8 +61,9 @@ _gdbm_read_entry (GDBM_FILE dbf, int elem_loc) file_pos = __lseek (dbf, dbf->bucket->h_table[elem_loc].data_pointer, L_SET); if (file_pos != dbf->bucket->h_table[elem_loc].data_pointer) _gdbm_fatal (dbf, _("lseek error")); - num_bytes = __read (dbf, data_ca->dptr, key_size+data_size); - if (num_bytes != key_size+data_size) _gdbm_fatal (dbf, _("read error")); + rc = _gdbm_full_read (dbf, data_ca->dptr, key_size+data_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); return data_ca->dptr; } diff --git a/src/fullio.c b/src/fullio.c new file mode 100644 index 0000000..9e401da --- /dev/null +++ b/src/fullio.c @@ -0,0 +1,70 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GDBM. If not, see . */ + +#include "autoconf.h" +#include "gdbmdefs.h" + +/* Read exactly SIZE bytes of data into BUFFER. Return value is 0 on + success, GDBM_FILE_EOF, if not enough data is available, and + GDBM_FILE_READ_ERROR, if a read error occurs. In the latter case + errno keeps actual system error code. */ +int +_gdbm_full_read (GDBM_FILE dbf, void *buffer, size_t size) +{ + char *ptr = buffer; + while (size) + { + ssize_t rdbytes = __read (dbf, ptr, size); + if (rdbytes == -1) + { + if (errno == EINTR) + continue; + return GDBM_FILE_READ_ERROR; + } + if (rdbytes == 0) + return GDBM_FILE_EOF; + ptr += rdbytes; + size -= rdbytes; + } + return 0; +} + +/* Write exactly SIZE bytes of data from BUFFER tp DBF. Return 0 on + success, and GDBM_FILE_READ_ERROR on error. In the latter case errno + will keep actual system error code. */ +int +_gdbm_full_write (GDBM_FILE dbf, void *buffer, size_t size) +{ + char *ptr = buffer; + while (size) + { + ssize_t wrbytes = __write (dbf, ptr, size); + if (wrbytes == -1) + { + if (errno == EINTR) + continue; + return GDBM_FILE_WRITE_ERROR; + } + if (wrbytes == 0) + { + errno = ENOSPC; + return GDBM_FILE_WRITE_ERROR; + } + ptr += wrbytes; + size -= wrbytes; + } + return 0; +} diff --git a/src/gdbmerrno.c b/src/gdbmerrno.c index 9d7a2bf..d9c5c3f 100644 --- a/src/gdbmerrno.c +++ b/src/gdbmerrno.c @@ -53,6 +53,7 @@ const char * const gdbm_errlist[_GDBM_MAX_ERRNO+1] = { N_("Wrong file offset"), /* GDBM_BAD_FILE_OFFSET */ N_("Bad file flags"), /* GDBM_BAD_OPEN_FLAGS */ N_("Cannot stat file"), /* GDBM_FILE_STAT_ERROR */ + N_("Unexpected end of file"), /* GDBM_FILE_EOF */ }; const char * diff --git a/src/gdbmopen.c b/src/gdbmopen.c index 3b178d2..09eb629 100644 --- a/src/gdbmopen.c +++ b/src/gdbmopen.c @@ -58,13 +58,13 @@ gdbm_open (const char *file, int block_size, int flags, int mode, GDBM_FILE dbf; /* The record to return. */ struct stat file_stat; /* Space for the stat information. */ int len; /* Length of the file name. */ - int num_bytes; /* Used in reading and writing. */ off_t file_pos; /* Used with seeks. */ int file_block_size; /* Block size to use for a new file. */ int index; /* Used as a loop index. */ char need_trunc; /* Used with GDBM_NEWDB and locking to avoid truncating a file from under a reader. */ - + int rc; /* temporary error code */ + /* Initialize the gdbm_errno variable. */ gdbm_errno = GDBM_NO_ERROR; @@ -282,29 +282,29 @@ gdbm_open (const char *file, int block_size, int flags, int mode, /* Write initial configuration to the file. */ /* Block 0 is the file header and active avail block. */ - num_bytes = __write (dbf, dbf->header, dbf->header->block_size); - if (num_bytes != dbf->header->block_size) + rc = _gdbm_full_write (dbf, dbf->header, dbf->header->block_size); + if (rc) { SAVE_ERRNO (gdbm_close (dbf)); - gdbm_errno = GDBM_FILE_WRITE_ERROR; + gdbm_errno = rc; return NULL; } /* Block 1 is the initial bucket directory. */ - num_bytes = __write (dbf, dbf->dir, dbf->header->dir_size); - if (num_bytes != dbf->header->dir_size) + rc = _gdbm_full_write (dbf, dbf->dir, dbf->header->dir_size); + if (rc) { SAVE_ERRNO (gdbm_close (dbf)); - gdbm_errno = GDBM_FILE_WRITE_ERROR; + gdbm_errno = rc; return NULL; } /* Block 2 is the only bucket. */ - num_bytes = __write (dbf, dbf->bucket, dbf->header->bucket_size); - if (num_bytes != dbf->header->bucket_size) + rc = _gdbm_full_write (dbf, dbf->bucket, dbf->header->bucket_size); + if (rc) { SAVE_ERRNO (gdbm_close (dbf)); - gdbm_errno = GDBM_FILE_WRITE_ERROR; + gdbm_errno = rc; return NULL; } @@ -321,11 +321,11 @@ gdbm_open (const char *file, int block_size, int flags, int mode, gdbm_file_header partial_header; /* For the first part of it. */ /* Read the partial file header. */ - num_bytes = __read (dbf, &partial_header, sizeof (gdbm_file_header)); - if (num_bytes != sizeof (gdbm_file_header)) + rc = _gdbm_full_read (dbf, &partial_header, sizeof (gdbm_file_header)); + if (rc) { SAVE_ERRNO (gdbm_close (dbf)); - gdbm_errno = GDBM_FILE_READ_ERROR; + gdbm_errno = rc; return NULL; } @@ -360,12 +360,12 @@ gdbm_open (const char *file, int block_size, int flags, int mode, return NULL; } memcpy (dbf->header, &partial_header, sizeof (gdbm_file_header)); - num_bytes = __read (dbf, &dbf->header->avail.av_table[1], - dbf->header->block_size-sizeof (gdbm_file_header)); - if (num_bytes != dbf->header->block_size-sizeof (gdbm_file_header)) + rc = _gdbm_full_read (dbf, &dbf->header->avail.av_table[1], + dbf->header->block_size-sizeof (gdbm_file_header)); + if (rc) { SAVE_ERRNO (gdbm_close (dbf)); - gdbm_errno = GDBM_FILE_READ_ERROR; + gdbm_errno = rc; return NULL; } @@ -387,11 +387,11 @@ gdbm_open (const char *file, int block_size, int flags, int mode, return NULL; } - num_bytes = __read (dbf, dbf->dir, dbf->header->dir_size); - if (num_bytes != dbf->header->dir_size) + rc = _gdbm_full_read (dbf, dbf->dir, dbf->header->dir_size); + if (rc) { SAVE_ERRNO (gdbm_close (dbf)); - gdbm_errno = GDBM_FILE_READ_ERROR; + gdbm_errno = rc; return NULL; } diff --git a/src/gdbmstore.c b/src/gdbmstore.c index 5f1bb67..367cc4f 100644 --- a/src/gdbmstore.c +++ b/src/gdbmstore.c @@ -42,12 +42,11 @@ gdbm_store (GDBM_FILE dbf, datum key, datum content, int flags) int elem_loc; /* The location in hash bucket. */ off_t file_adr; /* The address of new space in the file. */ off_t file_pos; /* The position after a lseek. */ - int num_bytes; /* Used for error detection. */ off_t free_adr; /* For keeping track of a freed section. */ int free_size; int new_size; /* Used in allocating space. */ char *temp; /* Used in _gdbm_findkey call. */ - + int rc; /* First check to make sure this guy is a writer. */ if (dbf->read_write == GDBM_READER) @@ -139,10 +138,12 @@ gdbm_store (GDBM_FILE dbf, datum key, datum content, int flags) /* Write the data to the file. */ file_pos = __lseek (dbf, file_adr, L_SET); if (file_pos != file_adr) _gdbm_fatal (dbf, _("lseek error")); - num_bytes = __write (dbf, key.dptr, key.dsize); - if (num_bytes != key.dsize) _gdbm_fatal (dbf, _("write error")); - num_bytes = __write (dbf, content.dptr, content.dsize); - if (num_bytes != content.dsize) _gdbm_fatal (dbf, _("write error")); + rc = _gdbm_full_write (dbf, key.dptr, key.dsize); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + rc = _gdbm_full_write (dbf, content.dptr, content.dsize); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); /* Current bucket has changed. */ dbf->cache_entry->ca_changed = TRUE; diff --git a/src/proto.h b/src/proto.h index de96878..39a5e31 100644 --- a/src/proto.h +++ b/src/proto.h @@ -55,4 +55,8 @@ int _gdbm_mapped_sync (GDBM_FILE); void _gdbm_unlock_file (GDBM_FILE); int _gdbm_lock_file (GDBM_FILE); +/* From fullio.c */ +int _gdbm_full_read (GDBM_FILE, void *, size_t); +int _gdbm_full_write (GDBM_FILE, void *, size_t); + diff --git a/src/testgdbm.c b/src/testgdbm.c index b22e343..9d026cc 100644 --- a/src/testgdbm.c +++ b/src/testgdbm.c @@ -107,6 +107,7 @@ _gdbm_avail_list_size (GDBM_FILE dbf, size_t min_size) int size; avail_block *av_stk; size_t lines; + int rc; lines = 4 + dbf->header->avail.count; if (lines > min_size) @@ -128,9 +129,12 @@ _gdbm_avail_list_size (GDBM_FILE dbf, size_t min_size) break; } - if (__read (dbf, av_stk, size) != size) + if ((rc = _gdbm_full_read (dbf, av_stk, size))) { - error (0, "read: %s", strerror (errno)); + if (rc == GDBM_FILE_EOF) + error (0, "read: %s", gdbm_strerror (rc)); + else + error (0, "read: %s (%s)", gdbm_strerror (rc), strerror (errno)); break; } @@ -150,6 +154,7 @@ _gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf) int temp; int size; avail_block *av_stk; + int rc; /* Print the the header avail block. */ fprintf (fp, _("\nheader block\nsize = %d\ncount = %d\n"), @@ -178,9 +183,12 @@ _gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf) break; } - if (__read (dbf, av_stk, size) != size) + if ((rc = _gdbm_full_read (dbf, av_stk, size))) { - error (0, "read: %s", strerror (errno)); + if (rc == GDBM_FILE_EOF) + error (0, "read: %s", gdbm_strerror (rc)); + else + error (0, "read: %s (%s)", gdbm_strerror (rc), strerror (errno)); break; } diff --git a/src/update.c b/src/update.c index 9ae3732..79865c8 100644 --- a/src/update.c +++ b/src/update.c @@ -29,14 +29,14 @@ static void write_header (GDBM_FILE); static void write_header (GDBM_FILE dbf) { - int num_bytes; /* Return value for write. */ off_t file_pos; /* Return value for lseek. */ - + int rc; + file_pos = __lseek (dbf, 0L, L_SET); if (file_pos != 0) _gdbm_fatal (dbf, _("lseek error")); - num_bytes = __write (dbf, dbf->header, dbf->header->block_size); - if (num_bytes != dbf->header->block_size) - _gdbm_fatal (dbf, _("write error")); + rc = _gdbm_full_write (dbf, dbf->header, dbf->header->block_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); /* Sync the file if fast_write is FALSE. */ if (dbf->fast_write == FALSE) @@ -49,9 +49,8 @@ write_header (GDBM_FILE dbf) void _gdbm_end_update (GDBM_FILE dbf) { - int num_bytes; /* Return value for write. */ off_t file_pos; /* Return value for lseek. */ - + int rc; /* Write the current bucket. */ if (dbf->bucket_changed && (dbf->cache_entry != NULL)) @@ -81,9 +80,9 @@ _gdbm_end_update (GDBM_FILE dbf) { file_pos = __lseek (dbf, dbf->header->dir, L_SET); if (file_pos != dbf->header->dir) _gdbm_fatal (dbf, _("lseek error")); - num_bytes = __write (dbf, dbf->dir, dbf->header->dir_size); - if (num_bytes != dbf->header->dir_size) - _gdbm_fatal (dbf, _("write error")); + rc = _gdbm_full_write (dbf, dbf->dir, dbf->header->dir_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); dbf->directory_changed = FALSE; if (!dbf->header_changed && dbf->fast_write == FALSE) __fsync (dbf); -- cgit v1.2.1