Fix bugzilla bugs 69753 and 39465. Temporary DB files are now created
authornelsonb%netscape.com
Wed, 14 Mar 2001 00:29:55 +0000
changeset 1282 bdb633a7bb953847d2699950859c88a66a4be248
parent 1281 55985a0a9c24ecfa506db30d8446584b1f0b08c4
child 1283 03b28b6818e3fb24959c597565e76dc3c0a15847
push idunknown
push userunknown
push dateunknown
bugs69753, 39465
Fix bugzilla bugs 69753 and 39465. Temporary DB files are now created in $TMP, $TMPDIR or $TEMP, if possible. On windows, temporary DB files are now opened in binary mode and are removed when the progam terminates or the DB is closed. Multiple temp DB files can now be created in the same program. EOF value on disk is now updated when file is extended.
dbm/include/hash.h
dbm/src/h_page.c
dbm/src/hash.c
dbm/src/mktemp.c
--- a/dbm/include/hash.h
+++ b/dbm/include/hash.h
@@ -46,19 +46,19 @@ typedef enum {
 
 /* Buffer Management structures */
 typedef struct _bufhead BUFHEAD;
 
 struct _bufhead {
 	BUFHEAD		*prev;		/* LRU links */
 	BUFHEAD		*next;		/* LRU links */
 	BUFHEAD		*ovfl;		/* Overflow page buffer header */
-	uint32	 addr;		/* Address of this page */
+	uint32	 	addr;		/* Address of this page */
 	char		*page;		/* Actual page data */
-	char        is_disk;
+	char     	is_disk;
 	char	 	flags;
 #define	BUF_MOD		0x0001
 #define BUF_DISK	0x0002
 #define	BUF_BUCKET	0x0004
 #define	BUF_PIN		0x0008
 };
 
 #define IS_BUCKET(X)	((X) & BUF_BUCKET)
@@ -72,17 +72,17 @@ typedef int DBFILE_PTR;
 #define EXISTS(path)
 #else
 #define DBFILE_OPEN(path, flag,mode) open((path), (flag), (mode))
 #endif
 /* Hash Table Information */
 typedef struct hashhdr {		/* Disk resident portion */
 	int32		magic;		/* Magic NO for hash tables */
 	int32		version;	/* Version ID */
-	uint32	lorder;		/* Byte Order */
+	uint32		lorder;		/* Byte Order */
 	int32		bsize;		/* Bucket/Page Size */
 	int32		bshift;		/* Bucket shift */
 	int32		dsize;		/* Directory Size */
 	int32		ssize;		/* Segment Size */
 	int32		sshift;		/* Segment shift */
 	int32		ovfl_point;	/* Where overflow pages are being 
 					 * allocated */
 	int32		last_freed;	/* Last overflow page freed */
@@ -92,17 +92,17 @@ typedef struct hashhdr {		/* Disk reside
 					 * table */
 	int32		ffactor;	/* Fill factor */
 	int32		nkeys;		/* Number of keys in hash table */
 	int32		hdrpages;	/* Size of table header */
 	int32		h_charkey;	/* value of hash(CHARKEY) */
 #define NCACHED	32			/* number of bit maps and spare 
 					 * points */
 	int32		spares[NCACHED];/* spare pages for overflow */
-	uint16	bitmaps[NCACHED];	/* address of overflow page 
+	uint16		bitmaps[NCACHED];	/* address of overflow page 
 						 * bitmaps */
 } HASHHDR;
 
 typedef struct htab	 {		/* Memory resident data structure */
 	HASHHDR 	hdr;		/* Header */
 	int		nsegs;		/* Number of allocated segments */
 	int		exsegs;		/* Number of extra allocated 
 					 * segments */
@@ -118,22 +118,25 @@ typedef struct htab	 {		/* Memory reside
 	int		cndx;		/* Index of next item on cpage */
 	int		dbmerrno;		/* Error Number -- for DBM 
 					 * compatability */
 	int		new_file;	/* Indicates if fd is backing store 
 					 * or no */
 	int		save_file;	/* Indicates whether we need to flush 
 					 * file at
 					 * exit */
-	uint32	*mapp[NCACHED];	/* Pointers to page maps */
+	uint32		*mapp[NCACHED];	/* Pointers to page maps */
 	int		nmaps;		/* Initial number of bitmaps */
 	int		nbufs;		/* Number of buffers left to 
 					 * allocate */
 	BUFHEAD 	bufhead;	/* Header of buffer lru list */
 	SEGMENT 	*dir;		/* Hash Bucket directory */
+	off_t		file_size;	/* in bytes */
+	char		is_temp;	/* unlink file on close */
+	char		updateEOF;	/* close and reopen on flush */
 } HTAB;
 
 /*
  * Constants
  */
 #define DATABASE_CORRUPTED_ERROR -999   /* big ugly abort, delete database */
 #define	OLD_MAX_BSIZE		65536		/* 2^16 */
 #define MAX_BSIZE       	32l*1024l         /* 2^15 */
--- a/dbm/src/h_page.c
+++ b/dbm/src/h_page.c
@@ -67,32 +67,37 @@ static char sccsid[] = "@(#)hash_page.c	
 #endif
 
 #if defined(macintosh)
 #include <unistd.h>
 #endif
 
 #include <errno.h>
 #include <fcntl.h>
+#if defined(_WIN32) || defined(_WINDOWS) 
+#include <io.h>
+#endif
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) && !defined(XP_OS2_VACPP)
 #include <unistd.h>
 #endif
 
 #include <assert.h>
 
 #include "mcom_db.h"
 #include "hash.h"
 #include "page.h"
 /* #include "extern.h" */
 
+extern int mkstempflags(char *path, int extraFlags);
+
 static uint32	*fetch_bitmap __P((HTAB *, uint32));
 static uint32	 first_free __P((uint32));
 static int	 open_temp __P((HTAB *));
 static uint16	 overflow_page __P((HTAB *));
 static void	 squeeze_key __P((uint16 *, const DBT *, const DBT *));
 static int	 ugly_split
 		    __P((HTAB *, uint32, BUFHEAD *, BUFHEAD *, int, int));
 
@@ -821,16 +826,17 @@ extern int
  *	-1 ==>failure
  */
 extern int
 __put_page(HTAB *hashp, char *p, uint32 bucket, int is_bucket, int is_bitmap)
 {
 	register int fd, page;
 	size_t size;
 	int wsize;
+	off_t offset;
 
 	size = hashp->BSIZE;
 	if ((hashp->fp == -1) && open_temp(hashp))
 		return (-1);
 	fd = hashp->fp;
 
 	if (hashp->LORDER != BYTE_ORDER) {
 		register int i;
@@ -855,25 +861,30 @@ extern int
 
 		}
 	}
 
 	if (is_bucket)
 		page = BUCKET_TO_PAGE(bucket);
 	else
 		page = OADDR_TO_PAGE(bucket);
-	if ((MY_LSEEK(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) ||
+	offset = (off_t)page << hashp->BSHIFT;
+	if ((MY_LSEEK(fd, offset, SEEK_SET) == -1) ||
 	    ((wsize = write(fd, p, size)) == -1))
 		/* Errno is set */
 		return (-1);
 	if ((unsigned)wsize != size) {
 		errno = EFTYPE;
 		return (-1);
 	}
-
+#if defined(_WIN32) || defined(_WINDOWS) 
+	if (offset + size > hashp->file_size) {
+		hashp->updateEOF = 1;
+	}
+#endif
 	/* put the page back the way it was so that it isn't byteswapped
 	 * if it remains in memory - LJM
 	 */
 	if (hashp->LORDER != BYTE_ORDER) {
 		register int i;
 		register int max;
 
 		if (is_bitmap) {
@@ -1146,30 +1157,64 @@ extern void
  *	-1 failure
  */
 static int
 open_temp(HTAB *hashp)
 {
 #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) && !defined(XP_OS2)
 	sigset_t set, oset;
 #endif
-	static char namestr[] = "_hashXXXXXX";
+	char * tmpdir;
+	int    len;
+	static const char namestr[] = "/_hashXXXXXX";
+	char filename[1024];
+	char last;
 
 #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) && !defined(XP_OS2)
 	/* Block signals; make sure file goes away at process exit. */
 	(void)sigfillset(&set);
 	(void)sigprocmask(SIG_BLOCK, &set, &oset);
 #endif
 
-	if ((hashp->fp = mkstemp(namestr)) != -1) {
-		(void)unlink(namestr);
-#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) && !defined(XP_OS2)
+	filename[0] = 0;
+#if defined(macintosh)
+	strcat(filename, namestr + 1);
+#else
+	tmpdir = getenv("TMP");
+	if (!tmpdir)
+		tmpdir = getenv("TMPDIR");
+	if (!tmpdir)
+		tmpdir = getenv("TEMP");
+	if (!tmpdir)
+		tmpdir = ".";
+	len = strlen(tmpdir);
+	if (len && len < (sizeof filename - sizeof namestr)) {
+		strcpy(filename, tmpdir);
+	}
+	len = strlen(filename);
+	last = tmpdir[len - 1];
+	strcat(filename, (last == '/' || last == '\\') ? namestr + 1 : namestr);
+#endif
+
+#if defined(_WIN32) || defined(_WINDOWS) || defined(XP_OS2)
+	if ((hashp->fp = mkstempflags(filename, _O_BINARY|_O_TEMPORARY)) != -1) {
+		if (hashp->filename) {
+			free(hashp->filename);
+		}
+		hashp->filename = strdup(filename);
+		hashp->is_temp = 1;
+	}
+#else
+	if ((hashp->fp = mkstemp(filename)) != -1) {
+		(void)unlink(filename);
+#if !defined(macintosh)
 		(void)fcntl(hashp->fp, F_SETFD, 1);
 #endif									  
 	}
+#endif
 
 #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) && !defined(XP_OS2)
 	(void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
 #endif
 	return (hashp->fp != -1 ? 0 : -1);
 }
 
 /*
--- a/dbm/src/hash.c
+++ b/dbm/src/hash.c
@@ -60,25 +60,26 @@ static char sccsid[] = "@(#)hash.c	8.9 (
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) && !defined(XP_OS2_VACPP)
 #include <unistd.h>
 #endif
+#if defined(_WIN32) || defined(_WINDOWS) 
+#include <windows.h>
+#endif
 
 #ifdef XP_OS2_VACPP
 #include "types.h"
 #define EPERM SOCEPERM
 #endif
 
-#ifdef DEBUG
 #include <assert.h>
-#endif
 
 #include "mcom_db.h"
 #include "hash.h"
 #include "page.h"
 
 /*
 #include "extern.h"
 */
@@ -112,35 +113,31 @@ static void  swap_header_copy __P((HASHH
 #define	ABNORMAL (1)
 
 #ifdef HASH_STATISTICS
 int hash_accesses, hash_collisions, hash_expansions, hash_overflows;
 #endif
 
 /* A new Lou (montulli@mozilla.com) routine.
  *
- * The database is screwed.  Delete it by 
- * making it a zero length file
+ * The database is screwed.  
  *
- * This zero's hashp so that the
- * database can't be accessed any more
+ * This closes the file, flushing buffers as appropriate.
  */
 static void
 __remove_database(DB *dbp)
 {
 	HTAB *hashp = (HTAB *)dbp->internal;
+
+	assert(0);
+
 	if (!hashp)
 		return;
-
-	if(hashp->fp != NO_FILE)
-		close(hashp->fp);
-	if(hashp->filename)
-		unlink(hashp->filename);
-	dbp->internal = NULL; /* zero the internal stuff */
-
+	hdestroy(hashp);
+	dbp->internal = NULL; 
 }
 
 /************************** INTERFACE ROUTINES ***************************/
 /* OPEN/CLOSE */
 
 
 extern DB *
 __hash_open(const char *file, int flags, int mode, const HASHINFO *info, int dflags)
@@ -184,16 +181,17 @@ extern DB *
 	}
 	else if(statbuf.st_mtime && statbuf.st_size == 0)
 	{
 		/* check for a zero length file and delete it
 	 	 * if it exists
 	 	 */
 		new_table = 1;
 	}
+	hashp->file_size = statbuf.st_size;
 
 	if (file) {				 
 
 #if defined(_WIN32) || defined(_WINDOWS) || defined (macintosh)  || defined(XP_OS2_VACPP)
 		if ((hashp->fp = DBFILE_OPEN(file, flags | O_BINARY, mode)) == -1)
 			RETURN_ERROR(errno, error0);
 #else
  	if ((hashp->fp = open(file, flags, mode)) == -1)
@@ -556,27 +554,91 @@ hdestroy(HTAB *hashp)
 	/* Free Bigmaps */
 	for (i = 0; i < hashp->nmaps; i++)
 		if (hashp->mapp[i])
 			free(hashp->mapp[i]);
 
 	if (hashp->fp != -1)
 		(void)close(hashp->fp);
 
-	if(hashp->filename)
+	if(hashp->filename) {
+#if defined(_WIN32) || defined(_WINDOWS) || defined(XP_OS2)
+		if (hashp->is_temp)
+			(void)unlink(hashp->filename);
+#endif
 		free(hashp->filename);
+	}
 
 	free(hashp);
 
 	if (save_errno) {
 		errno = save_errno;
 		return (DBM_ERROR);
 	}
 	return (SUCCESS);
 }
+
+#if defined(_WIN32) || defined(_WINDOWS) 
+/*
+ * Close and reopen file to force file length update on windows. 
+ *
+ * Returns:
+ *	 0 == OK
+ *	-1 DBM_ERROR
+ */
+static int
+update_EOF(HTAB *hashp)
+{
+#if defined(DBM_REOPEN_ON_FLUSH)
+	char *      file       = hashp->filename;
+	off_t       file_size;
+	int         flags;
+	int         mode       = -1;
+	struct stat statbuf;
+
+	memset(&statbuf, 0, sizeof statbuf);
+
+	/* make sure we won't lose the file by closing it. */
+	if (!file || (stat(file, &statbuf)  && (errno == ENOENT)))  {
+		/* pretend we did it. */
+		return 0;
+	}
+
+	(void)close(hashp->fp);
+
+	flags = hashp->flags & ~(O_TRUNC | O_CREAT | O_EXCL);
+
+	if ((hashp->fp = DBFILE_OPEN(file, flags | O_BINARY, mode)) == -1)
+		return -1;
+	file_size = lseek(hashp->fp, (off_t)0, SEEK_END);
+	if (file_size == -1) 
+		return -1;
+	hashp->file_size = file_size;
+	return 0;
+#else
+	int    fd        = hashp->fp;
+	off_t  file_size = lseek(fd, (off_t)0, SEEK_END);
+	HANDLE handle    = (HANDLE)_get_osfhandle(fd);
+	BOOL   cool      = FlushFileBuffers(handle);
+#ifdef DEBUG3
+	if (!cool) {
+		DWORD err = GetLastError();
+		(void)fprintf(stderr,
+			"FlushFileBuffers failed, last error = %d, 0x%08x\n",
+			err, err);
+	}
+#endif
+	if (file_size == -1) 
+		return -1;
+	hashp->file_size = file_size;
+	return cool ? 0 : -1;
+#endif
+}
+#endif
+
 /*
  * Write modified pages to disk
  *
  * Returns:
  *	 0 == OK
  *	-1 DBM_ERROR
  */
 static int
@@ -595,16 +657,24 @@ hash_sync(const DB *dbp, uint flags)
 	hashp = (HTAB *)dbp->internal;
 	if(!hashp)
 		return (DBM_ERROR);
 
 	if (!hashp->save_file)
 		return (0);
 	if (__buf_free(hashp, 0, 1) || flush_meta(hashp))
 		return (DBM_ERROR);
+#if defined(_WIN32) || defined(_WINDOWS) 
+	if (hashp->updateEOF && hashp->filename && !hashp->is_temp) {
+		int status = update_EOF(hashp);
+		hashp->updateEOF = 0;
+		if (status)
+			return status;
+	}
+#endif
 	hashp->new_file = 0;
 	return (0);
 }
 
 /*
  * Returns:
  *	 0 == OK
  *	-1 indicates that errno should be set
--- a/dbm/src/mktemp.c
+++ b/dbm/src/mktemp.c
@@ -59,38 +59,45 @@ static char sccsid[] = "@(#)mktemp.c	8.1
 #include <dirent.h>
 #endif
 
 #ifdef _WINDOWS
 #include <process.h>
 #include "winfile.h"
 #endif
 
-static int _gettemp(char *path, register int *doopen);
+static int _gettemp(char *path, register int *doopen, int extraFlags);
 
 int
-mkstemp(path)
-	char *path;
+mkstemp(char *path)
+{
+	int fd;
+
+	return (_gettemp(path, &fd, 0) ? fd : -1);
+}
+
+int
+mkstempflags(char *path, int extraFlags)
 {
 	int fd;
 
-	return (_gettemp(path, &fd) ? fd : -1);
+	return (_gettemp(path, &fd, extraFlags) ? fd : -1);
 }
 
 char *
-mktemp(path)
-	char *path;
+mktemp(char *path)
 {
-	return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
+	return(_gettemp(path, (int *)NULL, 0) ? path : (char *)NULL);
 }
 
-static int
-_gettemp(path, doopen)
-	char *path;
-	register int *doopen;
+/* NB: This routine modifies its input string, and does not always restore it.
+** returns 1 on success, 0 on failure.
+*/
+static int 
+_gettemp(char *path, register int *doopen, int extraFlags)
 {    
 #if !defined(_WINDOWS) || defined(_WIN32)
 	extern int errno;                    
 #endif
 	register char *start, *trv;
 	struct stat sbuf;
 	unsigned int pid;
 
@@ -101,35 +108,39 @@ static int
 		pid /= 10;
 	}
 
 	/*
 	 * check the target directory; if you have six X's and it
 	 * doesn't exist this runs for a *very* long time.
 	 */
 	for (start = trv + 1;; --trv) {
+		char saved;
 		if (trv <= path)
 			break;
-		if (*trv == '/') {
+		saved = *trv;
+		if (saved == '/' || saved == '\\') {
+			int rv;
 			*trv = '\0';
-			if (stat(path, &sbuf))
+			rv = stat(path, &sbuf);
+			*trv = saved;
+			if (rv)
 				return(0);
 			if (!S_ISDIR(sbuf.st_mode)) {
 				errno = ENOTDIR;
 				return(0);
 			}
-			*trv = '/';
 			break;
 		}
 	}
 
 	for (;;) {
 		if (doopen) {
 			if ((*doopen =
-			    open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
+			    open(path, O_CREAT|O_EXCL|O_RDWR|extraFlags, 0600)) >= 0)
 				return(1);
 			if (errno != EEXIST)
 				return(0);
 		}
 		else if (stat(path, &sbuf))
 			return(errno == ENOENT ? 1 : 0);
 
 		/* tricky little algorithm for backward compatibility */