1/* gdbmreorg.c - Reorganize the database file. */
2
3/*  This file is part of GDBM, the GNU data base manager, by Philip A. Nelson.
4    Copyright (C) 1990, 1991, 1993  Free Software Foundation, Inc.
5
6    GDBM is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    GDBM is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with GDBM; see the file COPYING.  If not, write to
18    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
20    You may contact the author by:
21       e-mail:  phil@cs.wwu.edu
22      us-mail:  Philip A. Nelson
23                Computer Science Department
24                Western Washington University
25                Bellingham, WA 98226
26
27************************************************************************/
28
29
30/* include system configuration before all else. */
31#include "autoconf.h"
32
33#include "gdbmdefs.h"
34#include "gdbmerrno.h"
35#include "extern.h"
36
37#if !HAVE_RENAME
38
39/* Rename takes OLD_NAME and renames it as NEW_NAME.  If it can not rename
40   the file a non-zero value is returned.  OLD_NAME is guaranteed to
41   remain if it can't be renamed. It assumes NEW_NAME always exists (due
42   to being used in gdbm). */
43
44static int
45_gdbm_rename (old_name, new_name)
46     char* old_name;
47     char* new_name;
48{
49  if (unlink (new_name) != 0)
50    return -1;
51
52  if (link (old_name, new_name) != 0)
53    return -1;
54
55  unlink (old_name);
56  return 0;
57
58}
59
60#define rename _gdbm_rename
61#endif
62
63
64
65/* Reorganize the database.  This requires creating a new file and inserting
66   all the elements in the old file DBF into the new file.  The new file
67   is then renamed to the same name as the old file and DBF is updated to
68   contain all the correct information about the new file.  If an error
69   is detected, the return value is negative.  The value zero is returned
70   after a successful reorganization. */
71
72int
73gdbm_reorganize (dbf)
74     gdbm_file_info *dbf;
75
76{
77  gdbm_file_info *new_dbf;		/* The new file. */
78  char *new_name;			/* A temporary name. */
79  int  len;				/* Used in new_name construction. */
80  datum key, nextkey, data;		/* For the sequential sweep. */
81  struct stat fileinfo;			/* Information about the file. */
82  int  index;				/* Use in moving the bucket cache. */
83
84
85  /* Readers can not reorganize! */
86  if (dbf->read_write == GDBM_READER)
87    {
88      gdbm_errno = GDBM_READER_CANT_REORGANIZE;
89      return -1;
90    }
91
92  /* Initialize the gdbm_errno variable. */
93  gdbm_errno = GDBM_NO_ERROR;
94
95  /* Construct new name for temporary file. */
96  len = strlen (dbf->name);
97  new_name = (char *) malloc (len + 3);
98  if (new_name == NULL)
99    {
100      gdbm_errno = GDBM_MALLOC_ERROR;
101      return -1;
102    }
103  strcpy (&new_name[0], dbf->name);
104  new_name[len+2] = 0;
105  new_name[len+1] = '#';
106  while ( (len > 0) && new_name[len-1] != '/')
107    {
108      new_name[len] = new_name[len-1];
109      len -= 1;
110    }
111  new_name[len] = '#';
112
113  /* Get the mode for the old file and open the new database. */
114  fstat (dbf->desc, &fileinfo);
115  new_dbf = gdbm_open (new_name, dbf->header->block_size, GDBM_WRCREAT,
116		       fileinfo.st_mode, dbf->fatal_err);
117
118  if (new_dbf == NULL)
119    {
120      free (new_name);
121      gdbm_errno = GDBM_REORGANIZE_FAILED;
122      return -1;
123    }
124
125
126  /* For each item in the old database, add an entry in the new. */
127  key = gdbm_firstkey (dbf);
128
129  while (key.dptr != NULL)
130    {
131      data = gdbm_fetch (dbf, key);
132      if (data.dptr != NULL)
133 	{
134	  /* Add the data to the new file. */
135	  if (gdbm_store (new_dbf, key, data, GDBM_INSERT) != 0)
136	    {
137	      gdbm_close (new_dbf);
138	      gdbm_errno = GDBM_REORGANIZE_FAILED;
139	      unlink (new_name);
140	      free (new_name);
141	      return -1;
142	    }
143 	}
144      else
145 	{
146	  /* ERROR! Abort and don't finish reorganize. */
147	  gdbm_close (new_dbf);
148	  gdbm_errno = GDBM_REORGANIZE_FAILED;
149	  unlink (new_name);
150	  free (new_name);
151	  return -1;
152 	}
153      nextkey = gdbm_nextkey (dbf, key);
154      free (key.dptr);
155      free (data.dptr);
156      key = nextkey;
157    }
158
159  /* Write everything. */
160  _gdbm_end_update (new_dbf);
161  gdbm_sync (new_dbf);
162
163
164  /* Move the new file to old name. */
165
166  if (rename (new_name, dbf->name) != 0)
167    {
168      gdbm_errno = GDBM_REORGANIZE_FAILED;
169      gdbm_close (new_dbf);
170      free (new_name);
171      return -1;
172    }
173
174  /* Fix up DBF to have the correct information for the new file. */
175  if (dbf->file_locking)
176    {
177      UNLOCK_FILE(dbf);
178    }
179  close (dbf->desc);
180  free (dbf->header);
181  free (dbf->dir);
182
183  if (dbf->bucket_cache != NULL) {
184    for (index = 0; index < dbf->cache_size; index++) {
185      if (dbf->bucket_cache[index].ca_bucket != NULL)
186	free (dbf->bucket_cache[index].ca_bucket);
187      if (dbf->bucket_cache[index].ca_data.dptr != NULL)
188	free (dbf->bucket_cache[index].ca_data.dptr);
189    }
190    free (dbf->bucket_cache);
191  }
192
193  dbf->desc           = new_dbf->desc;
194  dbf->header         = new_dbf->header;
195  dbf->dir            = new_dbf->dir;
196  dbf->bucket         = new_dbf->bucket;
197  dbf->bucket_dir     = new_dbf->bucket_dir;
198  dbf->last_read      = new_dbf->last_read;
199  dbf->bucket_cache   = new_dbf->bucket_cache;
200  dbf->cache_size     = new_dbf->cache_size;
201  dbf->header_changed    = new_dbf->header_changed;
202  dbf->directory_changed = new_dbf->directory_changed;
203  dbf->bucket_changed    = new_dbf->bucket_changed;
204  dbf->second_changed    = new_dbf->second_changed;
205
206  free (new_dbf->name);
207  free (new_dbf);
208  free (new_name);
209
210  /* Make sure the new database is all on disk. */
211  fsync (dbf->desc);
212
213  /* Force the right stuff for a correct bucket cache. */
214  dbf->cache_entry    = &dbf->bucket_cache[0];
215  _gdbm_get_bucket (dbf, 0);
216
217  return 0;
218}
219