/******************************************************************** * MoveFile * lpFromFileName: old file path * lpToFileName: new file path ********************************************************************/ BOOL MoveFile(const char * lpFromFileName, const char * lpToFileName) { OSErr theErr; FSRef fromFileRef; FSRef toFileRef; FSRef parentFolderRef; // Get the path to the old file theErr = FSPathMakeRef((const UInt8 *)lpFromFileName, &fromFileRef, NULL); if (theErr != noErr) { SetLastError(theErr); return false; } // Get the path to the new folder for the file char folderName[strlen(lpToFileName)]; CFStringRef folderPathCFString = CFStringCreateWithCString(NULL, lpToFileName, kCFStringEncodingUTF8); CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, folderPathCFString, kCFURLPOSIXPathStyle, FALSE); CFURLRef folderURL = CFURLCreateCopyDeletingLastPathComponent(NULL, fileURL); CFURLGetFileSystemRepresentation(folderURL, TRUE, (UInt8 *)folderName, strlen(lpToFileName)); theErr = FSPathMakeRef((UInt8 *)folderName, &parentFolderRef, NULL); CFRelease(fileURL); CFRelease(folderURL); CFRelease(folderPathCFString); // Move the old file theErr = FSMoveObject(&fromFileRef, &parentFolderRef, &toFileRef); if (theErr != noErr) { SetLastError(theErr); return false; } // Get a CFString for the new file name CFStringRef newFileNameCFString = CFStringCreateWithCString(NULL, lpToFileName, kCFStringEncodingUTF8); fileURL = CFURLCreateWithFileSystemPath(NULL, newFileNameCFString, kCFURLPOSIXPathStyle, FALSE); CFRelease(newFileNameCFString); newFileNameCFString = CFURLCopyLastPathComponent(fileURL); CFRelease(fileURL); // Convert CFString to Unicode and rename the file UniChar unicodeFileName[256]; CFStringGetCharacters(newFileNameCFString, CFRangeMake(0, CFStringGetLength(newFileNameCFString)), unicodeFileName); theErr = FSRenameUnicode(&toFileRef, CFStringGetLength(newFileNameCFString), unicodeFileName, kTextEncodingUnknown, NULL); if (theErr != noErr) { SetLastError(theErr); CFRelease(newFileNameCFString); return false; } CFRelease(newFileNameCFString); SetLastError(theErr); return true; }
void *updatethreadproc(void*) { char tempDir[PATH_MAX] = ""; /* Flawfinder: ignore */ FSRef tempDirRef; char temp[PATH_MAX] = ""; /* Flawfinder: ignore */ // *NOTE: This buffer length is used in a scanf() below. char deviceNode[1024] = ""; /* Flawfinder: ignore */ LLFILE *downloadFile = NULL; OSStatus err; ProcessSerialNumber psn; char target[PATH_MAX] = ""; /* Flawfinder: ignore */ FSRef targetRef; FSRef targetParentRef; FSVolumeRefNum targetVol; FSRef trashFolderRef; Boolean replacingTarget = false; memset(&tempDirRef, 0, sizeof(tempDirRef)); memset(&targetRef, 0, sizeof(targetRef)); memset(&targetParentRef, 0, sizeof(targetParentRef)); try { // Attempt to get a reference to the Second Life application bundle containing this updater. // Any failures during this process will cause us to default to updating /Applications/Second Life.app { FSRef myBundle; err = GetCurrentProcess(&psn); if(err == noErr) { err = GetProcessBundleLocation(&psn, &myBundle); } if(err == noErr) { // Sanity check: Make sure the name of the item referenced by targetRef is "Second Life.app". FSRefMakePath(&myBundle, (UInt8*)target, sizeof(target)); llinfos << "Updater bundle location: " << target << llendl; } // Our bundle should be in Second Life.app/Contents/Resources/AutoUpdater.app // so we need to go up 3 levels to get the path to the main application bundle. if(err == noErr) { err = FSGetCatalogInfo(&myBundle, kFSCatInfoNone, NULL, NULL, NULL, &targetRef); } if(err == noErr) { err = FSGetCatalogInfo(&targetRef, kFSCatInfoNone, NULL, NULL, NULL, &targetRef); } if(err == noErr) { err = FSGetCatalogInfo(&targetRef, kFSCatInfoNone, NULL, NULL, NULL, &targetRef); } // And once more to get the parent of the target if(err == noErr) { err = FSGetCatalogInfo(&targetRef, kFSCatInfoNone, NULL, NULL, NULL, &targetParentRef); } if(err == noErr) { FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target)); llinfos << "Path to target: " << target << llendl; } // Sanity check: make sure the target is a bundle with the right identifier if(err == noErr) { // Assume the worst... err = -1; if(isFSRefViewerBundle(&targetRef)) { // This is the bundle we're looking for. err = noErr; replacingTarget = true; } } // Make sure the target's parent directory is writable. if(err == noErr) { if(!isDirWritable(targetParentRef)) { // Parent directory isn't writable. llinfos << "Target parent directory not writable." << llendl; err = -1; replacingTarget = false; } } if(err != noErr) { Boolean isDirectory; llinfos << "Target search failed, defaulting to /Applications/" << gProductName << ".app." << llendl; // Set up the parent directory err = FSPathMakeRef((UInt8*)"/Applications", &targetParentRef, &isDirectory); if((err != noErr) || (!isDirectory)) { // We're so hosed. llinfos << "Applications directory not found, giving up." << llendl; throw 0; } snprintf(target, sizeof(target), "/Applications/%s.app", gProductName); memset(&targetRef, 0, sizeof(targetRef)); err = FSPathMakeRef((UInt8*)target, &targetRef, NULL); if(err == fnfErr) { // This is fine, just means we're not replacing anything. err = noErr; replacingTarget = false; } else { replacingTarget = true; } // Make sure the target's parent directory is writable. if(err == noErr) { if(!isDirWritable(targetParentRef)) { // Parent directory isn't writable. llinfos << "Target parent directory not writable." << llendl; err = -1; replacingTarget = false; } } } // If we haven't fixed all problems by this point, just bail. if(err != noErr) { llinfos << "Unable to pick a target, giving up." << llendl; throw 0; } } // Find the volID of the volume the target resides on { FSCatalogInfo info; err = FSGetCatalogInfo( &targetParentRef, kFSCatInfoVolume, &info, NULL, NULL, NULL); if(err != noErr) throw 0; targetVol = info.volume; } // Find the temporary items and trash folders on that volume. err = FSFindFolder( targetVol, kTrashFolderType, true, &trashFolderRef); if(err != noErr) throw 0; #if 0 // *HACK for DEV-11935 see below for details. FSRef tempFolderRef; err = FSFindFolder( targetVol, kTemporaryFolderType, true, &tempFolderRef); if(err != noErr) throw 0; err = FSRefMakePath(&tempFolderRef, (UInt8*)temp, sizeof(temp)); if(err != noErr) throw 0; #else // *HACK for DEV-11935 the above kTemporaryFolderType query was giving // back results with path names that seem to be too long to be used as // mount points. I suspect this incompatibility was introduced in the // Leopard 10.5.2 update, but I have not verified this. char const HARDCODED_TMP[] = "/tmp"; strncpy(temp, HARDCODED_TMP, sizeof(HARDCODED_TMP)); #endif // 0 *HACK for DEV-11935 // Skip downloading the file if the dmg was passed on the command line. std::string dmgName; if(gDmgFile != NULL) { dmgName = basename((char *)gDmgFile); char * dmgDir = dirname((char *)gDmgFile); strncpy(tempDir, dmgDir, sizeof(tempDir)); err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL); if(err != noErr) throw 0; chdir(tempDir); goto begin_install; } else { // Continue on to download file. dmgName = "SecondLife.dmg"; } strncat(temp, "/SecondLifeUpdate_XXXXXX", (sizeof(temp) - strlen(temp)) - 1); if(mkdtemp(temp) == NULL) { throw 0; } strncpy(tempDir, temp, sizeof(tempDir)); temp[sizeof(tempDir) - 1] = '\0'; llinfos << "tempDir is " << tempDir << llendl; err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL); if(err != noErr) throw 0; chdir(tempDir); snprintf(temp, sizeof(temp), "SecondLife.dmg"); downloadFile = LLFile::fopen(temp, "wb"); /* Flawfinder: ignore */ if(downloadFile == NULL) { throw 0; } { CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback); curl_easy_setopt(curl, CURLOPT_FILE, downloadFile); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func); curl_easy_setopt(curl, CURLOPT_URL, gUpdateURL); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); sendProgress(0, 1, CFSTR("Downloading...")); CURLcode result = curl_easy_perform(curl); curl_easy_cleanup(curl); if(gCancelled) { llinfos << "User cancel, bailing out."<< llendl; throw 0; } if(result != CURLE_OK) { llinfos << "Error " << result << " while downloading disk image."<< llendl; throw 0; } fclose(downloadFile); downloadFile = NULL; } begin_install: sendProgress(0, 0, CFSTR("Mounting image...")); LLFile::mkdir("mnt", 0700); // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder, // but if our cleanup fails, this makes it much harder for the user to unmount the image. std::string mountOutput; boost::format cmdFormat("hdiutil attach %s -mountpoint mnt"); cmdFormat % dmgName; FILE* mounter = popen(cmdFormat.str().c_str(), "r"); /* Flawfinder: ignore */ if(mounter == NULL) { llinfos << "Failed to mount disk image, exiting."<< llendl; throw 0; } // We need to scan the output from hdiutil to find the device node it uses to attach the disk image. // If we don't have this information, we can't detach it later. while(mounter != NULL) { size_t len = fread(temp, 1, sizeof(temp)-1, mounter); temp[len] = 0; mountOutput.append(temp); if(len < sizeof(temp)-1) { // End of file or error. int result = pclose(mounter); if(result != 0) { // NOTE: We used to abort here, but pclose() started returning // -1, possibly when the size of the DMG passed a certain point llinfos << "Unexpected result closing pipe: " << result << llendl; } mounter = NULL; } } if(!mountOutput.empty()) { const char *s = mountOutput.c_str(); const char *prefix = "/dev/"; char *sub = strstr(s, prefix); if(sub != NULL) { sub += strlen(prefix); /* Flawfinder: ignore */ sscanf(sub, "%1023s", deviceNode); /* Flawfinder: ignore */ } } if(deviceNode[0] != 0) { llinfos << "Disk image attached on /dev/" << deviceNode << llendl; } else { llinfos << "Disk image device node not found!" << llendl; throw 0; } // Get an FSRef to the new application on the disk image FSRef sourceRef; FSRef mountRef; snprintf(temp, sizeof(temp), "%s/mnt", tempDir); llinfos << "Disk image mount point is: " << temp << llendl; err = FSPathMakeRef((UInt8 *)temp, &mountRef, NULL); if(err != noErr) { llinfos << "Couldn't make FSRef to disk image mount point." << llendl; throw 0; } sendProgress(0, 0, CFSTR("Searching for the app bundle...")); err = findAppBundleOnDiskImage(&mountRef, &sourceRef); if(err != noErr) { llinfos << "Couldn't find application bundle on mounted disk image." << llendl; throw 0; } else { llinfos << "found the bundle." << llendl; } sendProgress(0, 0, CFSTR("Preparing to copy files...")); FSRef asideRef; char aside[MAX_PATH]; /* Flawfinder: ignore */ // this will hold the name of the destination target CFStringRef appNameRef; if(replacingTarget) { // Get the name of the target we're replacing HFSUniStr255 appNameUniStr; err = FSGetCatalogInfo(&targetRef, 0, NULL, &appNameUniStr, NULL, NULL); if(err != noErr) throw 0; appNameRef = FSCreateStringFromHFSUniStr(NULL, &appNameUniStr); // Move aside old version (into work directory) err = FSMoveObject(&targetRef, &tempDirRef, &asideRef); if(err != noErr) { llwarns << "failed to move aside old version (error code " << err << ")" << llendl; throw 0; } // Grab the path for later use. err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside)); } else { // Construct the name of the target based on the product name char appName[MAX_PATH]; /* Flawfinder: ignore */ snprintf(appName, sizeof(appName), "%s.app", gProductName); appNameRef = CFStringCreateWithCString(NULL, appName, kCFStringEncodingUTF8); } sendProgress(0, 0, CFSTR("Copying files...")); llinfos << "Starting copy..." << llendl; // Copy the new version from the disk image to the target location. err = FSCopyObjectSync( &sourceRef, &targetParentRef, appNameRef, &targetRef, kFSFileOperationDefaultOptions); // Grab the path for later use. err = FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target)); if(err != noErr) throw 0; llinfos << "Copy complete. Target = " << target << llendl; if(err != noErr) { // Something went wrong during the copy. Attempt to put the old version back and bail. (void)FSDeleteObject(&targetRef); if(replacingTarget) { (void)FSMoveObject(&asideRef, &targetParentRef, NULL); } throw 0; } else { // The update has succeeded. Clear the cache directory. sendProgress(0, 0, CFSTR("Clearing cache...")); llinfos << "Clearing cache..." << llendl; char mask[LL_MAX_PATH]; /* Flawfinder: ignore */ snprintf(mask, LL_MAX_PATH, "%s*.*", gDirUtilp->getDirDelimiter().c_str()); gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask); llinfos << "Clear complete." << llendl; } } catch(...) { if(!gCancelled) if(gFailure == noErr) gFailure = -1; } // Failures from here on out are all non-fatal and not reported. sendProgress(0, 3, CFSTR("Cleaning up...")); // Close disk image file if necessary if(downloadFile != NULL) { llinfos << "Closing download file." << llendl; fclose(downloadFile); downloadFile = NULL; } sendProgress(1, 3); // Unmount image if(deviceNode[0] != 0) { llinfos << "Detaching disk image." << llendl; snprintf(temp, sizeof(temp), "hdiutil detach '%s'", deviceNode); system(temp); /* Flawfinder: ignore */ } sendProgress(2, 3); // Move work directory to the trash if(tempDir[0] != 0) { // chdir("/"); // FSDeleteObjects(tempDirRef); llinfos << "Moving work directory to the trash." << llendl; err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL); if(err != noErr) { llwarns << "failed to move files to trash, (error code " << err << ")" << llendl; } // snprintf(temp, sizeof(temp), "rm -rf '%s'", tempDir); // printf("%s\n", temp); // system(temp); } if(!gCancelled && !gFailure && (target[0] != 0)) { llinfos << "Touching application bundle." << llendl; snprintf(temp, sizeof(temp), "touch '%s'", target); system(temp); /* Flawfinder: ignore */ llinfos << "Launching updated application." << llendl; snprintf(temp, sizeof(temp), "open '%s'", target); system(temp); /* Flawfinder: ignore */ } sendDone(); return(NULL); }
OSErr FSExchangeObjectsEmulate(const FSRef *sourceRef, const FSRef *destRef, FSRef *newSourceRef, FSRef *newDestRef) { enum { /* get all settable info except for mod dates, plus the volume refNum and parent directory ID */ kGetCatInformationMask = (kFSCatInfoSettableInfo | kFSCatInfoVolume | kFSCatInfoParentDirID) & ~(kFSCatInfoContentMod | kFSCatInfoAttrMod), /* set everything possible except for mod dates */ kSetCatinformationMask = kFSCatInfoSettableInfo & ~(kFSCatInfoContentMod | kFSCatInfoAttrMod) }; OSErr result; FSCatalogInfo sourceCatalogInfo; /* source file's catalog information */ FSCatalogInfo destCatalogInfo; /* destination file's catalog information */ HFSUniStr255 sourceName; /* source file's Unicode name */ HFSUniStr255 destName; /* destination file's Unicode name */ FSRef sourceCurrentRef; /* FSRef to current location of source file throughout this function */ FSRef destCurrentRef; /* FSRef to current location of destination file throughout this function */ FSRef sourceParentRef; /* FSRef to parent directory of source file */ FSRef destParentRef; /* FSRef to parent directory of destination file */ HFSUniStr255 sourceUniqueName; /* unique name given to source file while exchanging it with destination */ HFSUniStr255 destUniqueName; /* unique name given to destination file while exchanging it with source */ long theSeed; /* the seed for generating unique names */ Boolean sameParentDirs; /* true if source and destinatin parent directory is the same */ /* check parameters */ require_action((NULL != newSourceRef) && (NULL != newDestRef), BadParameter, result = paramErr); /* output refs and current refs = input refs to start with */ memcpy(newSourceRef, sourceRef, sizeof(FSRef)); memcpy(&sourceCurrentRef, sourceRef, sizeof(FSRef)); memcpy(newDestRef, destRef, sizeof(FSRef)); memcpy(&destCurrentRef, destRef, sizeof(FSRef)); /* Note: The compatibility case won't work for files with *Btree control blocks. */ /* Right now the only *Btree files are created by the system. */ /* get all catalog information and Unicode names for each file */ result = FSGetCatalogInfo(&sourceCurrentRef, kGetCatInformationMask, &sourceCatalogInfo, &sourceName, NULL, &sourceParentRef); require_noerr(result, SourceFSGetCatalogInfoFailed); result = FSGetCatalogInfo(&destCurrentRef, kGetCatInformationMask, &destCatalogInfo, &destName, NULL, &destParentRef); require_noerr(result, DestFSGetCatalogInfoFailed); /* make sure source and destination are on same volume */ require_action(sourceCatalogInfo.volume == destCatalogInfo.volume, NotSameVolume, result = diffVolErr); /* make sure both files are *really* files */ require_action((0 == (sourceCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) && (0 == (destCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)), NotAFile, result = notAFileErr); /* generate 2 names that are unique in both directories */ theSeed = 0x4a696d4c; /* a fine unlikely filename */ result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &sourceUniqueName); require_noerr(result, GenerateUniqueHFSUniStr1Failed); result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &destUniqueName); require_noerr(result, GenerateUniqueHFSUniStr2Failed); /* rename sourceCurrentRef to sourceUniqueName */ result = FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef); require_noerr(result, FSRenameUnicode1Failed); memcpy(&sourceCurrentRef, newSourceRef, sizeof(FSRef)); /* rename destCurrentRef to destUniqueName */ result = FSRenameUnicode(&destCurrentRef, destUniqueName.length, destUniqueName.unicode, kTextEncodingUnknown, newDestRef); require_noerr(result, FSRenameUnicode2Failed); memcpy(&destCurrentRef, newDestRef, sizeof(FSRef)); /* are the source and destination parent directories the same? */ sameParentDirs = ( sourceCatalogInfo.parentDirID == destCatalogInfo.parentDirID ); if ( !sameParentDirs ) { /* move source file to dest parent directory */ result = FSMoveObject(&sourceCurrentRef, &destParentRef, newSourceRef); require_noerr(result, FSMoveObject1Failed); memcpy(&sourceCurrentRef, newSourceRef, sizeof(FSRef)); /* move dest file to source parent directory */ result = FSMoveObject(&destCurrentRef, &sourceParentRef, newDestRef); require_noerr(result, FSMoveObject2Failed); memcpy(&destCurrentRef, newDestRef, sizeof(FSRef)); } /* At this point, the files are in their new locations (if they were moved). */ /* The source file is named sourceUniqueName and is in the directory referred to */ /* by destParentRef. The destination file is named destUniqueName and is in the */ /* directory referred to by sourceParentRef. */ /* give source file the dest file's catalog information except for mod dates */ result = FSSetCatalogInfo(&sourceCurrentRef, kSetCatinformationMask, &destCatalogInfo); require_noerr(result, FSSetCatalogInfo1Failed); /* give dest file the source file's catalog information except for mod dates */ result = FSSetCatalogInfo(&destCurrentRef, kSetCatinformationMask, &sourceCatalogInfo); require_noerr(result, FSSetCatalogInfo2Failed); /* rename source file with dest file's name */ result = FSRenameUnicode(&sourceCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newSourceRef); require_noerr(result, FSRenameUnicode3Failed); memcpy(&sourceCurrentRef, newSourceRef, sizeof(FSRef)); /* rename dest file with source file's name */ result = FSRenameUnicode(&destCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newDestRef); require_noerr(result, FSRenameUnicode4Failed); /* we're done with no errors, so swap newSourceRef and newDestRef */ memcpy(newSourceRef, newDestRef, sizeof(FSRef)); memcpy(newDestRef, &sourceCurrentRef, sizeof(FSRef)); return ( result ); /**********************/ /* If there are any failures while emulating FSExchangeObjects, attempt to reverse any steps */ /* already taken. In any case, newSourceRef and newDestRef will refer to the files in whatever */ /* state and location they ended up in so that both files can be found by the calling code. */ FSRenameUnicode4Failed: /* attempt to rename source file to sourceUniqueName */ if ( noErr == FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef) ) { memcpy(&sourceCurrentRef, newSourceRef, sizeof(FSRef)); } FSRenameUnicode3Failed: /* attempt to restore dest file's catalog information */ verify_noerr(FSSetCatalogInfo(&destCurrentRef, kFSCatInfoSettableInfo, &destCatalogInfo)); FSSetCatalogInfo2Failed: /* attempt to restore source file's catalog information */ verify_noerr(FSSetCatalogInfo(&sourceCurrentRef, kFSCatInfoSettableInfo, &sourceCatalogInfo)); FSSetCatalogInfo1Failed: if ( !sameParentDirs ) { /* attempt to move dest file back to dest directory */ if ( noErr == FSMoveObject(&destCurrentRef, &destParentRef, newDestRef) ) { memcpy(&destCurrentRef, newDestRef, sizeof(FSRef)); } } FSMoveObject2Failed: if ( !sameParentDirs ) { /* attempt to move source file back to source directory */ if ( noErr == FSMoveObject(&sourceCurrentRef, &sourceParentRef, newSourceRef) ) { memcpy(&sourceCurrentRef, newSourceRef, sizeof(FSRef)); } } FSMoveObject1Failed: /* attempt to rename dest file to original name */ verify_noerr(FSRenameUnicode(&destCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newDestRef)); FSRenameUnicode2Failed: /* attempt to rename source file to original name */ verify_noerr(FSRenameUnicode(&sourceCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newSourceRef)); FSRenameUnicode1Failed: GenerateUniqueHFSUniStr2Failed: GenerateUniqueHFSUniStr1Failed: NotAFile: NotSameVolume: DestFSGetCatalogInfoFailed: SourceFSGetCatalogInfoFailed: BadParameter: return ( result ); }