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