#import "NSLVnode.h"
#import "Controller.h"
#import "AMString.h"
#import "AMMap.h"
#import "automount.h"
#import "log.h"
#import <syslog.h>
#import <string.h>
#import <stdlib.h>
#import <stdio.h>
#import <netdb.h>
#import "nfs_prot.h"
#import "Server.h"
#import <sys/types.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <netdb.h>
#import <unistd.h>
#import <stdlib.h>
#import <string.h>
#import <sys/socket.h>
#import <net/if.h>
#import <sys/ioctl.h>
#import <rpc/types.h>
#import <rpc/xdr.h>
#import <rpc/auth.h>
#import <rpc/clnt.h>
#import <rpc/svc.h>
#import <errno.h>
#import "nfs_prot.h"
#import "automount.h"
#import "log.h"
#import "mount.h"
#import "AMString.h"
#import "NSLUtil.h"
#import <CoreServices/CoreServices.h>
/* Re-enumerate folders at least once an hour: */
#define NSL_REFRESH_RATE (10*60*60)
#define NSL_ERROR_PERSISTENCE_DURATION 10
#define DONTINVALIDATEINTERMEDIATECONTAINERS 1
extern BOOL doServerMounts;
static String *generateLinkTarget(Vnode *v);
static void setupLink(Vnode *v);
static char gLocalHostName[] = "localhost";
@implementation NSLVnode
- (NSLVnode *)init
{
[super init];
generation = 0;
NSLObjectType = kNetworkObjectTypeNone;
fixedEntry = NO;
havePopulated = NO;
beingPopulated = NO;
mountInProgress = NO;
currentContentGeneration = 0;
return self;
}
- (NSLVnode *)newNeighborhoodWithName:(String *)neighborhoodNameString neighborhood:(NSLNeighborhood)neighborhood
{
NSLVnode *v;
v = [NSLVnode alloc];
if (v == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.newNeighborhoodWithName: Failed to allocate NSLVnode; aborting.");
goto Error_Exit;
};
[v init];
[v setName:neighborhoodNameString];
[v setNSLObject:neighborhood type:kNetworkNeighborhood];
[v setMap:map];
[controller registerVnode:v];
[self addChild:v];
return v;
Error_Exit:
if (v) [v release];
return nil;
}
- (NSLVnode *)newServiceWithName:(String *)newServiceName service:(NSLService)service serviceURL:(char *)serverURL
{
NSLVnode *v;
Server *newserver = nil;
String *serversrc = nil;
String *serviceURL = nil;
v = [NSLVnode alloc];
if (v == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.newServerWithName: Failed to allocate NSLVnode; aborting.");
goto Error_Exit;
};
[v init];
if (serverURL) {
serviceURL = [String uniqueString:serverURL];
if (serviceURL == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.initAsServerWithName: Failed to allocate serviceURL; aborting.");
goto Error_Exit;
};
};
newserver = [controller serverWithName:newServiceName];
if (newserver == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.initAsServerWithName: Failed to allocate controller; aborting.");
goto Error_Exit;
};
if ([newserver isLocalHost])
{
serversrc = [String uniqueString:"/"];
[v setMounted:YES];
[v setFakeMount:YES];
} else {
serversrc = [String uniqueString:"*"];
};
if (serversrc == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.initAsServerWithName: Failed to allocate serversrc; aborting.");
goto Error_Exit;
};
[v setName:newServiceName];
[v setServer:newserver];
[v setSource:serversrc];
[v setUrlString:serviceURL];
[v setType:NFLNK];
[v setMode:01755 | NFSMODE_LNK];
[v setVfsType:[String uniqueString:"url"]];
[v setNSLObject:service type:kNetworkServer];
/* [v setupOptions:opts]; */
[v setMap:map];
[controller registerVnode:v];
// sys_msg(debug, LOG_DEBUG, "NSLVnode.populate: Adding new offspring [self addChild:v];
/* This relies on the superlink/child/map information to construct its path and must be done last... */
setupLink(v);
return v;
Error_Exit:
if (serversrc) [serversrc release];
if (newserver) [newserver release];
if (serviceURL) [serviceURL release];
if (v) [v release];
return nil;
}
- (NSLVnode *)newSymlinkWithName:(String *)newSymlink target:(char *)target
{
String *localLinkSpec;
String *targetString;
NSLVnode *v;
localLinkSpec = [String uniqueString:gLocalHostName];
if (localLinkSpec == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.newSymlinkWithName: Failed to allocate localLinkSpec; aborting.");
goto Error_Exit;
};
targetString = [String uniqueString:target];
if (targetString == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.newSymlinkWithName: Failed to allocate targetString; aborting.");
goto Error_Exit;
};
v = [self newServiceWithName:localLinkSpec service:nil serviceURL:nil];
if (v == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.newSymlinkWithName: Failed to allocate NSLVnode; aborting.");
goto Error_Exit;
};
[v setMode:([v mode] & ~01000)]; /* Turn off the sticky bit: this is not a special mount-trigger link */
[v setName:newSymlink];
[v setLink:targetString];
[v setFixedEntry:YES];
return v;
Error_Exit:
return nil;
}
- (void)dealloc
{
[self freeNSLObject];
[super dealloc];
}
- (void)invalidateRecursively:(BOOL)invalidateDescendants;
{
struct timeval now;
int offspring_count, neighborhood_offspring;
[super invalidateRecursively:invalidateDescendants];
if (!havePopulated) return;
sys_msg(debug, LOG_DEBUG, "NSLVnode.invalidateRecursively: invalidating '%s'...", [[self path] value]);
offspring_count = [[self children] count];
neighborhood_offspring = 0;
if (invalidateDescendants) {
int i;
NSLVnode *offspring;
for (i = 0; i < offspring_count; ++i) {
offspring = [[self children] objectAtIndex:i];
if ([offspring getobjectType] == kNetworkNeighborhood) {
++neighborhood_offspring;
[offspring invalidateRecursively:YES];
};
};
};
#if DONTINVALIDATEINTERMEDIATECONTAINERS
if ((offspring_count > 0) && (neighborhood_offspring == offspring_count)) {
/* This object contains only other directories: no need to invalidate it... */
return;
};
#endif
/* This will trigger a refresh on the next reference: */
lastUpdate.tv_sec = 0;
lastUpdate.tv_usec = 0;
do {
gettimeofday(&now, NULL);
} while ((now.tv_sec == attributes.atime.seconds) && (now.tv_usec == attributes.atime.useconds));
attributes.atime.seconds = now.tv_sec;
attributes.atime.useconds = now.tv_usec;
attributes.mtime = attributes.atime;
attributes.ctime = attributes.atime;
sys_msg(debug, LOG_DEBUG, "NSLVnode.invalidateRecursively: notifying Finder about changes to '%s'...", [[self path] value]);
FNNotifyByPath([[self path] value], kFNDirectoryModifiedMessage, kNilOptions);
sys_msg(debug, LOG_DEBUG, "NSLVnode.invalidateRecursively: invalidation of '%s' complete.", [[self path] value]);
}
- (unsigned long)getGeneration
{
return generation;
}
- (void)setGeneration:(unsigned long)newGeneration
{
generation = newGeneration;
}
- (NetworkObjectType)getobjectType {
return NSLObjectType;
}
- (void)freeNSLObject {
switch (NSLObjectType) {
case kNetworkNeighborhood:
if (NSLObject.neighborhood) NSLFreeNeighborhood(NSLObject.neighborhood);
NSLObject.neighborhood = NULL;
break;
case kNetworkServer:
if (NSLObject.service) CFRelease(NSLObject.service);
NSLObject.service = NULL;
break;
default:
break;
};
NSLObjectType = kNetworkObjectTypeNone;
}
- (void)setNSLObject:(const void *)object type:(NetworkObjectType)objecttype {
[self freeNSLObject];
NSLObjectType = objecttype;
switch (objecttype) {
case kNetworkNeighborhood:
NSLObject.neighborhood = NSLCopyNeighborhood((NSLNeighborhood)object);
break;
case kNetworkServer:
NSLObject.service =
(NSLService)CFPropertyListCreateDeepCopy( kCFAllocatorDefault,
(CFMutableDictionaryRef)object,
kCFPropertyListMutableContainers);
break;
default:
break;
};
}
- (BOOL)fixedEntry
{
return fixedEntry;
}
- (void)setFixedEntry:(BOOL)newFixedEntryStatus
{
fixedEntry = newFixedEntryStatus;
}
- (void)depopulateDescendants:(BOOL)depopulateDescendants destroyEmptyNeighborhoods:(BOOL)destroyEmptyNeighborhoods
{
int target_offspring = 0;
NSLVnode *targetVnode = nil;
BOOL contentsChanged = NO;
struct timeval now;
sys_msg(debug, LOG_DEBUG, "NSLVnode.depopulate: depopulating neighborhood '%s'...", [[self name] value]);
if ([self children]) {
while ((NSLVnode *)([[self children] objectAtIndex:target_offspring]) != targetVnode) {
targetVnode = (NSLVnode *)[[self children] objectAtIndex:target_offspring];
if (targetVnode == nil) {
++target_offspring; /* Skip this entry */
} else {
if ([targetVnode getobjectType] == kNetworkNeighborhood) {
if (depopulateDescendants) {
[targetVnode depopulateDescendants:YES destroyEmptyNeighborhoods:destroyEmptyNeighborhoods];
};
if (destroyEmptyNeighborhoods && ([[targetVnode children] count] == 0)) {
sys_msg(debug, LOG_DEBUG, "NSLVnode.depopulate: discarding neighborhood '%s'...", [[targetVnode name] value]);
[controller destroyVnode:targetVnode];
contentsChanged = YES;
} else {
++target_offspring; /* Skip this entry */
};
} else if ([targetVnode getobjectType] == kNetworkServer) {
if ([targetVnode mounted]) {
++target_offspring; /* Skip this entry */
} else {
sys_msg(debug, LOG_DEBUG, "NSLVnode.depopulate: discarding server '%s'...", [[targetVnode name] value]);
[controller destroyVnode:targetVnode];
contentsChanged = YES;
};
} else {
sys_msg(debug, LOG_DEBUG, "NSLVnode.depopulate: unknown node type ('%s')...", [[targetVnode name] value]);
++target_offspring; /* Skip this entry */
};
};
};
};
havePopulated = NO; /* Reset to trigger a fresh look at things */
if (contentsChanged) {
do {
gettimeofday(&now, NULL);
} while ((now.tv_sec == attributes.atime.seconds) && (now.tv_usec == attributes.atime.useconds));
/* Update this directory's timestamps as well: */
attributes.atime.seconds = now.tv_sec;
attributes.atime.useconds = now.tv_usec;
attributes.mtime = attributes.atime;
attributes.ctime = attributes.atime;
};
}
- (void)destroyVnodeGenerationsPriorTo:(unsigned long)currentGeneration
{
int offspringCount;
int target_offspring;
NSLVnode *targetVnode = nil;
BOOL contentsChanged = NO;
struct timeval now;
sys_msg(debug, LOG_DEBUG, "NSLVnode.destroyVnodeGenerationsPriorTo: destroying generations before #
if ([self children]) {
offspringCount = [[self children] count];
target_offspring = 0;
while (target_offspring < offspringCount) {
targetVnode = (NSLVnode *)[[self children] objectAtIndex:target_offspring];
if ((targetVnode == nil) ||
([targetVnode fixedEntry]) ||
([targetVnode getGeneration] >= currentGeneration)) {
++target_offspring;
continue;
};
/* This Vnode belongs to an older generation: */
if ([targetVnode getobjectType] == kNetworkNeighborhood) {
sys_msg(debug, LOG_DEBUG, "NSLVnode.destroyVnodeGenerationsPriorTo: destroying offspring of '%s'...", [[targetVnode name] value]);
[targetVnode depopulateDescendants:YES destroyEmptyNeighborhoods:YES];
};
sys_msg(debug, LOG_DEBUG, "NSLVnode.destroyVnodeGenerationsPriorTo: destroying '%s'...", [[targetVnode name] value]);
[controller destroyVnode:targetVnode];
--offspringCount;
contentsChanged = YES;
};
};
if (contentsChanged) {
do {
gettimeofday(&now, NULL);
} while ((now.tv_sec == attributes.atime.seconds) && (now.tv_usec == attributes.atime.useconds));
/* Update this directory's timestamps as well: */
attributes.atime.seconds = lastUpdate.tv_sec;
attributes.atime.useconds = lastUpdate.tv_usec;
attributes.mtime = attributes.atime;
attributes.ctime = attributes.atime;
};
}
- (unsigned long)populateWithNeighborhoodsWithGeneration:(unsigned long)newGeneration
{
unsigned long entrycount = 0;
struct SearchResultList neighborhoodContents;
SearchContext context;
int result;
struct SearchResult *resultEntry;
char *neighborhoodname = NULL;
long neighborhoodnamelength;
NSLClientRef automountNSLClientRef = [(NSLMap *)map getNSLClientRef];
INIT_SEARCHRESULTLIST(&neighborhoodContents, NSLObject.neighborhood);
INIT_SEARCHCONTEXT(&context, automountNSLClientRef, &neighborhoodContents);
result = StartSearchForNeighborhoodsInNeighborhood(NSLObject.neighborhood, &context);
if (result) {
sys_msg(debug, LOG_ERR, "NSLVnode.populateWithNeighborhoodsWithGeneration: error goto Error_Exit;
};
WaitForSearchCompletion(&neighborhoodContents);
(void)NSLDeleteRequest(context.searchRef);
#if 0
sys_msg(debug, LOG_DEBUG, "NSLVnode.populateWithNeighborhoodsWithGeneration: Search result list at 0x (unsigned long)&neighborhoodContents.contentsFound,
(unsigned long)neighborhoodContents.contentsFound.tqh_first,
(unsigned long)neighborhoodContents.contentsFound.tqh_last);
#endif
while (!TAILQ_EMPTY(&neighborhoodContents.contentsFound)) {
String *neighborhoodNameString = nil;
NSLVnode *v = nil;
resultEntry = TAILQ_FIRST(&neighborhoodContents.contentsFound);
NSLGetNameFromNeighborhood( resultEntry->result.neighborhood, &neighborhoodname, &neighborhoodnamelength );
if ((neighborhoodname == NULL) || (neighborhoodnamelength == 0)) {
sys_msg(debug, LOG_ERR, "NSLVnode.populateWithNeighborhoodsWithGeneration: Failed to get neighborhood name for entry goto Abort_Neighborhood_Entry;
};
neighborhoodNameString = [String uniqueString:neighborhoodname];
if (neighborhoodNameString == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.populateWithNeighborhoodsWithGeneration: Failed to allocate String 'neighborhoodNameString'; aborting.");
goto Abort_Neighborhood_Entry;
}
v = (NSLVnode *)[self lookup:neighborhoodNameString];
if (v && ([v getobjectType] != kNetworkNeighborhood)) {
[controller destroyVnode:v];
v = nil;
};
if (v == nil) {
sys_msg(debug, LOG_DEBUG, "NSLVnode.populateWithNeighborhoodsWithGeneration: Adding neighborhood v = [self newNeighborhoodWithName:neighborhoodNameString neighborhood:resultEntry->result.neighborhood];
if (v == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.populateWithNeighborhoodsWithGeneration: Failed to allocate new neighborhood node; aborting.");
goto Abort_Neighborhood_Entry;
};
};
[v setGeneration:newGeneration];
/* [v setVfsType:??? ]; */
++entrycount;
goto Next_Neighborhood_Entry;
Abort_Neighborhood_Entry:
/* Release any data structures otherwise permanently incorporated into this new node: */
Next_Neighborhood_Entry:
/* Release any data structures used to construct this new node: */
if (neighborhoodNameString) [neighborhoodNameString release];
TAILQ_REMOVE(&neighborhoodContents.contentsFound, resultEntry, sibling_link);
NSLFreeNeighborhood(resultEntry->result.neighborhood);
free(resultEntry);
};
Error_Exit:
return entrycount;
}
- (unsigned long)populateWithServicesWithGeneration:(unsigned long)newGeneration
{
unsigned long entrycount = 0;
struct SearchResultList neighborhoodContents;
SearchContext context;
int result;
struct SearchResult *resultEntry;
CFMutableArrayRef serviceListRef = NULL;
char servicename[MAXNSLOBJECTNAMELENGTH];
NSLClientRef automountNSLClientRef = [(NSLMap *)map getNSLClientRef];
char url[MAXNSLOBJECTNAMELENGTH];
INIT_SEARCHRESULTLIST(&neighborhoodContents, NSLObject.neighborhood);
INIT_SEARCHCONTEXT(&context, automountNSLClientRef, &neighborhoodContents);
serviceListRef = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
if (serviceListRef == NULL) goto Error_Exit;
CFArrayAppendValue( serviceListRef, CFSTR("afp") );
CFArrayAppendValue( serviceListRef, CFSTR("smb") );
CFArrayAppendValue( serviceListRef, CFSTR("cifs") );
CFArrayAppendValue( serviceListRef, CFSTR("nfs") );
CFArrayAppendValue( serviceListRef, CFSTR("webdav") );
#if 0
sys_msg(debug, LOG_DEBUG, "Pre-StartSearchForServicesInNeighborhood: Search result list at 0x (unsigned long)&neighborhoodContents.contentsFound,
(unsigned long)neighborhoodContents.contentsFound.tqh_first,
(unsigned long)neighborhoodContents.contentsFound.tqh_last);
#endif
result = StartSearchForServicesInNeighborhood( NSLObject.neighborhood, serviceListRef, &context );
if (result) {
sys_msg(debug, LOG_ERR, "NSLVnode.populate: error goto Error_Exit;
};
WaitForSearchCompletion(&neighborhoodContents);
(void)NSLDeleteRequest(context.searchRef);
#if 0
sys_msg(debug, LOG_DEBUG, "After search: Search result list at 0x (unsigned long)&neighborhoodContents.contentsFound,
(unsigned long)neighborhoodContents.contentsFound.tqh_first,
(unsigned long)neighborhoodContents.contentsFound.tqh_last);
#endif
while (!TAILQ_EMPTY(&neighborhoodContents.contentsFound)) {
String *servername = nil;
CFStringRef urlStringRef = NULL;
CFStringRef serviceNameRef;
NSLVnode *v = nil;
resultEntry = TAILQ_FIRST(&neighborhoodContents.contentsFound);
serviceNameRef = GetMainStringFromAttribute( resultEntry->result.service, kNSLNameAttrRef );
if (serviceNameRef == NULL) {
sys_msg(debug, LOG_ERR, "NSLVnode.populate: Failed to get serviceNameRef; aborting.");
goto Abort_Service_Entry;
};
servicename[0] = (char)0;
(void)CFStringGetCString(serviceNameRef, servicename, (CFIndex)sizeof(servicename), kCFStringEncodingUTF8);
#if 0
sys_msg(debug, LOG_DEBUG, "NSLVnode.populate: resultEntry = 0x (unsigned long)resultEntry, (unsigned long)serviceNameRef, servicename);
#endif
if (servicename[0] == (char)0) {
sys_msg(debug, LOG_ERR, "NSLVnode.populate: empty servicename string; aborting.");
goto Abort_Service_Entry;
};
servername = [String uniqueString:servicename];
if (servername == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.populate: Failed to allocate servername string; aborting.");
goto Abort_Service_Entry;
}
v = (NSLVnode *)[self lookup:servername];
if (v && ([v getobjectType] != kNetworkServer)) {
[controller destroyVnode:v];
v = nil;
};
if (v == nil) {
sys_msg(debug, LOG_DEBUG, "NSLVnode.populate: Adding server
urlStringRef = GetMainStringFromAttribute( resultEntry->result.service, kNSLURLAttrRef );
if (urlStringRef) {
(void)CFStringGetCString(urlStringRef, url, (CFIndex)sizeof(url), kCFStringEncodingUTF8);
};
v = [self newServiceWithName:servername service:resultEntry->result.service serviceURL:(urlStringRef ? url : nil)];
if (v == nil) {
sys_msg(debug, LOG_ERR, "NSLVnode.populate: Failed to allocate new service node; aborting.");
goto Abort_Service_Entry;
};
};
[v setGeneration:newGeneration];
++entrycount;
goto Next_Service_Entry;
Abort_Service_Entry:
/* Release any data structures otherwise permanently incorporated into this new node: */
if (v) [v release];
Next_Service_Entry:
/* Release any data structures used to construct this new node: */
if (servername) [servername release];
TAILQ_REMOVE(&neighborhoodContents.contentsFound, resultEntry, sibling_link);
CFRelease(resultEntry->result.service);
free(resultEntry);
};
Error_Exit:
return entrycount;
}
/*
* Get sub-neighborhoods and servers for this neighborhood.
*/
- (void)populate
{
struct timeval now;
unsigned long entrycount;
if (beingPopulated) return;
beingPopulated = YES;
if (havePopulated) {
if (NSL_REFRESH_RATE == 0) goto Std_Exit; /* No refresh requested */
gettimeofday(&now, NULL);
if (now.tv_sec < (lastUpdate.tv_sec + NSL_REFRESH_RATE)) goto Std_Exit;
};
++currentContentGeneration; /* About to populate with a new generation of descendants */
sys_msg(debug, LOG_DEBUG, "NSLVnode.populate: populating neighborhood '%s'...", [[self name] value]);
entrycount = [self populateWithNeighborhoodsWithGeneration:currentContentGeneration] +
[self populateWithServicesWithGeneration:currentContentGeneration];
[self destroyVnodeGenerationsPriorTo:currentContentGeneration];
if (entrycount == 1) {
sys_msg(debug, LOG_DEBUG, "NSLVnode.populate: 1 entry.");
} else {
sys_msg(debug, LOG_DEBUG, "NSLVnode.populate: };
gettimeofday(&lastUpdate, NULL);
/* Update this directory's timestamps as well: */
attributes.atime.seconds = lastUpdate.tv_sec;
attributes.atime.useconds = lastUpdate.tv_usec;
attributes.mtime = attributes.atime;
attributes.ctime = attributes.atime;
havePopulated = YES;
Std_Exit:
beingPopulated = NO;
}
- (Vnode *)lookup:(String *)n
{
[self populate]; /* Look up the directory's contents in NSL if necessary */
return [super lookup:n];
}
/*
* called from readdir
*/
- (Array *)dirlist
{
[self populate]; /* Look up the directory's contents in NSL if necessary */
return [super dirlist];
}
- (int)symlinkWithName:(char *)from to:(char *)to attributes:(struct nfsv2_sattr *)attributes;
{
int result;
String *source = nil;
NSLVnode *v = nil;
if (strchr(from, '/')) {
result = NFSERR_NXIO;
goto Error_Exit;
};
source = [String uniqueString:from];
if (havePopulated) {
v = (NSLVnode *)[self lookup:source];
} else {
int i, count;
NSLVnode *sub;
count = [subnodes count];
for (i = 0; i < count; i++)
{
sub = [subnodes objectAtIndex:i];
if ([source equal:[sub name]]) {
v = sub;
break;
};
};
};
if (v) {
result = NFSERR_EXIST;
goto Error_Exit;
};
v = [self newSymlinkWithName:source target:to];
result = (v) ? NFS_OK : NFSERR_NXIO;
Error_Exit:
if (source) [source release];
return result;
}
-(NSLNeighborhood)getNSLNeighborhood
{
if (NSLObjectType == kNetworkNeighborhood) {
return NSLObject.neighborhood;
} else {
sys_msg(debug, LOG_ERR,
"NSLVnode.getNSLNeighborhood: type for [[self name] value], NSLObjectType, kNetworkNeighborhood);
return NULL;
};
}
-(NSLService)getNSLService
{
if (NSLObjectType == kNetworkServer) {
return NSLObject.service;
} else {
sys_msg(debug, LOG_ERR,
"NSLVnode.getNSLService: type for [[self name] value], NSLObjectType, kNetworkServer);
return NULL;
};
}
- (String *)urlString
{
if (urlString) {
return urlString;
} else {
NSLService service = [self getNSLService];
CFStringRef urlStringRef = NULL;
char urlbuffer[MAXNSLOBJECTNAMELENGTH];
urlbuffer[0] = (char)0;
if (service == NULL) {
sys_msg(debug, LOG_ERR, "NSLVnode.urlString: NULL service record for return nil;
};
urlStringRef = GetMainStringFromAttribute( service, kNSLURLAttrRef );
if (urlStringRef == NULL) {
sys_msg(debug, LOG_DEBUG, "NSLVnode.urlString: No URL attribute in service record; calling NSLXResolveService...");
(void)NSLXResolveService(service);
urlStringRef = GetMainStringFromAttribute( service, kNSLURLAttrRef );
};
if (urlStringRef) {
(void)CFStringGetCString(urlStringRef, urlbuffer, (CFIndex)sizeof(urlbuffer), kCFStringEncodingUTF8);
} else {
CFStringRef attributeStringRef = NULL;
char attributeCString[64];
char servicetype[64];
char hostaddress[MAXNSLOBJECTNAMELENGTH];
char portnumber[64];
sys_msg(debug, LOG_DEBUG, "NSLVnode.urlString: reconstituting URL for
attributeStringRef = GetMainStringFromAttribute( service, kNSLServiceTypeAttrRef );
if (attributeStringRef == NULL) {
sys_msg(debug, LOG_DEBUG, "NSLVnode.urlString: kNSLServiceTypeAttrRef attributeStringRef is NULL; aborting.");
goto Error_Exit;
};
(void)CFStringGetCString(attributeStringRef, servicetype, (CFIndex)sizeof(servicetype), kCFStringEncodingUTF8);
// sys_msg(debug, LOG_DEBUG, "NSLVnode.urlString: kNSLServiceTypeAttrRef = '%s'...", servicetype);
if (strcmp(servicetype, "afpovertcp") == 0) {
strcpy(servicetype, "afp");
};
attributeStringRef = GetMainStringFromAttribute( service, kNSLIPAddressAttrRef );
if (attributeStringRef == NULL) {
sys_msg(debug, LOG_DEBUG, "NSLVnode.urlString: kNSLIPAddressAttrRef attributeStringRef is NULL; aborting.");
goto Error_Exit;
};
(void)CFStringGetCString(attributeStringRef, hostaddress, (CFIndex)sizeof(attributeCString), kCFStringEncodingUTF8);
// sys_msg(debug, LOG_DEBUG, "NSLVnode.urlString: hostaddress = '%s'...", hostaddress);
attributeStringRef = GetMainStringFromAttribute( service, kNSLPortAttrRef );
if (attributeStringRef == NULL) {
sys_msg(debug, LOG_DEBUG, "NSLVnode.urlString: kNSLPortAttrRef attributeStringRef is NULL; aborting.");
goto Error_Exit;
};
(void)CFStringGetCString(attributeStringRef, portnumber, (CFIndex)sizeof(attributeCString), kCFStringEncodingUTF8);
// sys_msg(debug, LOG_DEBUG, "NSLVnode.urlString: portnumber = '%s'...", portnumber);
snprintf(urlbuffer, sizeof(urlbuffer), " sys_msg(debug, LOG_DEBUG, "NSLVnode.urlString: reconstituted URL = '%s'...", urlbuffer);
};
[self setUrlString:[String uniqueString:urlbuffer]];
};
Error_Exit:
return urlString;
}
- (void)setNfsStatus:(unsigned int)s
{
gettimeofday(&ErrorTime, NULL); /* Mark the time this error was generated */
[super setNfsStatus:s];
}
- (unsigned int)nfsStatus
{
if (nfsStatus) {
struct timeval now;
gettimeofday(&now, NULL);
if (now.tv_sec >= (ErrorTime.tv_sec + NSL_ERROR_PERSISTENCE_DURATION)) {
[self setNfsStatus:0];
};
};
return nfsStatus;
}
@end
static String *generateLinkTarget(Vnode *v)
{
size_t len;
char *buf;
String *s;
len = [[[v map] mountPoint] length] + [[v path] length] + 1;
buf = malloc(len);
if (buf == NULL) return nil;
sprintf(buf, " s = [String uniqueString:buf];
free(buf);
return s;
}
static void setupLink(Vnode *v)
{
String *x;
if (v == nil) return;
if ([[v server] isLocalHost])
{
[v setLink:[v source]];
[v setMode:00755 | NFSMODE_LNK];
[v setMounted:YES];
[v setFakeMount:YES];
return;
}
x = generateLinkTarget(v);
if (x == nil) {
sys_msg(debug, LOG_ERR, "setupLink: Failed to allocate link string for '%s'...", [[v name] value]);
goto Error_Exit;
};
sys_msg(debug, LOG_DEBUG, "setupLink: Targeting '%s' to mount on '%s'...", [[v name] value], [x value]);
[v setLink:x];
[x release];
Error_Exit: ;
}