]> arthur.barton.de Git - netatalk.git/blob - libatalk/acl/cache.c
Merge branch 'v3-cleanup' into tmp/v3.0.4-alex
[netatalk.git] / libatalk / acl / cache.c
1 /*
2   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <time.h>
23 #include <errno.h>
24
25 #include <atalk/logger.h>
26 #include <atalk/afp.h>
27 #include <atalk/uuid.h>
28 #include "cache.h"
29
30 typedef struct cacheduser {
31     unsigned long uid;      /* for future use */
32     uuidtype_t type;
33     unsigned char *uuid;
34     char *name;
35     time_t creationtime;
36     struct cacheduser *prev;
37     struct cacheduser *next;
38 } cacheduser_t;
39
40 static cacheduser_t *namecache[256];   /* indexed by hash of name */
41 static cacheduser_t *uuidcache[256];   /* indexed by hash of uuid */
42
43 /********************************************************
44  * helper function
45  ********************************************************/
46
47 void uuidcache_dump(void) {
48     int i;
49     cacheduser_t *entry;
50     char timestr[200];
51     struct tm *tmp = NULL;
52
53     for ( i=0 ; i<256; i++) {
54         if ((entry = namecache[i]) != NULL) {
55             do {
56                 tmp = localtime(&entry->creationtime);
57                 if (tmp == NULL)
58                     continue;
59                 if (strftime(timestr, 200, "%c", tmp) == 0)
60                     continue;
61                 LOG(log_debug, logtype_default,
62                     "namecache{%d}: name:%s, uuid:%s, type%s: %s, cached: %s",
63                     i,
64                     entry->name,
65                     uuid_bin2string(entry->uuid),
66                     (entry->type & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
67                     uuidtype[entry->type & UUIDTYPESTR_MASK],
68                     timestr);
69             } while ((entry = entry->next) != NULL);
70         }
71     }
72
73     for ( i=0; i<256; i++) {
74         if ((entry = uuidcache[i]) != NULL) {
75             do {
76
77                 tmp = localtime(&entry->creationtime);
78                 if (tmp == NULL)
79                     continue;
80                 if (strftime(timestr, 200, "%c", tmp) == 0)
81                     continue;
82                 LOG(log_debug, logtype_default,
83                     "uuidcache{%d}: uuid:%s, name:%s, type%s: %s, cached: %s",
84                     i,
85                     uuid_bin2string(entry->uuid),
86                     entry->name,
87                     (entry->type & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
88                     uuidtype[entry->type & UUIDTYPESTR_MASK],
89                     timestr);
90             } while ((entry = entry->next) != NULL);
91         }
92     }
93 }
94
95 /* hash string it into unsigned char */
96 static unsigned char hashstring(unsigned char *str) {
97     unsigned long hash = 5381;
98     unsigned char index;
99     int c;
100     while ((c = *str++) != 0)
101         hash = ((hash << 5) + hash) ^ c; /* (hash * 33) ^ c */
102
103     index = 85 ^ (hash & 0xff);
104     while ((hash = hash >> 8) != 0)
105         index ^= (hash & 0xff);
106
107     return index;
108 }
109
110 /* hash atalk_uuid_t into unsigned char */
111 static unsigned char hashuuid(uuidp_t uuid) {
112     unsigned char index = 83;
113     int i;
114
115     for (i=0; i<16; i++) {
116         index ^= uuid[i];
117         index += uuid[i];
118     }
119     return index;
120 }
121
122 /********************************************************
123  * Interface
124  ********************************************************/
125
126 int add_cachebyname( const char *inname, const uuidp_t inuuid, const uuidtype_t type, const unsigned long uid _U_) {
127     int ret = 0;
128     char *name = NULL;
129     unsigned char *uuid = NULL;
130     cacheduser_t *cacheduser = NULL;
131     unsigned char hash;
132
133     /* allocate mem and copy values */
134     name = malloc(strlen(inname)+1);
135     if (!name) {
136         LOG(log_error, logtype_default, "add_cachebyname: mallor error");
137         ret = -1;
138         goto cleanup;
139     }
140
141     uuid = malloc(UUID_BINSIZE);
142     if (!uuid) {
143         LOG(log_error, logtype_default, "add_cachebyname: mallor error");
144         ret = -1;
145         goto cleanup;
146     }
147
148     cacheduser = malloc(sizeof(cacheduser_t));
149     if (!cacheduser) {
150         LOG(log_error, logtype_default, "add_cachebyname: mallor error");
151         ret = -1;
152         goto cleanup;
153     }
154
155     strcpy(name, inname);
156     memcpy(uuid, inuuid, UUID_BINSIZE);
157
158     /* fill in the cacheduser */
159     cacheduser->name = name;
160     cacheduser->uuid = uuid;
161     cacheduser->type = type;
162     cacheduser->creationtime = time(NULL);
163     cacheduser->prev = NULL;
164     cacheduser->next = NULL;
165
166     /* get hash */
167     hash = hashstring((unsigned char *)name);
168
169     /* insert cache entry into cache array at head of queue */
170     if (namecache[hash] == NULL) {
171         /* this queue is empty */
172         namecache[hash] = cacheduser;
173     } else {
174         cacheduser->next = namecache[hash];
175         namecache[hash]->prev = cacheduser;
176         namecache[hash] = cacheduser;
177     }
178
179 cleanup:
180     if (ret != 0) {
181         if (name)
182             free(name);
183         if (uuid)
184             free(uuid);
185         if (cacheduser)
186             free(cacheduser);
187     }
188
189     return ret;
190 }
191
192 /*!
193  * Search cache by name and uuid type
194  *
195  * @args name     (r)  name to search
196  * @args type     (rw) type (user or group) of name, returns found type here which might
197  *                     mark it as a negative entry
198  * @args uuid     (w)  found uuid is returned here
199  * @returns       0 on sucess, entry found
200  *                -1 no entry found
201  */
202 int search_cachebyname( const char *name, uuidtype_t *type, unsigned char *uuid) {
203     int ret;
204     unsigned char hash;
205     cacheduser_t *entry;
206     time_t tim;
207
208     hash = hashstring((unsigned char *)name);
209
210     if (namecache[hash] == NULL)
211         return -1;
212
213     entry = namecache[hash];
214     while (entry) {
215         ret = strcmp(entry->name, name);
216         if (ret == 0 && *type == (entry->type & UUIDTYPESTR_MASK)) {
217             /* found, now check if expired */
218             tim = time(NULL);
219             if ((tim - entry->creationtime) > CACHESECONDS) {
220                 LOG(log_debug, logtype_default, "search_cachebyname: expired: name:\"%s\"", entry->name);
221                 /* remove item */
222                 if (entry->prev) {
223                     /* 2nd to last in queue */
224                     entry->prev->next = entry->next;
225                     if (entry->next)
226                         /* not the last element */
227                         entry->next->prev = entry->prev;
228                 } else  {
229                     /* queue head */
230                     if ((namecache[hash] = entry->next) != NULL)
231                         namecache[hash]->prev = NULL;
232                 }
233                 free(entry->name);
234                 free(entry->uuid);
235                 free(entry);
236                 return -1;
237             } else {
238                 memcpy(uuid, entry->uuid, UUID_BINSIZE);
239                 *type = entry->type;
240                 return 0;
241             }
242         }
243         entry = entry->next;
244     }
245
246     return -1;
247 }
248
249 /* 
250  * Caller must free allocated name
251  */
252 int search_cachebyuuid( uuidp_t uuidp, char **name, uuidtype_t *type) {
253     int ret;
254     unsigned char hash;
255     cacheduser_t *entry;
256     time_t tim;
257
258     hash = hashuuid(uuidp);
259
260     if (! uuidcache[hash])
261         return -1;
262
263     entry = uuidcache[hash];
264     while (entry) {
265         ret = memcmp(entry->uuid, uuidp, UUID_BINSIZE);
266         if (ret == 0) {
267             tim = time(NULL);
268             if ((tim - entry->creationtime) > CACHESECONDS) {
269                 LOG(log_debug, logtype_default, "search_cachebyuuid: expired: name:\'%s\' in queue {%d}", entry->name, hash);
270                 if (entry->prev) {
271                     /* 2nd to last in queue */
272                     entry->prev->next = entry->next;
273                     if (entry->next)
274                         /* not the last element */
275                         entry->next->prev = entry->prev;
276                 } else {
277                     /* queue head  */
278                     if ((uuidcache[hash] = entry->next) != NULL)
279                         uuidcache[hash]->prev = NULL;
280                 }
281                 free(entry->name);
282                 free(entry->uuid);
283                 free(entry);
284                 return -1;
285             } else {
286                 *name = malloc(strlen(entry->name)+1);
287                 strcpy(*name, entry->name);
288                 *type = entry->type;
289                 return 0;
290             }
291         }
292         entry = entry->next;
293     }
294
295     return -1;
296 }
297
298 int add_cachebyuuid( uuidp_t inuuid, const char *inname, uuidtype_t type, const unsigned long uid _U_) {
299     int ret = 0;
300     char *name = NULL;
301     unsigned char *uuid = NULL;
302     cacheduser_t *cacheduser = NULL;
303     unsigned char hash;
304
305     /* allocate mem and copy values */
306     name = malloc(strlen(inname)+1);
307     if (!name) {
308         LOG(log_error, logtype_default, "add_cachebyuuid: mallor error");
309         ret = -1;
310         goto cleanup;
311     }
312
313     uuid = malloc(UUID_BINSIZE);
314     if (!uuid) {
315         LOG(log_error, logtype_default, "add_cachebyuuid: mallor error");
316         ret = -1;
317         goto cleanup;
318     }
319
320     cacheduser = malloc(sizeof(cacheduser_t));
321     if (!cacheduser) {
322         LOG(log_error, logtype_default, "add_cachebyuuid: mallor error");
323         ret = -1;
324         goto cleanup;
325     }
326
327     strcpy(name, inname);
328     memcpy(uuid, inuuid, UUID_BINSIZE);
329
330     /* fill in the cacheduser */
331     cacheduser->name = name;
332     cacheduser->type = type;
333     cacheduser->uuid = uuid;
334     cacheduser->creationtime = time(NULL);
335     cacheduser->prev = NULL;
336     cacheduser->next = NULL;
337
338     /* get hash */
339     hash = hashuuid(uuid);
340
341     /* insert cache entry into cache array at head of queue */
342     if (uuidcache[hash] == NULL) {
343         /* this queue is empty */
344         uuidcache[hash] = cacheduser;
345     } else {
346         cacheduser->next = uuidcache[hash];
347         uuidcache[hash]->prev = cacheduser;
348         uuidcache[hash] = cacheduser;
349     }
350
351 cleanup:
352     if (ret != 0) {
353         if (name)
354             free(name);
355         if (uuid)
356             free(uuid);
357         if (cacheduser)
358             free(cacheduser);
359     }
360
361     return ret;
362 }