Imported Upstream version 2.8.4
[platform/upstream/man-db.git] / libdb / db_btree.c
1 /*
2  * db_btree.c: low level btree interface routines for man.
3  *
4  * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.)
5  * Copyright (C) 2002 Colin Watson.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  * Mon Aug  8 20:35:30 BST 1994  Wilf. (G.Wilford@ee.surrey.ac.uk)
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif /* HAVE_CONFIG_H */
27
28 /* below this line are routines only useful for the BTREE interface */
29 #ifdef BTREE
30
31 #include <stdio.h>
32 #include <errno.h>
33 #include <string.h>
34
35 #include <sys/file.h> /* for flock() */
36 #include <sys/types.h> /* for open() */
37 #include <sys/stat.h>
38
39 #if HAVE_FCNTL_H
40 #  include <fcntl.h>
41 #endif
42
43 #include <unistd.h>
44
45 #include "stat-time.h"
46 #include "timespec.h"
47
48 #include "manconfig.h"
49
50 #include "error.h"
51 #include "hashtable.h"
52
53 #include "mydbm.h"
54 #include "db_storage.h"
55
56 struct hashtable *loop_check_hash;
57
58 /* the Berkeley database libraries do nothing to arbitrate between concurrent
59    database accesses, so we do a simple flock(). If the db is opened in
60    anything but O_RDONLY mode, an exclusive lock is enabled. Otherwise, the
61    lock is shared. A file cannot have both locks at once, and the non
62    blocking method is used ": Try again". This adopts GNU dbm's approach. */
63
64 /* release the lock and close the database */
65 int btree_close (DB *db)
66 {
67         (void) flock ((db->fd) (db), LOCK_UN);
68         return (db->close) (db);
69 }
70
71 /* open a btree type database, with file locking. */
72 DB *btree_flopen (char *filename, int flags, int mode)
73 {
74         DB *db;
75         BTREEINFO b;
76         int lock_op;
77         int lock_failed;
78
79         b.flags = 0;            /* do not allow any duplicate keys */
80
81         b.cachesize = 0;        /* default size */
82         b.maxkeypage = 0;       /* default */
83         b.minkeypage = 0;       /* default */
84         b.psize = 0;            /* default page size (2048?) */
85         b.compare = NULL;       /* builtin compare() function */
86         b.prefix = NULL;        /* builtin function */
87         b.lorder = 0;           /* byte order of host */
88
89         if (flags & ~O_RDONLY) {
90                 /* flags includes O_RDWR or O_WRONLY, need an exclusive lock */
91                 lock_op = LOCK_EX | LOCK_NB;
92         } else {
93                 lock_op = LOCK_SH | LOCK_NB;
94         }
95
96         if (!(flags & O_CREAT)) {
97                 /* Berkeley DB thinks that a zero-length file means that
98                  * somebody else is writing it, and sleeps for a few
99                  * seconds to give the writer a chance. All very well, but
100                  * the common case is that the database is just zero-length
101                  * because mandb was interrupted or ran out of disk space or
102                  * something like that - so we check for this case by hand
103                  * and ignore the database if it's zero-length.
104                  */
105                 struct stat iszero;
106                 if (stat (filename, &iszero) < 0)
107                         return NULL;
108                 if (iszero.st_size == 0) {
109                         errno = EINVAL;
110                         return NULL;
111                 }
112         }
113
114         if (flags & O_TRUNC) {
115                 /* opening the db is destructive, need to lock first */
116                 int fd;
117
118                 db = NULL;
119                 lock_failed = 1;
120                 fd = open (filename, flags & ~O_TRUNC, mode);
121                 if (fd != -1) {
122                         if (!(lock_failed = flock (fd, lock_op)))
123                                 db = dbopen (filename, flags, mode,
124                                              DB_BTREE, &b);
125                         close (fd);
126                 }
127         } else {
128                 db = dbopen (filename, flags, mode, DB_BTREE, &b);
129                 if (db)
130                         lock_failed = flock ((db->fd) (db), lock_op);
131         }
132
133         if (!db)
134                 return NULL;
135
136         if (lock_failed) {
137                 gripe_lock (filename);
138                 btree_close (db);
139                 return NULL;
140         }
141
142         return db;
143 }
144
145 /* do a replace when we have the duplicate flag set on the database -
146    we must do a del and insert, as a direct insert will not wipe out the
147    old entry */
148 int btree_replace (DB *db, datum key, datum cont)
149 {
150         return (db->put) (db, (DBT *) &key, (DBT *) &cont, 0);
151 }
152
153 int btree_insert (DB *db, datum key, datum cont)
154 {
155         return (db->put) (db, (DBT *) &key, (DBT *) &cont, R_NOOVERWRITE);
156 }
157
158 /* generic fetch routine for the btree database */
159 datum btree_fetch (DB *db, datum key)
160 {
161         datum data;
162
163         memset (&data, 0, sizeof data);
164
165         if ((db->get) (db, (DBT *) &key, (DBT *) &data, 0)) {
166                 memset (&data, 0, sizeof data);
167                 return data;
168         }
169
170         return copy_datum (data);
171 }
172
173 /* return 1 if the key exists, 0 otherwise */
174 int btree_exists (DB *db, datum key)
175 {
176         datum data;
177         return ((db->get) (db, (DBT *) &key, (DBT *) &data, 0) ? 0 : 1);
178 }
179
180 /* initiate a sequential access */
181 static datum btree_findkey (DB *db, u_int flags)
182 {
183         datum key, data;
184
185         memset (&key, 0, sizeof key);
186         memset (&data, 0, sizeof data);
187
188         if (flags == R_FIRST) {
189                 if (loop_check_hash) {
190                         hashtable_free (loop_check_hash);
191                         loop_check_hash = NULL;
192                 }
193         }
194         if (!loop_check_hash)
195                 loop_check_hash = hashtable_create (&free);
196
197         if (((db->seq) (db, (DBT *) &key, (DBT *) &data, flags))) {
198                 memset (&key, 0, sizeof key);
199                 return key;
200         }
201
202         if (hashtable_lookup (loop_check_hash,
203                               MYDBM_DPTR (key), MYDBM_DSIZE (key))) {
204                 /* We've seen this key already, which is broken. Return NULL
205                  * so the caller doesn't go round in circles.
206                  */
207                 debug ("Corrupt database! Already seen %*s. "
208                        "Attempting to recover ...\n",
209                        (int) MYDBM_DSIZE (key), MYDBM_DPTR (key));
210                 memset (&key, 0, sizeof key);
211                 return key;
212         }
213
214         hashtable_install (loop_check_hash,
215                            MYDBM_DPTR (key), MYDBM_DSIZE (key), NULL);
216
217         return copy_datum (key);
218 }
219
220 /* return the first key in the db */
221 datum btree_firstkey (DB *db)
222 {
223         return btree_findkey (db, R_FIRST);
224 }
225
226 /* return the next key in the db. NB. This routine only works if the cursor
227    has been previously set by btree_firstkey() since it was last opened. So
228    if we close/reopen a db mid search, we have to manually set up the
229    cursor again. */
230 datum btree_nextkey (DB *db)
231 {
232         return btree_findkey (db, R_NEXT);
233 }
234
235 /* compound nextkey routine, initialising key and content */
236 int btree_nextkeydata (DB *db, datum *key, datum *cont)
237 {
238         int status;
239
240         if ((status = (db->seq) (db, (DBT *) key, (DBT *) cont, R_NEXT)) != 0)
241                 return status;
242
243         *key = copy_datum (*key);
244         *cont = copy_datum (*cont);
245
246         return 0;
247 }
248
249 struct timespec btree_get_time (DB *db)
250 {
251         struct stat st;
252
253         if (fstat ((db->fd) (db), &st) < 0) {
254                 struct timespec t;
255                 t.tv_sec = -1;
256                 t.tv_nsec = -1;
257                 return t;
258         }
259         return get_stat_mtime (&st);
260 }
261
262 void btree_set_time (DB *db, const struct timespec time)
263 {
264         struct timespec times[2];
265
266         times[0] = time;
267         times[1] = time;
268         futimens ((db->fd) (db), times);
269 }
270
271 #endif /* BTREE */