summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2016-07-15 11:46:08 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2016-07-15 11:57:40 (GMT)
commit44ca2b760b393e993b2a42fc489fa170910ad810 (patch) (side-by-side diff)
tree12aa7390b19bed8aba2535cedb04376c45f820a5
parent5580c3248794803a8e45dd58b45933a32637954d (diff)
downloadgdbm-44ca2b760b393e993b2a42fc489fa170910ad810.tar.gz
gdbm-44ca2b760b393e993b2a42fc489fa170910ad810.tar.bz2
gdbm_open adjusts requested block size to accomodate integer number of directory offsets.
* src/gdbm.h.in (GDBM_BSEXACT): New flag. * src/gdbmopen.c (compute_directory_size): New function. (gdbm_open): When creating new database, adjust the requested block size so that the block holds integer number of directory indices. Disable this behavior if GDBM_BSEXACT flag is set (this reverts to old behavior). Always unset GDBM_BSEXACT if using block size returned by stat(2). This makes sure gdbm_open succeeds on file systems reporting block sizes not divisible by 512. * src/gdbmconst.h (GDBM_MIN_BLOCK_SIZE): New constant. * src/gdbmimp.c (gdbm_import_from_file): Use GDBM_MIN_BLOCK_SIZE instead of the hardcoded value. * NEWS: Document GDBM_BSEXACT. * doc/gdbm.texi: Likewise.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--NEWS12
-rw-r--r--doc/gdbm.texi21
-rw-r--r--src/gdbm.h.in3
-rw-r--r--src/gdbmconst.h3
-rw-r--r--src/gdbmimp.c8
-rw-r--r--src/gdbmopen.c89
6 files changed, 91 insertions, 45 deletions
diff --git a/NEWS b/NEWS
index 0c0525d..99d4963 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU dbm NEWS -- history of user-visible changes. 2016-07-13
+GNU dbm NEWS -- history of user-visible changes. 2016-07-15
Copyright (C) 1990-2016 Free Software Foundation, Inc.
See the end of file for copying conditions.
@@ -23,6 +23,16 @@ Instead it sets gdbm_errno to GDBM_MALLOC_ERROR and returns NULL datum.
* Per-database error codes
+* New gdbm_open flag: GDBM_BSEXACT
+
+When creating a new database, the gdbm_open function will adjust the
+requested block size so that the block can hold integer number of
+directory entries. Thus, the resulting block size can be bigger than
+the requested one. If the GDBM_BSEXACT flag is set, this behavior is
+suppressed and gdbm_open will try to force exactly the requested block
+size. If unable to do so, it will set the gdbm_errno variable to
+GDBM_BLOCK_SIZE_ERROR and return NULL.
+
* New functions
** gdbm_last_errno
diff --git a/doc/gdbm.texi b/doc/gdbm.texi
index d172ad7..8096215 100644
--- a/doc/gdbm.texi
+++ b/doc/gdbm.texi
@@ -258,9 +258,14 @@ characters to this name).
It is used during initialization to determine the size of various
constructs. It is the size of a single transfer from disk to
memory. This parameter is ignored if the file has been previously
-initialized. The minimum size is 512. If the value is less than 512,
-the file system block size is used, otherwise the value of
-@var{block_size} is used.
+initialized. If the value is less than 512, the file system block
+size is used instead. The size is adjusted so that the block can hold
+exact number of directory entries, so that the effective block size
+can be slightly greater than requested. However, if the
+@samp{GDBM_BSEXACT} flag is set and the size needs to be adjusted, the
+function will return with error status, setting the @samp{gdbm_errno}
+variable to @samp{GDBM_BLOCK_SIZE_ERROR}.
+
@item flags
@kwindex GDBM_READER
@kwindex GDBM_WRITER
@@ -287,6 +292,12 @@ from performing any locking on the database file, and @samp{GDBM_NOMMAP},
which disables the memory mapping mechanism. The option @samp{GDBM_FAST} is
now obsolete, since @code{gdbm} defaults to no-sync mode.
+@kwindex GDBM_BSEXACT
+If this flag is set and the requested @var{block_size} cannot be used
+without adjustment, @code{gdbm_open} will refuse to create the
+databases. In this case it will set the @samp{gdbm_errno}
+variable to @samp{GDBM_BLOCK_SIZE_ERROR} and return @samp{NULL}.
+
@kwindex GDBM_CLOEXEC
@cindex close-on-exec
If the host @samp{open} call
@@ -1233,9 +1244,11 @@ No error occurred.
Memory allocation failed. Not enough memory.
@kwindex GDBM_BLOCK_SIZE_ERROR
+@kwindex GDBM_BSEXACT
@item GDBM_BLOCK_SIZE_ERROR
This error is set by the @code{gdbm_open} function (@pxref{Open}), if
-the value of its @var{block_size} argument is incorrect.
+the value of its @var{block_size} argument is incorrect and the
+@samp{GDBM_BSEXACT} flag is set.
@kwindex GDBM_FILE_OPEN_ERROR
@item GDBM_FILE_OPEN_ERROR
diff --git a/src/gdbm.h.in b/src/gdbm.h.in
index a3d0461..07d3d1f 100644
--- a/src/gdbm.h.in
+++ b/src/gdbm.h.in
@@ -49,6 +49,9 @@ extern "C" {
# define GDBM_NOLOCK 0x040 /* Don't do file locking operations. */
# define GDBM_NOMMAP 0x080 /* Don't use mmap(). */
# define GDBM_CLOEXEC 0x100 /* Close the underlying fd on exec(3) */
+# define GDBM_BSEXACT 0x200 /* Don't adjust block_size. Bail out with
+ GDBM_BLOCK_SIZE_ERROR error if unable to
+ set it. */
/* Parameters to gdbm_store for simple insertion or replacement in the
case that the key is already in the database. */
diff --git a/src/gdbmconst.h b/src/gdbmconst.h
index 56820db..66b0fb2 100644
--- a/src/gdbmconst.h
+++ b/src/gdbmconst.h
@@ -38,6 +38,9 @@
/* Size of a hash value, in bits */
#define GDBM_HASH_BITS 31
+/* Minimal acceptable block size */
+#define GDBM_MIN_BLOCK_SIZE 512
+
/* In freeing blocks, we will ignore any blocks smaller (and equal) to
IGNORE_SIZE number of bytes. */
#define IGNORE_SIZE 4
diff --git a/src/gdbmimp.c b/src/gdbmimp.c
index 57d1933..fc2710d 100644
--- a/src/gdbmimp.c
+++ b/src/gdbmimp.c
@@ -65,14 +65,14 @@ gdbm_import_from_file (GDBM_FILE dbf, FILE *fp, int flag)
}
/* Allocate buffers. */
- kbufsize = 512;
+ kbufsize = GDBM_MIN_BLOCK_SIZE;
kbuffer = malloc (kbufsize);
if (kbuffer == NULL)
{
gdbm_set_errno (NULL, GDBM_MALLOC_ERROR, FALSE);
return -1;
}
- dbufsize = 512;
+ dbufsize = GDBM_MIN_BLOCK_SIZE;
dbuffer = malloc (dbufsize);
if (dbuffer == NULL)
{
@@ -95,7 +95,7 @@ gdbm_import_from_file (GDBM_FILE dbf, FILE *fp, int flag)
if (size > kbufsize)
{
- kbufsize = (size + 512);
+ kbufsize = (size + GDBM_MIN_BLOCK_SIZE);
kbuffer = realloc (kbuffer, kbufsize);
if (kbuffer == NULL)
{
@@ -127,7 +127,7 @@ gdbm_import_from_file (GDBM_FILE dbf, FILE *fp, int flag)
}
if (size > dbufsize)
{
- dbufsize = (size + 512);
+ dbufsize = (size + GDBM_MIN_BLOCK_SIZE);
dbuffer = realloc (dbuffer, dbufsize);
if (dbuffer == NULL)
{
diff --git a/src/gdbmopen.c b/src/gdbmopen.c
index 52814df..5210001 100644
--- a/src/gdbmopen.c
+++ b/src/gdbmopen.c
@@ -31,19 +31,38 @@
# error "Unsupported off_t size, contact GDBM maintainer. What crazy system is this?!?"
#endif
+static void
+compute_directory_size (GDBM_FILE dbf, blksize_t block_size,
+ int *ret_dir_size, int *ret_dir_bits)
+{
+ /* Create the initial hash table directory. */
+ int dir_size = 8 * sizeof (off_t);
+ int dir_bits = 3;
+
+ while (dir_size < block_size && dir_bits < GDBM_HASH_BITS - 3)
+ {
+ dir_size <<= 1;
+ dir_bits++;
+ }
+
+ *ret_dir_size = dir_size;
+ *ret_dir_bits = dir_bits;
+}
+
+
/* Initialize dbm system. FILE is a pointer to the file name. If the file
has a size of zero bytes, a file initialization procedure is performed,
setting up the initial structure in the file. BLOCK_SIZE is used during
initialization to determine the size of various constructs. If the value
- is less than 512, the file system blocksize is used, otherwise the value
- of BLOCK_SIZE is used. BLOCK_SIZE is ignored if the file has previously
- initialized. If FLAGS is set to GDBM_READ the user wants to just
- read the database and any call to dbm_store or dbm_delete will fail. Many
- readers can access the database at the same time. If FLAGS is set to
- GDBM_WRITE, the user wants both read and write access to the database and
- requires exclusive access. If FLAGS is GDBM_WRCREAT, the user wants
- both read and write access to the database and if the database does not
- exist, create a new one. If FLAGS is GDBM_NEWDB, the user want a
+ is less than GDBM_MIN_BLOCK_SIZE, the file system blocksize is used,
+ otherwise the value of BLOCK_SIZE is used. BLOCK_SIZE is ignored if the
+ file has previously initialized. If FLAGS is set to GDBM_READ the user
+ wants to just read the database and any call to dbm_store or dbm_delete
+ will fail. Many readers can access the database at the same time. If FLAGS
+ is set to GDBM_WRITE, the user wants both read and write access to the
+ database and requires exclusive access. If FLAGS is GDBM_WRCREAT, the user
+ wants both read and write access to the database and if the database does
+ not exist, create a new one. If FLAGS is GDBM_NEWDB, the user want a
new database created, regardless of whether one existed, and wants read
and write access to the new database. Any error detected will cause a
return value of null and an approprate value will be in gdbm_errno. If
@@ -59,7 +78,6 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
struct stat file_stat; /* Space for the stat information. */
int len; /* Length of the file name. */
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. */
@@ -211,18 +229,32 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
/* Decide if this is a new file or an old file. */
if (file_stat.st_size == 0)
{
-
/* This is a new file. Create an empty database. */
-
+ int dir_size, dir_bits;
+
/* Start with the blocksize. */
- if (block_size < 512)
- file_block_size = STATBLKSIZE(file_stat);
- else
- file_block_size = block_size;
-
+ if (block_size < GDBM_MIN_BLOCK_SIZE)
+ {
+ block_size = STATBLKSIZE (file_stat);
+ flags &= ~GDBM_BSEXACT;
+ }
+ compute_directory_size (dbf, block_size, &dir_size, &dir_bits);
+ /* Check for correct block_size. */
+ if (dir_size != block_size)
+ {
+ if (flags & GDBM_BSEXACT)
+ {
+ gdbm_close (dbf);
+ gdbm_set_errno (NULL, GDBM_BLOCK_SIZE_ERROR, FALSE);
+ return NULL;
+ }
+ else
+ block_size = dir_size;
+ }
+
/* Get space for the file header. It will be written to disk, so
make sure there's no garbage in it. */
- dbf->header = (gdbm_file_header *) calloc (1, file_block_size);
+ dbf->header = (gdbm_file_header *) calloc (1, block_size);
if (dbf->header == NULL)
{
gdbm_close (dbf);
@@ -232,24 +264,9 @@ gdbm_open (const char *file, int block_size, int flags, int mode,
/* Set the magic number and the block_size. */
dbf->header->header_magic = GDBM_MAGIC;
- dbf->header->block_size = file_block_size;
-
- /* Create the initial hash table directory. */
- dbf->header->dir_size = 8 * sizeof (off_t);
- dbf->header->dir_bits = 3;
- while (dbf->header->dir_size < dbf->header->block_size)
- {
- dbf->header->dir_size <<= 1;
- dbf->header->dir_bits += 1;
- }
-
- /* Check for correct block_size. */
- if (dbf->header->dir_size != dbf->header->block_size)
- {
- gdbm_close (dbf);
- gdbm_set_errno (NULL, GDBM_BLOCK_SIZE_ERROR, FALSE);
- return NULL;
- }
+ dbf->header->block_size = block_size;
+ dbf->header->dir_size = dir_size;
+ dbf->header->dir_bits = dir_bits;
/* Allocate the space for the directory. */
dbf->dir = (off_t *) malloc (dbf->header->dir_size);

Return to:

Send suggestions and report system problems to the System administrator.