#import "NSLVnode.h" #import "Controller.h" #import "AMString.h" #import "AMMap.h" #import "automount.h" #import "log.h" #import #import #import #import #import #import "nfs_prot.h" #import "Server.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "nfs_prot.h" #import "automount.h" #import "log.h" #import "mount.h" #import "AMString.h" #import "NSLUtil.h" #import /* 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 %s (%s)...", [[v name] value], [[v link] value]); [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 #%ld...", currentGeneration); 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 %d in SearchForNeighborhoodsInNeighborhood", result); goto Error_Exit; }; WaitForSearchCompletion(&neighborhoodContents); (void)NSLDeleteRequest(context.searchRef); #if 0 sys_msg(debug, LOG_DEBUG, "NSLVnode.populateWithNeighborhoodsWithGeneration: Search result list at 0x%08lx = { 0x%08lx, 0x%08lx }.", (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 %d; aborting.", entrycount); 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 %s...", neighborhoodname); 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%08lx = { 0x%08lx, 0x%08lx }.", (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 %d in StartSearchForServicesInNeighborhood", result); goto Error_Exit; }; WaitForSearchCompletion(&neighborhoodContents); (void)NSLDeleteRequest(context.searchRef); #if 0 sys_msg(debug, LOG_DEBUG, "After search: Search result list at 0x%08lx = { 0x%08lx, 0x%08lx }.", (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%08lx; serviceNameRef = 0x%08lx, servicename = '%s'...", (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 %s (%s)...", servicename, url); 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: %ld entries.", entrycount); }; 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 %s is %d instead of kNetworkNeighborhood (%d)?!", [[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 %s is %d instead of kNetworkServer (%d)?!", [[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 %d?!", [[self name] value]); 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 %s...", [[self name] value]); 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), "%s://%s:%s/", servicetype, hostaddress, portnumber); 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%s", [[[v map] mountPoint] value], [[v path] value]); 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: ; }