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