char * Unicode_ReplaceRange(const char *dest, // IN: UnicodeIndex destStart, // IN: UnicodeIndex destLength, // IN: const char *src, // IN: UnicodeIndex srcStart, // IN: UnicodeIndex srcLength) // IN: { char *result; char *stringOne; char *stringTwo; char *stringThree; ASSERT(dest); ASSERT((destStart >= 0) || (destStart == -1)); ASSERT((destLength >= 0) || (destLength == -1)); ASSERT(src); ASSERT((srcStart >= 0) || (srcStart == -1)); ASSERT((srcLength >= 0) || (srcLength == -1)); stringOne = Unicode_Substr(dest, 0, destStart); stringTwo = Unicode_Substr(src, srcStart, srcLength); stringThree = Unicode_Substr(dest, destStart + destLength, -1); result = Unicode_Join(stringOne, stringTwo, stringThree, NULL); free(stringOne); free(stringTwo); free(stringThree); return result; }
static int RemoveLockingFile(ConstUnicode lockDir, // IN: ConstUnicode fileName) // IN: { int err; Unicode path; ASSERT(lockDir); ASSERT(fileName); path = Unicode_Join(lockDir, DIRSEPS, fileName, NULL); err = FileDeletionRobust(path, FALSE); if (err != 0) { if (err == ENOENT) { /* Not there anymore; locker unlocked or timed out */ err = 0; } else { Warning(LGPFX" %s of '%s' failed: %s\n", __FUNCTION__, UTF8(path), strerror(err)); } } Unicode_Free(path); return err; }
Unicode File_PathJoin(ConstUnicode dirName, // IN: ConstUnicode baseName) // IN: See above. { Unicode result; Unicode newDir = NULL; ASSERT(dirName); ASSERT(baseName); /* * Remove ALL directory separators from baseName begin. */ #if defined(_WIN32) { ConstUnicode oldBaseName = baseName; /* * Reject drive letters in baseName. */ ASSERT(Unicode_LengthInCodePoints(baseName) < 2 || Unicode_FindSubstrInRange(baseName, 1, 1, ":", 0, 1) == UNICODE_INDEX_NOT_FOUND); while (*baseName == '/' || *baseName == '\\') { baseName++; } /* * Reject UNC paths for baseName. */ ASSERT(baseName - oldBaseName < 2); } #else while (*baseName == '/') { baseName++; } #endif /* * Remove ALL directory separators from dirName end. */ newDir = File_StripSlashes(dirName); result = Unicode_Join(newDir, DIRSEPS, baseName, NULL); Unicode_Free(newDir); return result; }
static char * FileFindExistingSafeTmpDir(uid_t userId, // IN: const char *userName, // IN: const char *baseTmpDir) // IN: { int i; int numFiles; char *pattern; char *tmpDir = NULL; char **fileList = NULL; /* * We always use the pattern PRODUCT-USER-xxxx when creating * alternative safe temp directories, so check for ones with * those names and the appropriate permissions. */ pattern = Unicode_Format("%s-%s-", PRODUCT_GENERIC_NAME_LOWER, userName); if (pattern == NULL) { return NULL; } numFiles = File_ListDirectory(baseTmpDir, &fileList); if (numFiles == -1) { free(pattern); return NULL; } for (i = 0; i < numFiles; i++) { if (Unicode_StartsWith(fileList[i], pattern)) { char *path = Unicode_Join(baseTmpDir, DIRSEPS, fileList[i], NULL); if (File_IsDirectory(path) && FileAcceptableSafeTmpDir(path, userId)) { tmpDir = path; break; } free(path); } } Util_FreeStringList(fileList, numFiles); free(pattern); return tmpDir; }
Unicode FileIO_AtomicTempPath(ConstUnicode path) // IN: { Unicode srcPath; Unicode retPath; srcPath = File_FullPath(path); if (!srcPath) { Log("%s: File_FullPath of '%s' failed.\n", __FUNCTION__, path); return NULL; } retPath = Unicode_Join(srcPath, "~", NULL); Unicode_Free(srcPath); return retPath; }
char * FileIO_AtomicTempPath(const char *path) // IN: { char *srcPath; char *retPath; srcPath = File_FullPath(path); if (!srcPath) { Log("%s: File_FullPath of '%s' failed.\n", __FUNCTION__, path); return NULL; } retPath = Unicode_Join(srcPath, "~", NULL); free(srcPath); return retPath; }
Unicode File_ReplaceExtension(ConstUnicode pathName, // IN: ConstUnicode newExtension, // IN: uint32 numExtensions, // IN: ...) // IN: { Unicode path; Unicode base; Unicode result; va_list arguments; UnicodeIndex index; ASSERT(pathName); ASSERT(newExtension); ASSERT(Unicode_StartsWith(newExtension, ".")); File_GetPathName(pathName, &path, &base); index = Unicode_FindLast(base, "."); if (index != UNICODE_INDEX_NOT_FOUND) { Unicode oldBase = base; if (numExtensions) { uint32 i; /* * Only truncate the old extension from the base if it exists in * in the valid extensions list. */ va_start(arguments, numExtensions); for (i = 0; i < numExtensions ; i++) { Unicode oldExtension = va_arg(arguments, Unicode); ASSERT(Unicode_StartsWith(oldExtension, ".")); if (Unicode_CompareRange(base, index, -1, oldExtension, 0, -1, FALSE) == 0) { base = Unicode_Truncate(oldBase, index); // remove '.' break; } } va_end(arguments); } else { /* Always truncate the old extension if extension list is empty . */ base = Unicode_Truncate(oldBase, index); // remove '.' } if (oldBase != base) { Unicode_Free(oldBase); } } if (Unicode_IsEmpty(path)) { result = Unicode_Append(base, newExtension); } else { result = Unicode_Join(path, DIRSEPS, base, newExtension, NULL); } Unicode_Free(path); Unicode_Free(base); return result; }
Unicode File_StripSlashes(ConstUnicode path) // IN: { Unicode result, volume, dir, base; /* * SplitName handles all drive letter/UNC/whatever cases, all we * have to do is make sure the dir part is stripped of slashes if * there isn't a base part. */ File_SplitName(path, &volume, &dir, &base); if (!Unicode_IsEmpty(dir) && Unicode_IsEmpty(base)) { char *dir2 = Unicode_GetAllocBytes(dir, STRING_ENCODING_UTF8); size_t i = strlen(dir2); /* * Don't strip first slash on Windows, since we want at least * one slash to trail a drive letter/colon or UNC specifier. */ #if defined(_WIN32) while ((i > 1) && (('/' == dir2[i - 1]) || ('\\' == dir2[i - 1]))) { #else while ((i > 0) && ('/' == dir2[i - 1])) { #endif i--; } Unicode_Free(dir); dir = Unicode_AllocWithLength(dir2, i, STRING_ENCODING_UTF8); free(dir2); } result = Unicode_Join(volume, dir, base, NULL); Unicode_Free(volume); Unicode_Free(dir); Unicode_Free(base); return result; } /* *----------------------------------------------------------------------------- * * File_MapPathPrefix -- * * Given a path and a newPrefix -> oldPrefix mapping, transform * oldPath according to the mapping. * * Results: * The new path, or NULL if there is no mapping. * * Side effects: * The returned string is allocated, free it. * *----------------------------------------------------------------------------- */ char * File_MapPathPrefix(const char *oldPath, // IN: const char **oldPrefixes, // IN: const char **newPrefixes, // IN: size_t numPrefixes) // IN: { int i; size_t oldPathLen = strlen(oldPath); for (i = 0; i < numPrefixes; i++) { char *newPath; char *oldPrefix; char *newPrefix; size_t oldPrefixLen; oldPrefix = File_StripSlashes(oldPrefixes[i]); newPrefix = File_StripSlashes(newPrefixes[i]); oldPrefixLen = strlen(oldPrefix); /* * If the prefix matches on a DIRSEPS boundary, or the prefix is the * whole string, replace it. * * If we don't insist on matching a whole directory name, we could * mess things of if one directory is a substring of another. * * Perform a case-insensitive compare on Windows. (There are * case-insensitive filesystems on MacOS also, but the problem * is more acute with Windows because of frequent drive-letter * case mismatches. So in lieu of actually asking the * filesystem, let's just go with a simple ifdef for now.) */ if ((oldPathLen >= oldPrefixLen) && #ifdef _WIN32 (Str_Strncasecmp(oldPath, oldPrefix, oldPrefixLen) == 0) && #else (Str_Strncmp(oldPath, oldPrefix, oldPrefixLen) == 0) && #endif (strchr(VALID_DIRSEPS, oldPath[oldPrefixLen]) || (oldPath[oldPrefixLen] == '\0'))) { size_t newPrefixLen = strlen(newPrefix); size_t newPathLen = (oldPathLen - oldPrefixLen) + newPrefixLen; ASSERT(newPathLen > 0); ASSERT(oldPathLen >= oldPrefixLen); newPath = Util_SafeMalloc((newPathLen + 1) * sizeof(char)); memcpy(newPath, newPrefix, newPrefixLen); memcpy(newPath + newPrefixLen, oldPath + oldPrefixLen, oldPathLen - oldPrefixLen + 1); /* * It should only match once. Weird self-referencing mappings * aren't allowed. */ free(oldPrefix); free(newPrefix); return newPath; } free(oldPrefix); free(newPrefix); } return NULL; }
static int Scanner(ConstUnicode lockDir, // IN: int (*func)( // IN: ConstUnicode lockDir, ConstUnicode fileName, LockValues *memberValues, LockValues *myValues ), LockValues *myValues, // IN: Bool cleanUp) // IN: { int err; ActiveLock *ptr; ASSERT(lockDir); myValues->lockList = NULL; while (TRUE) { ActiveLock *prev; err = ScanDirectory(lockDir, func, myValues, cleanUp); if ((err > 0) || ((err == 0) && (myValues->lockList == NULL))) { break; } prev = NULL; ptr = myValues->lockList; /* * Some 'D' entries have persisted. Age them and remove those that * have not progressed. Remove those that have disappeared. */ while (ptr != NULL) { Bool remove; if (ptr->marked) { if (ptr->age > FILELOCK_PROGRESS_DEARTH) { Unicode temp; Unicode path; UnicodeIndex index; ASSERT(Unicode_StartsWith(ptr->dirName, "D")); Log(LGPFX" %s discarding %s data from '%s'.\n", __FUNCTION__, UTF8(ptr->dirName), UTF8(lockDir)); path = Unicode_Join(lockDir, DIRSEPS, ptr->dirName, NULL); index = Unicode_FindLast(path, "D"); ASSERT(index != UNICODE_INDEX_NOT_FOUND); temp = Unicode_Replace(path, index, 1, "M"); FileDeletionRobust(temp, FALSE); Unicode_Free(temp); temp = Unicode_Replace(path, index, 1, "E"); FileDeletionRobust(temp, FALSE); Unicode_Free(temp); FileRemoveDirectoryRobust(path); Unicode_Free(path); remove = TRUE; } else { ptr->marked = FALSE; ptr->age += FILELOCK_PROGRESS_SAMPLE; remove = FALSE; } } else { remove = TRUE; } if (remove) { if (prev == NULL) { myValues->lockList = ptr->next; } else { prev->next = ptr->next; } } prev = ptr; ptr = ptr->next; } FileSleeper(FILELOCK_PROGRESS_SAMPLE); // relax } /* Clean up anything still on the list; they are no longer important */ while (myValues->lockList != NULL) { ptr = myValues->lockList; myValues->lockList = ptr->next; Unicode_Free(ptr->dirName); free(ptr); } return err; }
int FileLockMemberValues(ConstUnicode lockDir, // IN: ConstUnicode fileName, // IN: char *buffer, // OUT: uint32 requiredSize, // IN: LockValues *memberValues) // OUT: { #ifndef __MINGW32__ uint32 argc = 0; FILELOCK_FILE_HANDLE handle; uint32 len; char *argv[FL_MAX_ARGS]; char *saveptr = NULL; int err; Unicode path; FileData fileData; ParseTable table = { PARSE_TABLE_STRING, "lc", (void *) &memberValues->locationChecksum }; ASSERT(lockDir); ASSERT(fileName); path = Unicode_Join(lockDir, DIRSEPS, fileName, NULL); err = FileLockOpenFile(path, O_RDONLY, &handle); if (err != 0) { /* * A member file may "disappear" if is deleted due to an unlock * immediately after a directory scan but before the scan is processed. * Since this is a "normal" thing ENOENT will be suppressed. */ if (err != ENOENT) { Warning(LGPFX" %s open failure on '%s': %s\n", __FUNCTION__, UTF8(path), strerror(err)); } goto bail; } /* Attempt to obtain the lock file attributes now that it is opened */ err = FileAttributesRobust(path, &fileData); if (err != 0) { Warning(LGPFX" %s file size failure on '%s': %s\n", __FUNCTION__, UTF8(path), strerror(err)); FileLockCloseFile(handle); goto bail; } /* Complain if the lock file is not the proper size */ if (fileData.fileSize != requiredSize) { Warning(LGPFX" %s file '%s': size %"FMT64"u, required size %u\n", __FUNCTION__, UTF8(path), fileData.fileSize, requiredSize); FileLockCloseFile(handle); goto corrupt; } /* Attempt to read the lock file data and validate how much was read. */ err = FileLockReadFile(handle, buffer, requiredSize, &len); FileLockCloseFile(handle); if (err != 0) { Warning(LGPFX" %s read failure on '%s': %s\n", __FUNCTION__, UTF8(path), strerror(err)); goto bail; } if (len != requiredSize) { Warning(LGPFX" %s read length issue on '%s': %u and %u\n", __FUNCTION__, UTF8(path), len, requiredSize); err = EIO; goto bail; } /* Extract and validate the lock file data. */ for (argc = 0; argc < FL_MAX_ARGS; argc++) { argv[argc] = strtok_r((argc == 0) ? buffer : NULL, " ", &saveptr); if (argv[argc] == NULL) { break; } } if ((argc < 4) || ((argc == FL_MAX_ARGS) && (strtok_r(NULL, " ", &saveptr) != NULL))) { goto corrupt; } /* * Lock file arguments are space separated. There is a minimum of 4 * arguments - machineID, executionID, Lamport number and lock type. * The maximum number of arguments is FL_MAX_ARGS. * * The fifth argument, if present, is the payload or "[" if there is no * payload and additional arguments are present. The additional arguments * form a properly list - one or more "name=value" pairs. * * Here is picture of valid forms: * * 0 1 2 3 4 5 6 Comment *------------------------- * A B C D contents, no payload, no list entries * A B C D [ contents, no payload, no list entries * A B C D P contents, a payload, no list entries * A B C D [ x contents, no payload, one list entry * A B C D P x contents, a payload, one list entry * A B C D [ x y contents, no payload, two list entries * A B C D P x y contents, a payload, two list entries */ memberValues->locationChecksum = NULL; if (argc == 4) { memberValues->payload = NULL; } else { if (strcmp(argv[4], "[") == 0) { memberValues->payload = NULL; } else { memberValues->payload = argv[4]; } if (FileLockParseArgs(argv, argc - 5, &table, 1)) { goto corrupt; } } if (sscanf(argv[2], "%u", &memberValues->lamportNumber) != 1) { goto corrupt; } if ((strcmp(argv[3], LOCK_SHARED) != 0) && (strcmp(argv[3], LOCK_EXCLUSIVE) != 0)) { goto corrupt; } memberValues->machineID = argv[0]; memberValues->executionID = argv[1]; memberValues->lockType = argv[3]; memberValues->memberName = Unicode_Duplicate(fileName); Unicode_Free(path); return 0; corrupt: Warning(LGPFX" %s removing problematic lock file '%s'\n", __FUNCTION__, UTF8(path)); if (argc) { Log(LGPFX" %s '%s' contents are:\n", __FUNCTION__, UTF8(fileName)); for (len = 0; len < argc; len++) { Log(LGPFX" %s %s argv[%d]: '%s'\n", __FUNCTION__, UTF8(fileName), len, argv[len]); } } /* Remove the lock file and behave like it has disappeared */ err = FileDeletionRobust(path, FALSE); if (err == 0) { err = ENOENT; } bail: Unicode_Free(path); return err; #else // XXX - incomplete: needs a win/mingw32 implementation, if required. NOT_IMPLEMENTED(); return 0; #endif }
static int CreateEntryDirectory(const char *machineID, // IN: const char *executionID, // IN: ConstUnicode lockDir, // IN: Unicode *entryDirectory, // OUT: Unicode *entryFilePath, // OUT: Unicode *memberFilePath, // OUT: Unicode *memberName) // OUT: { int err = 0; uint32 randomNumber = 0; ASSERT(lockDir); *entryDirectory = NULL; *entryFilePath = NULL; *memberFilePath = NULL; *memberName = NULL; /* Fun at the races */ while (TRUE) { Unicode temp; FileData fileData; err = FileAttributesRobust(lockDir, &fileData); if (err == 0) { /* The name exists. Deal with it... */ if (fileData.fileType == FILE_TYPE_REGULAR) { /* * It's a file. Assume this is an (active?) old style lock and * err on the safe side - don't remove it (and automatically * upgrade to a new style lock). */ Log(LGPFX" %s: '%s' exists; an old style lock file?\n", __FUNCTION__, UTF8(lockDir)); err = EAGAIN; break; } if (fileData.fileType != FILE_TYPE_DIRECTORY) { /* Not a directory; attempt to remove the debris */ if (FileDeletionRobust(lockDir, FALSE) != 0) { Warning(LGPFX" %s: '%s' exists and is not a directory.\n", __FUNCTION__, UTF8(lockDir)); err = ENOTDIR; break; } continue; } } else { if (err == ENOENT) { /* Not there anymore; locker unlocked or timed out */ err = MakeDirectory(lockDir); if ((err != 0) && (err != EEXIST)) { Warning(LGPFX" %s creation failure on '%s': %s\n", __FUNCTION__, UTF8(lockDir), strerror(err)); break; } } else { Warning(LGPFX" %s stat failure on '%s': %s\n", __FUNCTION__, UTF8(lockDir), strerror(err)); break; } } /* There is a small chance of collision/failure; grab stings now */ randomNumber = SimpleRandomNumber(machineID, executionID); *memberName = Unicode_Format("M%05u%s", randomNumber, FILELOCK_SUFFIX); temp = Unicode_Format("D%05u%s", randomNumber, FILELOCK_SUFFIX); *entryDirectory = Unicode_Join(lockDir, DIRSEPS, temp, NULL); Unicode_Free(temp); temp = Unicode_Format("E%05u%s", randomNumber, FILELOCK_SUFFIX); *entryFilePath = Unicode_Join(lockDir, DIRSEPS, temp, NULL); Unicode_Free(temp); *memberFilePath = Unicode_Join(lockDir, DIRSEPS, *memberName, NULL); err = MakeDirectory(*entryDirectory); if (err == 0) { /* * The entry directory was safely created. See if a member file * is in use (the entry directory is removed once the member file * is created). If a member file is in use, choose another number, * otherwise the use of the this number is OK. * * Err on the side of caution... don't want to trash perfectly * good member files. */ err = FileAttributesRobust(*memberFilePath, NULL); if (err != 0) { if (err == ENOENT) { err = 0; break; } if (vmx86_debug) { Log(LGPFX" %s stat failure on '%s': %s\n", __FUNCTION__, UTF8(*memberFilePath), strerror(err)); } } FileRemoveDirectoryRobust(*entryDirectory); } else { if ((err != EEXIST) && // Another process/thread created it... (err != ENOENT)) { // lockDir is gone... Warning(LGPFX" %s creation failure on '%s': %s\n", __FUNCTION__, UTF8(*entryDirectory), strerror(err)); break; } } Unicode_Free(*entryDirectory); Unicode_Free(*entryFilePath); Unicode_Free(*memberFilePath); Unicode_Free(*memberName); *entryDirectory = NULL; *entryFilePath = NULL; *memberFilePath = NULL; *memberName = NULL; } if (err != 0) { Unicode_Free(*entryDirectory); Unicode_Free(*entryFilePath); Unicode_Free(*memberFilePath); Unicode_Free(*memberName); *entryDirectory = NULL; *entryFilePath = NULL; *memberFilePath = NULL; *memberName = NULL; } return err; }
static int WaitForPossession(ConstUnicode lockDir, // IN: ConstUnicode fileName, // IN: LockValues *memberValues, // IN: LockValues *myValues) // IN: { int err = 0; ASSERT(lockDir); ASSERT(fileName); /* "Win" or wait? */ if (((memberValues->lamportNumber < myValues->lamportNumber) || ((memberValues->lamportNumber == myValues->lamportNumber) && (Unicode_Compare(memberValues->memberName, myValues->memberName) < 0))) && ((strcmp(memberValues->lockType, LOCK_EXCLUSIVE) == 0) || (strcmp(myValues->lockType, LOCK_EXCLUSIVE) == 0))) { Unicode path; uint32 loopCount; Bool thisMachine; thisMachine = FileLockMachineIDMatch(myValues->machineID, memberValues->machineID); loopCount = 0; path = Unicode_Join(lockDir, DIRSEPS, fileName, NULL); while ((err = Sleeper(myValues, &loopCount)) == 0) { /* still there? */ err = FileAttributesRobust(path, NULL); if (err != 0) { if (err == ENOENT) { /* Not there anymore; locker unlocked or timed out */ err = 0; } break; } /* still valid? */ if (thisMachine && !FileLockValidOwner(memberValues->executionID, memberValues->payload)) { /* Invalid Execution ID; remove the member file */ Warning(LGPFX" %s discarding file '%s'; invalid executionID.\n", __FUNCTION__, UTF8(path)); err = RemoveLockingFile(lockDir, fileName); break; } } /* * Log the disposition of each timeout for all non "try lock" locking * attempts. This can assist in debugging locking problems. */ if ((myValues->msecMaxWaitTime != FILELOCK_TRYLOCK_WAIT) && (err == EAGAIN)) { if (thisMachine) { Log(LGPFX" %s timeout on '%s' due to a local process (%s)\n", __FUNCTION__, UTF8(path), memberValues->executionID); } else { Log(LGPFX" %s timeout on '%s' due to another machine (%s)\n", __FUNCTION__, UTF8(path), memberValues->machineID); } } Unicode_Free(path); } return err; }
int File_MakeTempEx2(ConstUnicode dir, // IN: Bool createTempFile, // IN: File_MakeTempCreateNameFunc *createNameFunc, // IN: void *createNameFuncData, // IN: Unicode *presult) // OUT: { uint32 i; int fd = -1; uint32 var = 0; Unicode path = NULL; if ((dir == NULL) || (createNameFunc == NULL)) { errno = EFAULT; return -1; } ASSERT(presult); *presult = NULL; for (i = 0; i < (MAX_INT32 / 2); i++) { Unicode fileName; /* construct suffixed pathname to use */ Unicode_Free(path); path = NULL; /* * Files and directories are kept separate (odd and even respectfully). * This way the available exclusion mechanisms work properly - O_EXCL * on files, mkdir on directories - and races are avoided. * * Not attempting an open on a directory is a good thing... */ FileTempNum(createTempFile, &var); fileName = (*createNameFunc)(var, createNameFuncData); ASSERT(fileName); /* construct base full pathname to use */ path = Unicode_Join(dir, DIRSEPS, fileName, NULL); Unicode_Free(fileName); if (createTempFile) { fd = Posix_Open(path, O_CREAT | O_EXCL | O_BINARY | O_RDWR, 0600); } else { fd = Posix_Mkdir(path, 0700); } if (fd != -1) { *presult = path; path = NULL; break; } if (errno != EEXIST) { Log(LGPFX" Failed to create temporary %s \"%s\": %s.\n", createTempFile ? "file" : "directory", UTF8(path), strerror(errno)); goto exit; } } if (fd == -1) { Warning(LGPFX" Failed to create temporary %s \"%s\": " "The name space is full.\n", createTempFile ? "file" : "directory", UTF8(path)); errno = EAGAIN; } exit: Unicode_Free(path); return fd; }