]> arthur.barton.de Git - netatalk.git/blob - libatalk/acl/cache.c
Hide namecache and uuidcache
[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     int ret = 0;
50     cacheduser_t *entry;
51     char timestr[200];
52     struct tm *tmp = NULL;
53
54     for ( i=0 ; i<256; i++) {
55         if ((entry = namecache[i]) != NULL) {
56             do {
57                 tmp = localtime(&entry->creationtime);
58                 if (tmp == NULL)
59                     continue;
60                 if (strftime(timestr, 200, "%c", tmp) == 0)
61                     continue;
62                 LOG(log_debug, logtype_default,
63                     "namecache{%d}: name:%s, uuid:%s, type%s: %s, cached: %s",
64                     i,
65                     entry->name,
66                     uuid_bin2string(entry->uuid),
67                     (entry->type & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
68                     uuidtype[entry->type & UUIDTYPESTR_MASK],
69                     timestr);
70             } while ((entry = entry->next) != NULL);
71         }
72     }
73
74     for ( i=0; i<256; i++) {
75         if ((entry = uuidcache[i]) != NULL) {
76             do {
77
78                 tmp = localtime(&entry->creationtime);
79                 if (tmp == NULL)
80                     continue;
81                 if (strftime(timestr, 200, "%c", tmp) == 0)
82                     continue;
83                 LOG(log_debug, logtype_default,
84                     "uuidcache{%d}: uuid:%s, name:%s, type%s: %s, cached: %s",
85                     i,
86                     uuid_bin2string(entry->uuid),
87                     entry->name,
88                     (entry->type & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
89                     uuidtype[entry->type & UUIDTYPESTR_MASK],
90                     timestr);
91             } while ((entry = entry->next) != NULL);
92         }
93     }
94 }
95
96 /* hash string it into unsigned char */
97 static unsigned char hashstring(unsigned char *str) {
98     unsigned long hash = 5381;
99     unsigned char index;
100     int c;
101     while ((c = *str++) != 0)
102         hash = ((hash << 5) + hash) ^ c; /* (hash * 33) ^ c */
103
104     index = 85 ^ (hash & 0xff);
105     while ((hash = hash >> 8) != 0)
106         index ^= (hash & 0xff);
107
108     return index;
109 }
110
111 /* hash atalk_uuid_t into unsigned char */
112 static unsigned char hashuuid(uuidp_t uuid) {
113     unsigned char index = 83;
114     int i;
115
116     for (i=0; i<16; i++) {
117         index ^= uuid[i];
118         index += uuid[i];
119     }
120     return index;
121 }
122
123 /********************************************************
124  * Interface
125  ********************************************************/
126
127 int add_cachebyname( const char *inname, const uuidp_t inuuid, const uuidtype_t type, const unsigned long uid _U_) {
128     int ret = 0;
129     char *name = NULL;
130     unsigned char *uuid = NULL;
131     cacheduser_t *cacheduser = NULL;
132     unsigned char hash;
133
134     /* allocate mem and copy values */
135     name = malloc(strlen(inname)+1);
136     if (!name) {
137         LOG(log_error, logtype_default, "add_cachebyname: mallor error");
138         ret = -1;
139         goto cleanup;
140     }
141
142     uuid = malloc(UUID_BINSIZE);
143     if (!uuid) {
144         LOG(log_error, logtype_default, "add_cachebyname: mallor error");
145         ret = -1;
146         goto cleanup;
147     }
148
149     cacheduser = malloc(sizeof(cacheduser_t));
150     if (!cacheduser) {
151         LOG(log_error, logtype_default, "add_cachebyname: mallor error");
152         ret = -1;
153         goto cleanup;
154     }
155
156     strcpy(name, inname);
157     memcpy(uuid, inuuid, UUID_BINSIZE);
158
159     /* fill in the cacheduser */
160     cacheduser->name = name;
161     cacheduser->uuid = uuid;
162     cacheduser->type = type;
163     cacheduser->creationtime = time(NULL);
164     cacheduser->prev = NULL;
165     cacheduser->next = NULL;
166
167     /* get hash */
168     hash = hashstring((unsigned char *)name);
169
170     /* insert cache entry into cache array at head of queue */
171     if (namecache[hash] == NULL) {
172         /* this queue is empty */
173         namecache[hash] = cacheduser;
174     } else {
175         cacheduser->next = namecache[hash];
176         namecache[hash]->prev = cacheduser;
177         namecache[hash] = cacheduser;
178     }
179
180 cleanup:
181     if (ret != 0) {
182         if (name)
183             free(name);
184         if (uuid)
185             free(uuid);
186         if (cacheduser)
187             free(cacheduser);
188     }
189
190     return ret;
191 }
192
193 /*!
194  * Search cache by name and uuid type
195  *
196  * @args name     (r)  name to search
197  * @args type     (rw) type (user or group) of name, returns found type here which might
198  *                     mark it as a negative entry
199  * @args uuid     (w)  found uuid is returned here
200  * @returns       0 on sucess, entry found
201  *                -1 no entry found
202  */
203 int search_cachebyname( const char *name, uuidtype_t *type, unsigned char *uuid) {
204     int ret;
205     unsigned char hash;
206     cacheduser_t *entry;
207     time_t tim;
208
209     hash = hashstring((unsigned char *)name);
210
211     if (namecache[hash] == NULL)
212         return -1;
213
214     entry = namecache[hash];
215     while (entry) {
216         ret = strcmp(entry->name, name);
217         if (ret == 0 && *type == (entry->type & UUIDTYPESTR_MASK)) {
218             /* found, now check if expired */
219             tim = time(NULL);
220             if ((tim - entry->creationtime) > CACHESECONDS) {
221                 LOG(log_debug, logtype_default, "search_cachebyname: expired: name:\"%s\"", entry->name);
222                 /* remove item */
223                 if (entry->prev) {
224                     /* 2nd to last in queue */
225                     entry->prev->next = entry->next;
226                     if (entry->next)
227                         /* not the last element */
228                         entry->next->prev = entry->prev;
229                 } else  {
230                     /* queue head */
231                     if ((namecache[hash] = entry->next) != NULL)
232                         namecache[hash]->prev = NULL;
233                 }
234                 free(entry->name);
235                 free(entry->uuid);
236                 free(entry);
237                 return -1;
238             } else {
239                 memcpy(uuid, entry->uuid, UUID_BINSIZE);
240                 *type = entry->type;
241                 return 0;
242             }
243         }
244         entry = entry->next;
245     }
246
247     return -1;
248 }
249
250 /* 
251  * Caller must free allocated name
252  */
253 int search_cachebyuuid( uuidp_t uuidp, char **name, uuidtype_t *type) {
254     int ret;
255     unsigned char hash;
256     cacheduser_t *entry;
257     time_t tim;
258
259     hash = hashuuid(uuidp);
260
261     if (! uuidcache[hash])
262         return -1;
263
264     entry = uuidcache[hash];
265     while (entry) {
266         ret = memcmp(entry->uuid, uuidp, UUID_BINSIZE);
267         if (ret == 0) {
268             tim = time(NULL);
269             if ((tim - entry->creationtime) > CACHESECONDS) {
270                 LOG(log_debug, logtype_default, "search_cachebyuuid: expired: name:\'%s\' in queue {%d}", entry->name, hash);
271                 if (entry->prev) {
272                     /* 2nd to last in queue */
273                     entry->prev->next = entry->next;
274                     if (entry->next)
275                         /* not the last element */
276                         entry->next->prev = entry->prev;
277                 } else {
278                     /* queue head  */
279                     if ((uuidcache[hash] = entry->next) != NULL)
280                         uuidcache[hash]->prev = NULL;
281                 }
282                 free(entry->name);
283                 free(entry->uuid);
284                 free(entry);
285                 return -1;
286             } else {
287                 *name = malloc(strlen(entry->name)+1);
288                 strcpy(*name, entry->name);
289                 *type = entry->type;
290                 return 0;
291             }
292         }
293         entry = entry->next;
294     }
295
296     return -1;
297 }
298
299 int add_cachebyuuid( uuidp_t inuuid, const char *inname, uuidtype_t type, const unsigned long uid _U_) {
300     int ret = 0;
301     char *name = NULL;
302     unsigned char *uuid = NULL;
303     cacheduser_t *cacheduser = NULL;
304     cacheduser_t *entry;
305     unsigned char hash;
306
307     /* allocate mem and copy values */
308     name = malloc(strlen(inname)+1);
309     if (!name) {
310         LOG(log_error, logtype_default, "add_cachebyuuid: mallor error");
311         ret = -1;
312         goto cleanup;
313     }
314
315     uuid = malloc(UUID_BINSIZE);
316     if (!uuid) {
317         LOG(log_error, logtype_default, "add_cachebyuuid: mallor error");
318         ret = -1;
319         goto cleanup;
320     }
321
322     cacheduser = malloc(sizeof(cacheduser_t));
323     if (!cacheduser) {
324         LOG(log_error, logtype_default, "add_cachebyuuid: mallor error");
325         ret = -1;
326         goto cleanup;
327     }
328
329     strcpy(name, inname);
330     memcpy(uuid, inuuid, UUID_BINSIZE);
331
332     /* fill in the cacheduser */
333     cacheduser->name = name;
334     cacheduser->type = type;
335     cacheduser->uuid = uuid;
336     cacheduser->creationtime = time(NULL);
337     cacheduser->prev = NULL;
338     cacheduser->next = NULL;
339
340     /* get hash */
341     hash = hashuuid(uuid);
342
343     /* insert cache entry into cache array at head of queue */
344     if (uuidcache[hash] == NULL) {
345         /* this queue is empty */
346         uuidcache[hash] = cacheduser;
347     } else {
348         cacheduser->next = uuidcache[hash];
349         uuidcache[hash]->prev = cacheduser;
350         uuidcache[hash] = cacheduser;
351     }
352
353 cleanup:
354     if (ret != 0) {
355         if (name)
356             free(name);
357         if (uuid)
358             free(uuid);
359         if (cacheduser)
360             free(cacheduser);
361     }
362
363     return ret;
364 }