LICENSE.
[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.3  1995/05/16 09:40:53  adam
49  * LICENSE.
50  *
51  * Revision 1.2  1995/05/01  12:43:57  adam
52  * lgets function moved from kernel to util.
53  *
54  * Revision 1.1  1995/03/27  08:25:01  adam
55  * New module gip: Gateway IPc module.
56  * New module gw-db: Gateway hash-db module (user information table).
57  *
58  */
59
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <assert.h>
63 #include <string.h>
64 #include <unistd.h>
65 #include <fcntl.h>
66
67 #include <gw-db.h>
68
69 #define FILE_HEAD_LEN 128
70 #define DB_HASH 1997
71
72 struct gw_db {
73     struct file_head {     /* file header info */
74         char magic[8];     /* start info */
75         int no_of_entries; /* no of entries in table */
76         int sequence_no;   /* number of next to come */
77     } head;
78     int *hash_array;       /* hash array pointers (DB_HASH in size) */
79     int fd;                /* descriptor of file */
80     int dirty;             /* any writes so far? */
81 };
82
83 struct db_bucket {         /* information about individual entries */
84     int  name_length;      /* length of name */
85     int  info_length;      /* length of information */
86     int  next;             /* next in hash chain - possibly 0 */
87     int  prev;             /* prev in hach chain - possibly 0 */
88 };
89
90 /*
91  * write_head: write header of table
92  */
93 static int write_head (GW_DB db)
94 {
95     char file_head_buf[FILE_HEAD_LEN];
96     int r;
97
98     if (lseek (db->fd, 0L, SEEK_SET) == -1)
99         return -1;
100     memcpy (file_head_buf, &db->head, sizeof(db->head));
101     r = write (db->fd, file_head_buf, FILE_HEAD_LEN);
102     if (r == -1)
103         return -1;
104     r = write (db->fd, db->hash_array, DB_HASH * sizeof(*db->hash_array));
105     if (r == -1)
106         return -1;
107     return 0;
108 }
109
110 /*
111  * hash: calculate hash value
112  */
113 static unsigned hash (const char *name, int length)
114 {
115     unsigned l = 0;
116
117     while (--length >= 0)
118         l = l*65599 + *name++;
119     return l % DB_HASH;
120 }
121
122 /*
123  * lock_file: lock entire file 
124  */
125 static void lock_file (int fd, int type)
126 {
127     struct flock area;
128     area.l_type = type;
129     area.l_whence = SEEK_SET;
130     area.l_start = 0L;
131     area.l_len = 0L;
132     fcntl (fd, F_SETLKW, &area);
133 }
134
135 /*
136  * gw_db_lookup: table lookup
137  */
138 int gw_db_lookup (GW_DB db, const char *name, int name_length,
139                   void **buf, size_t *count)
140 {
141     struct db_bucket bucket;
142     unsigned l = hash (name, name_length);
143     char     *dbuf = NULL;
144     int      dsize = 0;
145     int      pos;
146     int      r;
147
148     for (pos = db->hash_array[l]; pos; pos = bucket.next)
149     {
150         int blen;
151
152         if (lseek (db->fd, pos, SEEK_SET) == -1)
153         {
154             free (dbuf);
155             return -1;
156         }
157         r = read (db->fd, &bucket, sizeof(bucket));
158         if (r == -1)
159         {
160             free (dbuf);
161             return -1;
162         }
163         if (r != sizeof(bucket))
164         {
165             free (dbuf);
166             return -2;
167         }
168         if (bucket.name_length <= 0 || bucket.info_length <= 0 ||
169             bucket.name_length >= 16384 || bucket.info_length >= 262144)
170         {
171             free (dbuf);
172             return -3;
173         }
174         if (bucket.name_length != name_length)
175             continue;
176         blen = bucket.name_length + bucket.info_length;
177         if (blen >= dsize)
178         {
179             dsize = blen + 1024;
180             free (dbuf);
181             if (!(dbuf = malloc (dsize)))
182                 return -1;
183         }
184         r = read (db->fd, dbuf, blen);
185         if (r == -1)
186         {
187             free (dbuf);
188             return -1;
189         }
190         else if (r < blen)
191         {
192             free (dbuf);
193             return -2;
194         }
195         if (memcmp (name, dbuf, name_length))
196             continue;
197
198         *count = bucket.info_length;
199         *buf = malloc (*count);
200         if (!*buf)
201         {
202             free (dbuf);
203             return -1;
204         }
205         memcpy (*buf, dbuf + name_length, *count);
206         free (dbuf);
207         return 1;
208     }
209     free (dbuf);
210     return 0;
211 }
212
213 /*
214  * gw_db_insert: table insertion
215  */
216 int gw_db_insert (GW_DB db, const char *name, int name_length,
217                   const void *buf, size_t count)
218 {
219     struct db_bucket n_bucket;
220     struct db_bucket bucket;
221     off_t    n_pos, r_pos;
222     unsigned l = hash (name, name_length);
223     int      pos = db->hash_array[l];
224     int      r;
225
226     db->dirty = 1;
227     (db->head.no_of_entries)++;
228     (db->head.sequence_no)++;
229     n_bucket.name_length = name_length;
230     n_bucket.info_length = count;
231     n_pos = lseek (db->fd, 0, SEEK_END);
232     if (n_pos == -1)
233         return -1;
234     n_bucket.next = pos;
235     db->hash_array[l] = n_pos;
236     n_bucket.prev = 0;
237
238     r = write (db->fd, &n_bucket, sizeof(n_bucket));
239     if (r == -1)
240         return -1;
241     else if (r < sizeof(n_bucket))
242         return -2;
243
244     r = write (db->fd, name, name_length);
245     if (r == -1)
246         return -1;
247     else if (r < name_length)
248         return -2;
249
250     r = write (db->fd, buf, count);
251     if (r == -1)
252         return -1;
253     else if (r < count)
254         return -2;
255
256     r_pos = lseek (db->fd, pos, SEEK_SET);
257     if (r_pos == -1)
258         return -1;
259     r = read (db->fd, &bucket, sizeof(bucket));
260     if (r == -1)
261         return -1;
262     else if (r < sizeof(bucket))
263         return -2;
264     bucket.prev = n_pos;
265
266     r_pos = lseek (db->fd, pos, SEEK_SET);
267     if (r_pos == -1)
268         return -1;
269     r = write (db->fd, &bucket, sizeof(bucket));
270     if (r == -1)
271         return -1;
272     else if (r < sizeof(bucket))
273         return -2;
274     return 0;
275 }
276
277 /*
278  * gw_db_free: release memory associated with table
279  */
280 static GW_DB gw_db_free (GW_DB db)
281 {
282     free (db->hash_array);
283     free (db);
284     return NULL;
285 }
286
287 /*
288  * gw_db_open: open and lock table for reading (and writing)
289  */
290 GW_DB gw_db_open (const char *fname, int write_flag)
291 {
292     char file_head_buf[FILE_HEAD_LEN];
293     GW_DB db;
294     int r;
295
296     if (!(db = malloc (sizeof(*db))))
297         return NULL;
298     db->dirty = 0;
299     if (!(db->hash_array = malloc (DB_HASH * sizeof(*db->hash_array))))
300         return gw_db_free (db);
301     if (write_flag)
302         db->fd = open (fname, O_RDWR|O_CREAT, 0666);
303     else
304         db->fd = open (fname, O_RDONLY);
305     if (db->fd == -1)
306         return gw_db_free (db);
307     lock_file (db->fd, write_flag ? F_WRLCK : F_RDLCK);
308     r = read (db->fd, file_head_buf, FILE_HEAD_LEN);
309     if (r == -1)
310         return gw_db_free (db);
311     if (r < FILE_HEAD_LEN)
312     {
313         int i;
314
315         if (!write_flag)
316             return gw_db_free (db);
317         db->head.no_of_entries = 0;
318         db->head.sequence_no = 1;
319         for (i=0; i<DB_HASH; i++)
320             db->hash_array[i] = 0;
321         if (write_head (db))
322             return gw_db_free (db);
323     }
324     else
325     {
326         memcpy (&db->head, file_head_buf, sizeof(db->head));
327         r = read (db->fd, db->hash_array, sizeof(*db->hash_array)*DB_HASH);
328         if (r < sizeof(*db->hash_array)*DB_HASH)
329             return gw_db_free (db);
330     }
331     return db;
332 }
333
334 /*
335  * gw_db_close: close table and flush
336  */
337 int gw_db_close (GW_DB db)
338 {
339     int r = 0;
340
341     if (db->dirty) 
342         r = write_head (db);
343     lock_file (db->fd, F_UNLCK);
344     if (close (db->fd) == -1)
345     {
346         gw_db_free (db);
347         return -1;
348     }
349     gw_db_free (db);
350     return r;
351 }
352
353 /*
354  * gw_db_no_ent: return number of entries in table
355  */
356 int gw_db_no_ent (GW_DB db)
357 {
358     return db->head.no_of_entries;
359 }
360
361 /*
362  * gw_db_seq_no: return sequence number
363  */
364 int gw_db_seq_no (GW_DB db)
365 {
366     return db->head.sequence_no;
367 }
368