mirror of
https://github.com/mabam/afpfs-ng-mac.git
synced 2025-01-14 23:30:04 +00:00
ec7b7475ab
Set the volume_open timeout to 20 seconds to allow an idle time capsule disk to spin up.
260 lines
5.1 KiB
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;
|
|
}
|
|
|