diff options
-rw-r--r-- | NEWS | 12 | ||||
-rw-r--r-- | doc/gdbm.texi | 21 | ||||
-rw-r--r-- | src/gdbm.h.in | 3 | ||||
-rw-r--r-- | src/gdbmconst.h | 3 | ||||
-rw-r--r-- | src/gdbmimp.c | 8 | ||||
-rw-r--r-- | src/gdbmopen.c | 83 |
6 files changed, 88 insertions, 42 deletions
@@ -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); + 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 - file_block_size = block_size; + 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); |