aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2011-10-05 13:06:15 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2011-10-05 13:06:15 +0000
commitd74b094cdcd25b5ce493640873d910a3b788fafe (patch)
tree6b952a6032b11dee04cec340af54d03ad63cce6e
parente0db2284c372ca8be529dec2ea3e78842e5486f3 (diff)
downloadgdbm-d74b094cdcd25b5ce493640873d910a3b788fafe.tar.gz
gdbm-d74b094cdcd25b5ce493640873d910a3b788fafe.tar.bz2
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.
-rw-r--r--ChangeLog21
-rw-r--r--src/Makefile.am1
-rw-r--r--src/bucket.c19
-rw-r--r--src/falloc.c17
-rw-r--r--src/findkey.c7
-rw-r--r--src/fullio.c70
-rw-r--r--src/gdbmerrno.c1
-rw-r--r--src/gdbmopen.c42
-rw-r--r--src/gdbmstore.c13
-rw-r--r--src/proto.h4
-rw-r--r--src/testgdbm.c16
-rw-r--r--src/update.c19
12 files changed, 169 insertions, 61 deletions
diff --git a/ChangeLog b/ChangeLog
index b6f2995..a7ef7dc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,26 @@
2011-10-05 Sergey Poznyakoff <gray@gnu.org.ua>
+ 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 <gray@gnu.org.ua>
+
* src/gdbmopen.c (gdbm_open): Initialize memory used for file
header. This fixes bug #151.
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 <http://www.gnu.org/licenses/>. */
+
+#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);

Return to:

Send suggestions and report system problems to the System administrator.