afpfs-ng-mac/lib/did.c

260 lines
5.1 KiB
C

/*
Copyright (C) 2006 Alex deVries <alexthepuffin@gmail.com>
*/
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#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;
}