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 30typedef struct cacheduser { 31 unsigned long uid; /* for future use */ 32 uuidtype_t type; 33 uuidp_t uuid; 34 char *name; 35 time_t creationtime; 36 struct cacheduser *prev; 37 struct cacheduser *next; 38} cacheduser_t; 39 40cacheduser_t *namecache[256]; /* indexed by hash of name */ 41cacheduser_t *uuidcache[256]; /* indexed by hash of uuid */ 42 43/******************************************************** 44 * helper function 45 ********************************************************/ 46 47void 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 */ 97static 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 */ 112static 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 127int 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 uuidp_t 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 180cleanup: 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 */ 203int search_cachebyname(const char *name, uuidtype_t *type, uuidp_t 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 */ 253int 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 299int 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 uuidp_t 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 353cleanup: 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} 365