/* Copyright (C) 2006 Alex deVries */ #include #include #include #include #include "afpfs-ng/afp.h" #include "afpfs-ng/afp_protocol.h" #undef DID_CACHE_DISABLE static unsigned short timeout=10; struct did_cache_entry { /* For the example /foo/bar/baz */ char dirname[AFP_MAX_PATH]; /* full name, eg. /foo/bar/ */ unsigned int did; /* eg 2323 */ struct timeval time; struct did_cache_entry * next; } ; int free_entire_did_cache(struct afp_volume * volume) { struct did_cache_entry * d, * p, *p2; pthread_mutex_lock(&volume->did_cache_mutex); p=volume->did_cache_base; for (d=volume->did_cache_base;d;d=p) { p2=p; p=d->next; free(p2); } pthread_mutex_unlock(&volume->did_cache_mutex); return 0; } int remove_did_entry(struct afp_volume * volume, const char * name) { struct did_cache_entry * d, * p=NULL; pthread_mutex_lock(&volume->did_cache_mutex); for (d=volume->did_cache_base;d;d=d->next) { if (strcmp(d->dirname,name)==0) { if (p) p->next=d->next; else volume->did_cache_base=d->next; volume->did_cache_stats.force_removed++; free(d); break; } else p=d; } pthread_mutex_unlock(&volume->did_cache_mutex); return 0; } static int add_did_cache_entry(struct afp_volume * volume, unsigned int new_did, char * path) { struct did_cache_entry * new, *old_base; #ifdef DID_CACHE_DISABLE return 0; #endif if ((new=malloc(sizeof(* new)))==NULL) return -1; memset(new,0,sizeof(*new)); new->did=new_did; memcpy(new->dirname,path,AFP_MAX_PATH); gettimeofday(&new->time,NULL); pthread_mutex_lock(&volume->did_cache_mutex); old_base=volume->did_cache_base; volume->did_cache_base=new; new->next=old_base; pthread_mutex_unlock(&volume->did_cache_mutex); return 0; } unsigned char is_dir(struct afp_volume * volume, unsigned int parentdid, const char * path) { int ret; unsigned int filebitmap=0; unsigned int dirbitmap=0; struct afp_file_info fi; #if 0 struct did_cache_entry * p; if ((p=find_did_cache_entry(volume,parentdid,path,strlen(path)))) return p->isdir; #endif ret =afp_getfiledirparms(volume,parentdid, filebitmap,dirbitmap,path,&fi); if (ret) return 0; return fi.isdir; } static unsigned int find_dirid_by_fullname(struct afp_volume * volume, char * path) { struct did_cache_entry * p, *prev=volume->did_cache_base; struct timeval time; unsigned int found_did=0; unsigned char breakearly=0; #ifdef DID_CACHE_DISABLE goto out; #endif gettimeofday(&time,NULL); pthread_mutex_lock(&volume->did_cache_mutex); for (p=volume->did_cache_base;p;p=p->next) { if (time.tv_sec > (p->time.tv_sec+timeout)) { volume->did_cache_stats.expired++; if (prev==volume->did_cache_base) { if (strcmp(p->dirname,path)==0) breakearly=1; volume->did_cache_base=p->next; free(p); if (breakearly) goto out; p=volume->did_cache_base; if (!p) goto out; prev=volume->did_cache_base; continue; } else { prev->next=p->next; free(p); p=prev; } } if (strcmp(p->dirname,path)==0) { found_did=p->did; volume->did_cache_stats.hits++; goto out; } prev=p; } out: pthread_mutex_unlock(&volume->did_cache_mutex); return found_did; } /* This calculates the dirid and basename. It *always* gets the parent did. */ int get_dirid(struct afp_volume * volume, const char * path, char * basename, unsigned int * dirid) { char * p, *p2; int ret; struct afp_file_info fi; unsigned int filebitmap,dirbitmap; unsigned int newdid; unsigned int parent_did; char copy[AFP_MAX_PATH]; if (((p=strrchr(path,'/')))==NULL) return -1; /* Calculate the basename, leave copy with just the parent */ if (basename) { memset(basename,0,AFP_MAX_PATH); memcpy(basename,p+1,strlen(path)-(p-path)-1); } /* p now points to the last '/' */ if (p-path==0) { *dirid=AFP_ROOT_DID; goto out; } memcpy(copy,path,p-path+1); if (copy[p-path]=='/') copy[p-path]='\0'; /* Lop off the last '/' */ /* See if the parent's fullname is in the cache */ if ((newdid=find_dirid_by_fullname(volume,copy))) { *dirid=newdid; goto out; } /* No? Work your way back to the start from the end looking for a parent */ while ((p=strrchr(copy,'/'))) { if (p==copy) { /* Okay, we're done since we're at the start*/ parent_did=AFP_ROOT_DID; break; } *p='\0'; if ((parent_did=find_dirid_by_fullname(volume,copy))) { break; } } /* Okay, now we have the topmost cached parent */ /* Move forward now from the last parentid */ filebitmap=kFPNodeIDBit ; dirbitmap=kFPNodeIDBit ; /* Go to the end of last known entry */ p=(char *)path+(p-copy); p2=p; while ((p=strchr(p+1,'/'))) { memset(copy,0,AFP_MAX_PATH); memcpy(copy,p2,p-p2); volume->did_cache_stats.misses++; ret =afp_getfiledirparms(volume,parent_did, filebitmap,dirbitmap,copy,&fi); if (fi.isdir) { /* Add it to the cache */ memset(copy,0,AFP_MAX_PATH); memcpy(copy,path,p-path); add_did_cache_entry(volume, fi.fileid,copy); } else { break; } parent_did=fi.fileid; p2=p; } *dirid=parent_did; out: return 0; }