dd3b2c12c1a1555ce40fd2d92404d82f9c1150cc
[egate.git] / util / gw-db.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation, in whole or in part, for any purpose, is hereby granted,
13  * provided that:
14  *
15  * 1. This copyright and permission notice appear in all copies of the
16  * software and its documentation. Notices of copyright or attribution
17  * which appear at the beginning of any file must remain unchanged.
18  *
19  * 2. The names of EUROPAGATE or the project partners may not be used to
20  * endorse or promote products derived from this software without specific
21  * prior written permission.
22  *
23  * 3. Users of this software (implementors and gateway operators) agree to
24  * inform the EUROPAGATE consortium of their use of the software. This
25  * information will be used to evaluate the EUROPAGATE project and the
26  * software, and to plan further developments. The consortium may use
27  * the information in later publications.
28  * 
29  * 4. Users of this software agree to make their best efforts, when
30  * documenting their use of the software, to acknowledge the EUROPAGATE
31  * consortium, and the role played by the software in their work.
32  *
33  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36  * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39  * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40  * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41  * USE OR PERFORMANCE OF THIS SOFTWARE.
42  *
43  */
44 /* Gateway utility
45  * Europagate, 1995
46  *
47  * $Log: gw-db.c,v $
48  * Revision 1.4  1995/12/20 16:28:07  adam
49  * Extra parameter block to gw_db_open. If block is 0 gw_db_open
50  * returns NULL if lock couldn't be satisfied.
51  * Minor changes in iso2709.c.
52  *
53  * Revision 1.3  1995/05/16  09:40:53  adam
54  * LICENSE.
55  *
56  * Revision 1.2  1995/05/01  12:43:57  adam
57  * lgets function moved from kernel to util.
58  *
59  * Revision 1.1  1995/03/27  08:25:01  adam
60  * New module gip: Gateway IPc module.
61  * New module gw-db: Gateway hash-db module (user information table).
62  *
63  */
64
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <assert.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <fcntl.h>
71
72 #include <gw-log.h>
73 #include <gw-db.h>
74
75 #define FILE_HEAD_LEN 128
76 #define DB_HASH 1997
77
78 struct gw_db {
79     struct file_head {     /* file header info */
80         char magic[8];     /* start info */
81         int no_of_entries; /* no of entries in table */
82         int sequence_no;   /* number of next to come */
83     } head;
84     int *hash_array;       /* hash array pointers (DB_HASH in size) */
85     int fd;                /* descriptor of file */
86     int dirty;             /* any writes so far? */
87 };
88
89 struct db_bucket {         /* information about individual entries */
90     int  name_length;      /* length of name */
91     int  info_length;      /* length of information */
92     int  next;             /* next in hash chain - possibly 0 */
93     int  prev;             /* prev in hach chain - possibly 0 */
94 };
95
96 static char *mod = "gwdb";
97
98 /*
99  * write_head: write header of table
100  */
101 static int write_head (GW_DB db)
102 {
103     char file_head_buf[FILE_HEAD_LEN];
104     int r;
105
106     if (lseek (db->fd, 0L, SEEK_SET) == -1)
107         return -1;
108     memcpy (file_head_buf, &db->head, sizeof(db->head));
109     r = write (db->fd, file_head_buf, FILE_HEAD_LEN);
110     if (r == -1)
111         return -1;
112     r = write (db->fd, db->hash_array, DB_HASH * sizeof(*db->hash_array));
113     if (r == -1)
114         return -1;
115     return 0;
116 }
117
118 /*
119  * hash: calculate hash value
120  */
121 static unsigned hash (const char *name, int length)
122 {
123     unsigned l = 0;
124
125     while (--length >= 0)
126         l = l*65599 + *name++;
127     return l % DB_HASH;
128 }
129
130 /*
131  * lock_file: lock entire file 
132  */
133 static int lock_file (int fd, int block, int type)
134 {
135     struct flock area;
136     area.l_type = type;
137     area.l_whence = SEEK_SET;
138     area.l_start = 0L;
139     area.l_len = 0L;
140     return fcntl (fd, block ? F_SETLKW : F_SETLK, &area);
141 }
142
143
144 /*
145  * gw_db_lookup: table lookup
146  */
147 int gw_db_lookup (GW_DB db, const char *name, int name_length,
148                   void **buf, size_t *count)
149 {
150     struct db_bucket bucket;
151     unsigned l = hash (name, name_length);
152     char     *dbuf = NULL;
153     int      dsize = 0;
154     int      pos;
155     int      r;
156
157     for (pos = db->hash_array[l]; pos; pos = bucket.next)
158     {
159         int blen;
160
161         if (lseek (db->fd, pos, SEEK_SET) == -1)
162         {
163             free (dbuf);
164             return -1;
165         }
166         r = read (db->fd, &bucket, sizeof(bucket));
167         if (r == -1)
168         {
169             free (dbuf);
170             return -1;
171         }
172         if (r != sizeof(bucket))
173         {
174             free (dbuf);
175             return -2;
176         }
177         if (bucket.name_length <= 0 || bucket.info_length <= 0 ||
178             bucket.name_length >= 16384 || bucket.info_length >= 262144)
179         {
180             free (dbuf);
181             return -3;
182         }
183         if (bucket.name_length != name_length)
184             continue;
185         blen = bucket.name_length + bucket.info_length;
186         if (blen >= dsize)
187         {
188             dsize = blen + 1024;
189             free (dbuf);
190             if (!(dbuf = malloc (dsize)))
191                 return -1;
192         }
193         r = read (db->fd, dbuf, blen);
194         if (r == -1)
195         {
196             free (dbuf);
197             return -1;
198         }
199         else if (r < blen)
200         {
201             free (dbuf);
202             return -2;
203         }
204         if (memcmp (name, dbuf, name_length))
205             continue;
206
207         *count = bucket.info_length;
208         *buf = malloc (*count);
209         if (!*buf)
210         {
211             free (dbuf);
212             return -1;
213         }
214         memcpy (*buf, dbuf + name_length, *count);
215         free (dbuf);
216         return 1;
217     }
218     free (dbuf);
219     return 0;
220 }
221
222 /*
223  * gw_db_insert: table insertion
224  */
225 int gw_db_insert (GW_DB db, const char *name, int name_length,
226                   const void *buf, size_t count)
227 {
228     struct db_bucket n_bucket;
229     struct db_bucket bucket;
230     off_t    n_pos, r_pos;
231     unsigned l = hash (name, name_length);
232     int      pos = db->hash_array[l];
233     int      r;
234
235     db->dirty = 1;
236     (db->head.no_of_entries)++;
237     (db->head.sequence_no)++;
238     n_bucket.name_length = name_length;
239     n_bucket.info_length = count;
240     n_pos = lseek (db->fd, 0, SEEK_END);
241     if (n_pos == -1)
242         return -1;
243     n_bucket.next = pos;
244     db->hash_array[l] = n_pos;
245     n_bucket.prev = 0;
246
247     r = write (db->fd, &n_bucket, sizeof(n_bucket));
248     if (r == -1)
249         return -1;
250     else if (r < sizeof(n_bucket))
251         return -2;
252
253     r = write (db->fd, name, name_length);
254     if (r == -1)
255         return -1;
256     else if (r < name_length)
257         return -2;
258
259     r = write (db->fd, buf, count);
260     if (r == -1)
261         return -1;
262     else if (r < count)
263         return -2;
264
265     r_pos = lseek (db->fd, pos, SEEK_SET);
266     if (r_pos == -1)
267         return -1;
268     r = read (db->fd, &bucket, sizeof(bucket));
269     if (r == -1)
270         return -1;
271     else if (r < sizeof(bucket))
272         return -2;
273     bucket.prev = n_pos;
274
275     r_pos = lseek (db->fd, pos, SEEK_SET);
276     if (r_pos == -1)
277         return -1;
278     r = write (db->fd, &bucket, sizeof(bucket));
279     if (r == -1)
280         return -1;
281     else if (r < sizeof(bucket))
282         return -2;
283     return 0;
284 }
285
286 /*
287  * gw_db_free: release memory associated with table
288  */
289 static GW_DB gw_db_free (GW_DB db)
290 {
291     free (db->hash_array);
292     free (db);
293     return NULL;
294 }
295
296 /*
297  * gw_db_open: open and lock table for reading (and writing)
298  */
299 GW_DB gw_db_open (const char *fname, int write_flag, int block)
300 {
301     char file_head_buf[FILE_HEAD_LEN];
302     GW_DB db;
303     int r;
304
305     db = malloc (sizeof(*db));
306     if (!db)
307     {
308         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "malloc");
309         exit (1);
310     }
311     db->dirty = 0;
312     if (!(db->hash_array = malloc (DB_HASH * sizeof(*db->hash_array))))
313     {
314         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "malloc");
315         exit (1);
316     }
317     if (write_flag)
318     {
319         if ((db->fd = open (fname, O_RDWR|O_CREAT, 0666)) == -1)
320         {
321             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open %s", fname);
322             exit (1);
323         }
324     }
325     else
326     {
327         if ((db->fd = open (fname, O_RDONLY)) == -1)
328         {
329             gw_log (GW_LOG_WARN|GW_LOG_ERRNO," open %s", fname);
330             return gw_db_free (db);
331         }
332     }
333     if (block)
334         lock_file (db->fd, 1, write_flag ? F_WRLCK : F_RDLCK);
335     else
336     {
337         r = lock_file (db->fd, 0, write_flag ? F_WRLCK : F_RDLCK);
338         if (r == -1)
339         {
340             gw_log (GW_LOG_WARN|GW_LOG_ERRNO, "flock %s", fname);
341             close (db->fd);
342             return gw_db_free (db);
343         }
344     }
345     r = read (db->fd, file_head_buf, FILE_HEAD_LEN);
346     if (r == -1)
347         return gw_db_free (db);
348     if (r < FILE_HEAD_LEN)
349     {
350         int i;
351
352         if (!write_flag)
353             return gw_db_free (db);
354         db->head.no_of_entries = 0;
355         db->head.sequence_no = 1;
356         for (i=0; i<DB_HASH; i++)
357             db->hash_array[i] = 0;
358         if (write_head (db))
359             return gw_db_free (db);
360     }
361     else
362     {
363         memcpy (&db->head, file_head_buf, sizeof(db->head));
364         r = read (db->fd, db->hash_array, sizeof(*db->hash_array)*DB_HASH);
365         if (r < sizeof(*db->hash_array)*DB_HASH)
366             return gw_db_free (db);
367     }
368     return db;
369 }
370
371 /*
372  * gw_db_close: close table and flush
373  */
374 int gw_db_close (GW_DB db)
375 {
376     int r = 0;
377
378     if (db->dirty) 
379         r = write_head (db);
380     lock_file (db->fd, 1, F_UNLCK);
381     if (close (db->fd) == -1)
382     {
383         gw_db_free (db);
384         return -1;
385     }
386     gw_db_free (db);
387     return r;
388 }
389
390 /*
391  * gw_db_no_ent: return number of entries in table
392  */
393 int gw_db_no_ent (GW_DB db)
394 {
395     return db->head.no_of_entries;
396 }
397
398 /*
399  * gw_db_seq_no: return sequence number
400  */
401 int gw_db_seq_no (GW_DB db)
402 {
403     return db->head.sequence_no;
404 }
405