int File_MakeSafeTemp(ConstUnicode tag, // IN (OPT): Unicode *presult) // OUT: { int fd; Unicode dir = NULL; Unicode fileName = NULL; ASSERT(presult); *presult = NULL; if (tag && File_IsFullPath(tag)) { File_GetPathName(tag, &dir, &fileName); } else { dir = File_GetSafeTmpDir(TRUE); fileName = Unicode_Duplicate(tag ? tag : "vmware"); } fd = File_MakeTempEx(dir, fileName, presult); Unicode_Free(dir); Unicode_Free(fileName); return fd; }
int FileUnlockIntrinsic(ConstUnicode pathName, // IN: const void *lockToken) // IN: { int err; ASSERT(pathName); ASSERT(lockToken); LOG(1, ("Requesting unlock on %s\n", UTF8(pathName))); if (lockToken == &implicitReadToken) { /* * The lock token is the fixed-address implicit read lock token. * Since no lock file was created no further action is required. */ err = 0; } else { Unicode lockDir; /* The lock directory path */ lockDir = Unicode_Append(pathName, FILELOCK_SUFFIX); /* * The lock token is the (unicode) path of the lock file. * * TODO: under vmx86_debug validate the contents of the lock file as * matching the machineID and executionID. */ err = FileDeletionRobust((Unicode) lockToken, FALSE); if (err && vmx86_debug) { Log(LGPFX" %s failed for '%s': %s\n", __FUNCTION__, (char *) lockToken, strerror(err)); } /* * The lockToken (a unicode path) was allocated in FileLockIntrinsic * and returned to the caller hidden behind a "void *" pointer. */ Unicode_Free((Unicode) lockToken); FileRemoveDirectoryRobust(lockDir); // just in case we can clean up Unicode_Free(lockDir); } return err; }
void File_GetPathName(ConstUnicode fullPath, // IN: Unicode *pathName, // OUT (OPT): Unicode *baseName) // OUT (OPT): { Unicode volume; UnicodeIndex len; UnicodeIndex curLen; File_SplitName(fullPath, &volume, pathName, baseName); if (pathName == NULL) { Unicode_Free(volume); return; } /* * The volume component may be empty. */ if (!Unicode_IsEmpty(volume)) { Unicode temp = Unicode_Append(volume, *pathName); Unicode_Free(*pathName); *pathName = temp; } Unicode_Free(volume); /* * Remove any trailing directory separator characters. */ len = Unicode_LengthInCodePoints(*pathName); curLen = len; while ((curLen > 0) && (FileFirstSlashIndex(*pathName, curLen - 1) == curLen - 1)) { curLen--; } if (curLen < len) { Unicode temp = Unicode_Substr(*pathName, 0, curLen); Unicode_Free(*pathName); *pathName = temp; } }
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 Unicode_Join(ConstUnicode first, // IN: ...) // IN { va_list args; Unicode result; ConstUnicode cur; if (first == NULL) { return NULL; } result = Unicode_Duplicate(first); va_start(args, first); while ((cur = va_arg(args, ConstUnicode)) != NULL) { Unicode temp; temp = Unicode_Append(result, cur); Unicode_Free(result); result = temp; } va_end(args); return result; }
static char * FileLockLocationChecksum(ConstUnicode path) // IN: { int c; uint32 hash = 5381; #if defined(_WIN32) char *p; Unicode value = Unicode_Duplicate(path); /* Don't get fooled by mixed case; "normalize" */ Str_ToLower(value); p = value; #else char *p = (char *) path; #endif /* DBJ2 hash... good enough? */ while ((c = *p++)) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } #if defined(_WIN32) Unicode_Free(value); #endif return Str_SafeAsprintf(NULL, "%u", hash); }
Bool FileLockIsLocked(ConstUnicode pathName, // IN: int *err) // OUT: { uint32 i; int errValue; int numEntries; Unicode lockDir; Bool isLocked = FALSE; Unicode *fileList = NULL; lockDir = Unicode_Append(pathName, FILELOCK_SUFFIX); numEntries = FileListDirectoryRobust(lockDir, &fileList); if (numEntries == -1) { errValue = errno; goto bail; } for (i = 0; i < numEntries; i++) { if (Unicode_StartsWith(fileList[i], "M")) { isLocked = TRUE; break; } } for (i = 0; i < numEntries; i++) { Unicode_Free(fileList[i]); } free(fileList); errValue = 0; bail: Unicode_Free(lockDir); if (err != NULL) { *err = errValue; } return isLocked; }
void FileIO_Cleanup(FileIODescriptor *fd) // IN/OUT: { ASSERT(fd); if (fd->fileName) { Unicode_Free(fd->fileName); fd->fileName = NULL; } }
Unicode System_GetTimeAsString(void) { struct timeval tv; time_t sec; int msec; size_t charsWritten; size_t bufSize = 8; // Multiplied by 2 for the initial allocation. char *buf = NULL; Unicode dateTime = NULL; Unicode output = NULL; if (gettimeofday(&tv, NULL)) { goto out; } sec = tv.tv_sec; msec = tv.tv_usec / 1000; /* * Loop repeatedly trying to format the time into a buffer, doubling the * buffer with each failure. This should be safe as the manpage for * strftime(3) seems to suggest that it only fails if the buffer isn't large * enough. * * The resultant string is encoded according to the current locale. */ do { char *newBuf; bufSize *= 2; newBuf = realloc(buf, bufSize); if (newBuf == NULL) { goto out; } buf = newBuf; charsWritten = strftime(buf, bufSize, "%b %d %H:%M:%S", localtime(&sec)); } while (charsWritten == 0); /* * Append the milliseconds field, but only after converting the date/time * string from encoding specified in the current locale to an opaque type. */ dateTime = Unicode_Alloc(buf, STRING_ENCODING_DEFAULT); if (dateTime == NULL) { goto out; } output = Unicode_Format("%s.%03d", dateTime, msec); out: free(buf); Unicode_Free(dateTime); return output; }
Bool File_DoesVolumeSupportAcls(ConstUnicode path) // IN: { Bool succeeded = FALSE; #if defined(_WIN32) Bool res; Unicode vol, vol2; const utf16_t *vol2W; DWORD fsFlags; ASSERT(path); File_SplitName(path, &vol, NULL, NULL); vol2 = Unicode_Append(vol, DIRSEPS); vol2W = UNICODE_GET_UTF16(vol2); res = GetVolumeInformationW(vol2W, NULL, 0, NULL, NULL, &fsFlags, NULL, 0); UNICODE_RELEASE_UTF16(vol2W); if (res) { if ((fsFlags & FS_PERSISTENT_ACLS) == 0) { goto exit; } } else { Log(LGPFX" %s: GetVolumeInformation failed: %d\n", __FUNCTION__, GetLastError()); goto exit; } succeeded = TRUE; exit: Unicode_Free(vol); Unicode_Free(vol2); #endif return succeeded; }
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; }
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; }
Unicode File_MakeSafeTempDir(ConstUnicode prefix) // IN: { Unicode result = NULL; Unicode dir = File_GetSafeTmpDir(TRUE); if (dir != NULL) { ConstUnicode effectivePrefix = (prefix == NULL) ? "safeDir" : prefix; File_MakeTempEx2(dir, FALSE, FileMakeTempExCreateNameFunc, (void *) effectivePrefix, &result); Unicode_Free(dir); } return result; }
ConstUnicode Unicode_GetStatic(const char *asciiBytes, // IN Bool unescape) // IN { Unicode result = NULL; HashTable *stringTable; if (unescape) { stringTable = HashTable_AllocOnce(&UnicodeUnescapedStringTable, 4096, HASH_FLAG_ATOMIC | HASH_STRING_KEY, UnicodeHashFree); } else { stringTable = HashTable_AllocOnce(&UnicodeStringTable, 4096, HASH_FLAG_ATOMIC | HASH_STRING_KEY, UnicodeHashFree); } /* * Attempt a lookup for the key value; if it is found things are easy and * fine. Otherwise HashTable_LookupOrInsert is used to attempt to enter * the data in a racey manner. Should multiple threads attempt to enter * the same key concurrently one thread will get the entered data and the * other threads will detect that their entries were rejected; they * discard their copies of the data and use the entered data (values * will be stable). */ if (!HashTable_Lookup(stringTable, asciiBytes, (void **) &result)) { Unicode newData = UnicodeAllocStatic(asciiBytes, unescape); if (newData) { result = HashTable_LookupOrInsert(stringTable, asciiBytes, newData); if (result != newData) { Unicode_Free(newData); } } } return result; }
Bool FileIO_CloseAndUnlink(FileIODescriptor *fd) // IN: { Unicode path; Bool ret; ASSERT(fd); ASSERT(FileIO_IsValid(fd)); path = Unicode_Duplicate(fd->fileName); ret = FileIO_Close(fd); if (!ret) { if (File_UnlinkIfExists(path) == -1) { ret = TRUE; } } Unicode_Free(path); return ret; }
void File_SplitName(ConstUnicode pathName, // IN: Unicode *volume, // OUT (OPT): Unicode *directory, // OUT (OPT): Unicode *base) // OUT (OPT): { Unicode vol; Unicode dir; Unicode bas; UnicodeIndex volEnd; UnicodeIndex length; UnicodeIndex baseBegin; WIN32_ONLY(UnicodeIndex pathLen); ASSERT(pathName); /* * Get volume. */ volEnd = 0; #if defined(_WIN32) pathLen = Unicode_LengthInCodePoints(pathName); if ((pathLen > 2) && (Unicode_StartsWith(pathName, "\\\\") || Unicode_StartsWith(pathName, "//"))) { /* UNC path */ volEnd = FileFirstSlashIndex(pathName, 2); if (volEnd == UNICODE_INDEX_NOT_FOUND) { /* we have \\foo, which is just bogus */ volEnd = 0; } else { volEnd = FileFirstSlashIndex(pathName, volEnd + 1); if (volEnd == UNICODE_INDEX_NOT_FOUND) { /* we have \\foo\bar, which is legal */ volEnd = pathLen; } } } else if ((pathLen >= 2) && (Unicode_FindSubstrInRange(pathName, 1, 1, ":", 0, 1) != UNICODE_INDEX_NOT_FOUND)) { /* drive-letter path */ volEnd = 2; } if (volEnd > 0) { vol = Unicode_Substr(pathName, 0, volEnd); } else { vol = Unicode_Duplicate(""); } #else vol = Unicode_Duplicate(""); #endif /* _WIN32 */ /* * Get base. */ baseBegin = FileLastSlashIndex(pathName, 0); baseBegin = (baseBegin == UNICODE_INDEX_NOT_FOUND) ? 0 : baseBegin + 1; if (baseBegin >= volEnd) { bas = Unicode_Substr(pathName, baseBegin, -1); } else { bas = Unicode_Duplicate(""); } /* * Get dir. */ length = baseBegin - volEnd; if (length > 0) { dir = Unicode_Substr(pathName, volEnd, length); } else { dir = Unicode_Duplicate(""); } /* * Return what needs to be returned. */ if (volume) { *volume = vol; } else { Unicode_Free(vol); } if (directory) { *directory = dir; } else { Unicode_Free(dir); } if (base) { *base = bas; } else { Unicode_Free(bas); } }
int Unicode_CompareRange(ConstUnicode str1, // IN UnicodeIndex str1Start, // IN UnicodeIndex str1Length, // IN ConstUnicode str2, // IN UnicodeIndex str2Start, // IN UnicodeIndex str2Length, // IN Bool ignoreCase) // IN { int result = -1; Unicode substr1 = NULL; Unicode substr2 = NULL; utf16_t *substr1UTF16 = NULL; utf16_t *substr2UTF16 = NULL; UnicodeIndex i = 0; UnicodeIndex utf16Index; utf16_t codeUnit1; utf16_t codeUnit2; uint32 codePoint1; uint32 codePoint2; UnicodePinIndices(str1, &str1Start, &str1Length); UnicodePinIndices(str2, &str2Start, &str2Length); /* * TODO: Allocating substrings is a performance hit. We should do * this search in-place. (However, searching UTF-8 requires tender loving * care, and it's just easier to search UTF-16.) */ substr1 = Unicode_Substr(str1, str1Start, str1Length); if (!substr1) { goto out; } substr2 = Unicode_Substr(str2, str2Start, str2Length); if (!substr2) { goto out; } /* * XXX TODO: Need to normalize the incoming strings to NFC or NFD. */ substr1UTF16 = Unicode_GetAllocUTF16(substr1); if (!substr1UTF16) { goto out; } substr2UTF16 = Unicode_GetAllocUTF16(substr2); if (!substr2UTF16) { goto out; } /* * TODO: This is the naive string search algorithm, which is * O(n * m). We can do better with KMP or Boyer-Moore if this * proves to be a bottleneck. */ while (TRUE) { codeUnit1 = *(substr1UTF16 + i); codeUnit2 = *(substr2UTF16 + i); /* * TODO: Simple case folding doesn't handle the situation where * more than one code unit is needed to store the result of the * case folding. * * This means that German "straBe" (where B = sharp S, U+00DF) * will not match "STRASSE", even though the two strings are the * same. */ if (ignoreCase) { codeUnit1 = UnicodeSimpleCaseFold(codeUnit1); codeUnit2 = UnicodeSimpleCaseFold(codeUnit2); } if (codeUnit1 != codeUnit2) { break; } if (codeUnit1 == 0) { // End of both strings reached: strings are equal. result = 0; goto out; } i++; } /* * The two UTF-16 code units differ. If they're the first code unit * of a surrogate pair (for Unicode values past U+FFFF), decode the * surrogate pair into a full Unicode code point. */ if (U16_IS_SURROGATE(codeUnit1)) { ssize_t substrUTF16Len = Unicode_UTF16Strlen(substr1UTF16); // U16_NEXT modifies the index, so let it work on a copy. utf16Index = i; // Decode the surrogate if needed. U16_NEXT(substr1UTF16, utf16Index, substrUTF16Len, codePoint1); } else { // Not a surrogate? Then the code point value is the code unit. codePoint1 = codeUnit1; } if (U16_IS_SURROGATE(codeUnit2)) { ssize_t substrUTF16Len = Unicode_UTF16Strlen(substr2UTF16); utf16Index = i; U16_NEXT(substr2UTF16, utf16Index, substrUTF16Len, codePoint2); } else { codePoint2 = codeUnit2; } if (codePoint1 < codePoint2) { result = -1; } else if (codePoint1 > codePoint2) { result = 1; } else { // If we hit the end of the string, we've already gone to 'out'. NOT_REACHED(); } out: free(substr1UTF16); free(substr2UTF16); Unicode_Free(substr1); Unicode_Free(substr2); return result; }
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; }
Bool FileIO_AtomicUpdate(FileIODescriptor *newFD, // IN/OUT: file IO descriptor FileIODescriptor *currFD) // IN/OUT: file IO descriptor { char *currPath = NULL; char *newPath = NULL; #if defined(_WIN32) uint32 currAccess; uint32 newAccess; FileIOResult status; FileIODescriptor tmpFD; #else int fd; #endif int savedErrno = 0; Bool ret = FALSE; ASSERT(FileIO_IsValid(newFD)); ASSERT(FileIO_IsValid(currFD)); if (HostType_OSIsVMK()) { #if defined(VMX86_SERVER) FS_SwapFilesArgs *args = NULL; char *dirName = NULL; char *fileName = NULL; char *dstDirName = NULL; char *dstFileName = NULL; currPath = File_FullPath(FileIO_Filename(currFD)); if (!currPath) { savedErrno = errno; Log("%s: File_FullPath of '%s' failed.\n", __FUNCTION__, FileIO_Filename(currFD)); goto swapdone; } newPath = File_FullPath(FileIO_Filename(newFD)); if (!newPath) { savedErrno = errno; Log("%s: File_FullPath of '%s' failed.\n", __FUNCTION__, FileIO_Filename(newFD)); goto swapdone; } File_GetPathName(newPath, &dirName, &fileName); File_GetPathName(currPath, &dstDirName, &dstFileName); ASSERT(dirName && *dirName); ASSERT(fileName && *fileName); ASSERT(dstDirName && *dstDirName); ASSERT(dstFileName && *dstFileName); ASSERT(!strcmp(dirName, dstDirName)); args = (FS_SwapFilesArgs *) Util_SafeCalloc(1, sizeof(*args)); if (Str_Snprintf(args->srcFile, sizeof(args->srcFile), "%s", fileName) < 0) { Log("%s: Path too long \"%s\".\n", __FUNCTION__, fileName); savedErrno = ENAMETOOLONG; goto swapdone; } if (Str_Snprintf(args->dstFilePath, sizeof(args->dstFilePath), "%s/%s", dstDirName, dstFileName) < 0) { Log("%s: Path too long \"%s\".\n", __FUNCTION__, dstFileName); savedErrno = ENAMETOOLONG; goto swapdone; } /* * Issue the ioctl on the directory rather than on the file, * because the file could be open. */ fd = Posix_Open(dirName, O_RDONLY); if (fd < 0) { Log("%s: Open failed \"%s\" %d.\n", __FUNCTION__, dirName, errno); ASSERT(errno != EBUSY); /* #615124. */ savedErrno = errno; goto swapdone; } if (ioctl(fd, IOCTLCMD_VMFS_SWAP_FILES, args) != 0) { savedErrno = errno; if (errno != ENOSYS && errno != ENOTTY) { Log("%s: ioctl failed %d.\n", __FUNCTION__, errno); ASSERT(errno != EBUSY); /* #615124. */ } } else { ret = TRUE; } close(fd); /* * Did we fail because we are on a file system that does not * support the IOCTLCMD_VMFS_SWAP_FILES ioctl? If so fallback to * using rename. * * Check for both ENOSYS and ENOTTY. PR 957695 */ if (savedErrno == ENOSYS || savedErrno == ENOTTY) { /* * NFS allows renames of locked files, even if both files * are locked. The file lock follows the file handle, not * the name, so after the rename we can swap the underlying * file descriptors instead of closing and reopening the * target file. * * This is different than the hosted path below because * ESX uses native file locks and hosted does not. * * We assume that all ESX file systems that support rename * have the same file lock semantics as NFS. */ if (File_Rename(newPath, currPath)) { Log("%s: rename of '%s' to '%s' failed %d.\n", __FUNCTION__, newPath, currPath, errno); savedErrno = errno; goto swapdone; } ret = TRUE; fd = newFD->posix; newFD->posix = currFD->posix; currFD->posix = fd; FileIO_Close(newFD); } swapdone: free(args); free(dirName); free(fileName); free(dstDirName); free(dstFileName); free(currPath); free(newPath); errno = savedErrno; return ret; #else NOT_REACHED(); #endif } #if defined(_WIN32) currPath = Unicode_Duplicate(FileIO_Filename(currFD)); newPath = Unicode_Duplicate(FileIO_Filename(newFD)); newAccess = newFD->flags; currAccess = currFD->flags; FileIO_Close(newFD); /* * The current file needs to be closed and reopened, * but we don't want to drop the file lock by calling * FileIO_Close() on it. Instead, use native close primitives. * We'll reopen it later with FileIO_Open. Set the * descriptor/handle to an invalid value while we're in the * middle of transferring ownership. */ CloseHandle(currFD->win32); currFD->win32 = INVALID_HANDLE_VALUE; if (File_RenameRetry(newPath, currPath, 10) == 0) { ret = TRUE; } else { savedErrno = errno; ASSERT(!ret); } FileIO_Invalidate(&tmpFD); /* * Clear the locking bits from the requested access so that reopening * the file ignores the advisory lock. */ ASSERT((currAccess & FILEIO_OPEN_LOCK_MANDATORY) == 0); currAccess &= ~(FILEIO_OPEN_LOCK_MANDATORY | FILEIO_OPEN_LOCK_ADVISORY | FILEIO_OPEN_LOCK_BEST | FILEIO_OPEN_LOCKED); status = FileIO_Open(&tmpFD, currPath, currAccess, FILEIO_OPEN); if (!FileIO_IsSuccess(status)) { Panic("Failed to reopen dictionary after renaming " "\"%s\" to \"%s\": %s (%d)\n", newPath, currPath, FileIO_ErrorEnglish(status), status); } ASSERT(tmpFD.lockToken == NULL); currFD->win32 = tmpFD.win32; FileIO_Cleanup(&tmpFD); Unicode_Free(currPath); Unicode_Free(newPath); errno = savedErrno; return ret; #else currPath = (char *)FileIO_Filename(currFD); newPath = (char *)FileIO_Filename(newFD); if (File_Rename(newPath, currPath)) { Log("%s: rename of '%s' to '%s' failed %d.\n", __FUNCTION__, newPath, currPath, errno); savedErrno = errno; } else { ret = TRUE; fd = newFD->posix; newFD->posix = currFD->posix; currFD->posix = fd; FileIO_Close(newFD); } errno = savedErrno; return ret; #endif }
FileIOResult FileIO_AtomicTempFile(FileIODescriptor *fileFD, // IN: FileIODescriptor *tempFD) // OUT: { Unicode tempPath = NULL; int permissions; FileIOResult status; #if !defined(_WIN32) int ret; struct stat stbuf; #endif ASSERT(FileIO_IsValid(fileFD)); ASSERT(tempFD && !FileIO_IsValid(tempFD)); tempPath = FileIO_AtomicTempPath(FileIO_Filename(fileFD)); if (!tempPath) { status = FILEIO_ERROR; goto bail; } #if defined(_WIN32) permissions = 0; File_UnlinkIfExists(tempPath); #else if (fstat(fileFD->posix, &stbuf)) { Log("%s: Failed to fstat '%s', errno: %d.\n", __FUNCTION__, FileIO_Filename(fileFD), errno); status = FILEIO_ERROR; goto bail; } permissions = stbuf.st_mode; /* Clean up a previously created temp file; if one exists. */ ret = Posix_Unlink(tempPath); if (ret != 0 && errno != ENOENT) { Log("%s: Failed to unlink temporary file, errno: %d\n", __FUNCTION__, errno); /* Fall through; FileIO_Create will report the actual error. */ } #endif status = FileIO_Create(tempFD, tempPath, FILEIO_ACCESS_READ | FILEIO_ACCESS_WRITE, FILEIO_OPEN_CREATE_SAFE, permissions); if (!FileIO_IsSuccess(status)) { Log("%s: Failed to create temporary file, %s (%d). errno: %d\n", __FUNCTION__, FileIO_ErrorEnglish(status), status, Err_Errno()); goto bail; } #if !defined(_WIN32) /* * On ESX we always use the vmkernel atomic file swap primitive, so * there's no need to set the permissions and owner of the temp file. * * XXX this comment is not true for NFS on ESX -- we use rename rather * than "vmkernel atomic file swap primitive" -- but we do not care * because files are always owned by root. Sigh. Bug 839283. */ if (!HostType_OSIsVMK()) { if (fchmod(tempFD->posix, stbuf.st_mode)) { Log("%s: Failed to chmod temporary file, errno: %d\n", __FUNCTION__, errno); status = FILEIO_ERROR; goto bail; } if (fchown(tempFD->posix, stbuf.st_uid, stbuf.st_gid)) { Log("%s: Failed to chown temporary file, errno: %d\n", __FUNCTION__, errno); status = FILEIO_ERROR; goto bail; } } #endif Unicode_Free(tempPath); return FILEIO_SUCCESS; bail: ASSERT(!FileIO_IsSuccess(status)); if (FileIO_IsValid(tempFD)) { FileIO_Close(tempFD); #if defined(_WIN32) File_UnlinkIfExists(tempPath); #else ret = Posix_Unlink(tempPath); if (ret != 0) { Log("%s: Failed to clean up temporary file, errno: %d\n", __FUNCTION__, errno); } ASSERT(ret == 0); #endif } Unicode_Free(tempPath); return status; }
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; }
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; }
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; }
void * FileLockIntrinsic(ConstUnicode pathName, // IN: Bool exclusivity, // IN: uint32 msecMaxWaitTime, // IN: const char *payload, // IN: int *err) // OUT: { FILELOCK_FILE_HANDLE handle; LockValues myValues; Unicode lockDir = NULL; Unicode entryFilePath = NULL; Unicode memberFilePath = NULL; Unicode entryDirectory = NULL; ASSERT(pathName); ASSERT(err); /* Construct the locking directory path */ lockDir = Unicode_Append(pathName, FILELOCK_SUFFIX); /* establish our values */ myValues.machineID = (char *) FileLockGetMachineID(); // don't free this! myValues.executionID = FileLockGetExecutionID(); // free this! myValues.payload = (char *) payload; myValues.lockType = exclusivity ? LOCK_EXCLUSIVE : LOCK_SHARED; myValues.lamportNumber = 0; myValues.locationChecksum = FileLockLocationChecksum(lockDir); // free this! myValues.waitTime = 0; myValues.msecMaxWaitTime = msecMaxWaitTime; myValues.memberName = NULL; LOG(1, ("Requesting %s lock on %s (%s, %s, %u).\n", myValues.lockType, UTF8(pathName), myValues.machineID, myValues.executionID, myValues.msecMaxWaitTime)); /* * Attempt to create the locking and entry directories; obtain the * entry and member path names. */ *err = CreateEntryDirectory(myValues.machineID, myValues.executionID, lockDir, &entryDirectory, &entryFilePath, &memberFilePath, &myValues.memberName); switch (*err) { case 0: break; case EROFS: /* FALL THROUGH */ case EACCES: if (!exclusivity) { /* * Lock is for read/shared access however the lock directory could * not be created. Grant an implicit read lock whenever possible. * The address of a private variable will be used for the lock token. */ Warning(LGPFX" %s implicit %s lock succeeded on '%s'.\n", __FUNCTION__, LOCK_SHARED, UTF8(pathName)); *err = 0; memberFilePath = &implicitReadToken; } /* FALL THROUGH */ default: goto bail; } ASSERT(Unicode_LengthInCodeUnits(memberFilePath) - Unicode_LengthInCodeUnits(pathName) <= FILELOCK_OVERHEAD); /* Attempt to create the entry file */ *err = FileLockOpenFile(entryFilePath, O_CREAT | O_WRONLY, &handle); if (*err != 0) { /* clean up */ FileRemoveDirectoryRobust(entryDirectory); FileRemoveDirectoryRobust(lockDir); goto bail; } /* what is max(Number[1]... Number[all lockers])? */ *err = Scanner(lockDir, NumberScan, &myValues, FALSE); if (*err != 0) { /* clean up */ FileLockCloseFile(handle); FileDeletionRobust(entryFilePath, FALSE); FileRemoveDirectoryRobust(entryDirectory); FileRemoveDirectoryRobust(lockDir); goto bail; } /* Number[i] = 1 + max([Number[1]... Number[all lockers]) */ myValues.lamportNumber++; /* Attempt to create the member file */ *err = CreateMemberFile(handle, &myValues, entryFilePath, memberFilePath); /* Remove entry directory; it has done its job */ FileRemoveDirectoryRobust(entryDirectory); if (*err != 0) { /* clean up */ FileDeletionRobust(entryFilePath, FALSE); FileDeletionRobust(memberFilePath, FALSE); FileRemoveDirectoryRobust(lockDir); goto bail; } /* Attempt to acquire the lock */ *err = Scanner(lockDir, WaitForPossession, &myValues, TRUE); switch (*err) { case 0: break; case EAGAIN: /* clean up */ FileDeletionRobust(memberFilePath, FALSE); FileRemoveDirectoryRobust(lockDir); /* FALL THROUGH */ default: break; } bail: Unicode_Free(lockDir); Unicode_Free(entryDirectory); Unicode_Free(entryFilePath); Unicode_Free(myValues.memberName); free(myValues.locationChecksum); free(myValues.executionID); if (*err != 0) { Unicode_Free(memberFilePath); memberFilePath = NULL; if (*err == EAGAIN) { *err = 0; // lock not acquired } } return (void *) memberFilePath; }
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 }
int FileLockHackVMX(ConstUnicode pathName) // IN: { int err; LockValues myValues; Unicode lockDir = NULL; Unicode entryFilePath = NULL; Unicode memberFilePath = NULL; Unicode entryDirectory = NULL; ASSERT(pathName); /* first the locking directory path name */ lockDir = Unicode_Append(pathName, FILELOCK_SUFFIX); /* establish our values */ myValues.machineID = (char *) FileLockGetMachineID(); // don't free this! myValues.executionID = FileLockGetExecutionID(); // free this! myValues.locationChecksum = FileLockLocationChecksum(lockDir); // free this! myValues.lamportNumber = 0; myValues.memberName = NULL; LOG(1, ("%s on %s (%s, %s).\n", __FUNCTION__, UTF8(pathName), myValues.machineID, myValues.executionID)); err = CreateEntryDirectory(myValues.machineID, myValues.executionID, lockDir, &entryDirectory, &entryFilePath, &memberFilePath, &myValues.memberName); if (err != 0) { goto bail; } /* Scan the lock directory */ err = Scanner(lockDir, ScannerVMX, &myValues, FALSE); if (err == 0) { /* if no members are valid, clean up */ if (myValues.lamportNumber == 1) { FileDeletionRobust(pathName, FALSE); } } else { if (vmx86_debug) { Warning(LGPFX" %s clean-up failure for '%s': %s\n", __FUNCTION__, UTF8(pathName), strerror(err)); } } /* clean up */ FileRemoveDirectoryRobust(entryDirectory); FileRemoveDirectoryRobust(lockDir); bail: Unicode_Free(lockDir); Unicode_Free(entryDirectory); Unicode_Free(entryFilePath); Unicode_Free(memberFilePath); Unicode_Free(myValues.memberName); free(myValues.locationChecksum); free(myValues.executionID); return err; }
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 ScanDirectory(ConstUnicode lockDir, // IN: int (*func)( // IN: ConstUnicode lockDir, ConstUnicode fileName, LockValues *memberValues, LockValues *myValues ), LockValues *myValues, // IN: Bool cleanUp) // IN: { uint32 i; int err; int numEntries; Unicode *fileList = NULL; char *myExecutionID = NULL; char *locationChecksum = NULL; ASSERT(lockDir); numEntries = FileListDirectoryRobust(lockDir, &fileList); if (numEntries == -1) { Log(LGPFX" %s: Could not read the directory '%s': %d\n", __FUNCTION__, UTF8(lockDir), Err_Errno()); return EDOM; // out of my domain } /* Pass 1: Validate entries and handle any 'D' entries */ for (i = 0, err = 0; i < numEntries; i++) { /* Remove any non-locking files */ if (!FileLockValidName(fileList[i])) { Log(LGPFX" %s discarding %s from %s'; invalid file name.\n", __FUNCTION__, UTF8(fileList[i]), UTF8(lockDir)); err = RemoveLockingFile(lockDir, fileList[i]); if (err != 0) { goto bail; } Unicode_Free(fileList[i]); fileList[i] = NULL; continue; } /* * Any lockers appear to be entering? * * This should be rather rare. If a locker dies while entering * this will cleaned-up. */ if (Unicode_StartsWith(fileList[i], "D")) { if (cleanUp) { err = ActivateLockList(fileList[i], myValues); if (err != 0) { goto bail; } } Unicode_Free(fileList[i]); fileList[i] = NULL; } } if (myValues->lockList != NULL) { goto bail; } myExecutionID = FileLockGetExecutionID(); locationChecksum = FileLockLocationChecksum(lockDir); /* Pass 2: Handle the 'M' entries */ for (i = 0, err = 0; i < numEntries; i++) { LockValues *ptr; Bool myLockFile; LockValues memberValues; char buffer[FILELOCK_DATA_SIZE]; if ((fileList[i] == NULL) || (Unicode_StartsWith(fileList[i], "E"))) { continue; } myLockFile = (Unicode_Compare(fileList[i], myValues->memberName) == 0) ? TRUE : FALSE; if (myLockFile) { /* It's me! No need to read or validate anything. */ ptr = myValues; } else { /* It's not me! Attempt to extract the member values. */ err = FileLockMemberValues(lockDir, fileList[i], buffer, FILELOCK_DATA_SIZE, &memberValues); if (err != 0) { if (err == ENOENT) { err = 0; /* Not there anymore; locker unlocked or timed out */ continue; } break; } /* Remove any stale locking files */ if (FileLockMachineIDMatch(myValues->machineID, memberValues.machineID)) { char *dispose = NULL; if (FileLockValidOwner(memberValues.executionID, memberValues.payload)) { /* If it's mine it better still be where I put it! */ if ((strcmp(myExecutionID, memberValues.executionID) == 0) && ((memberValues.locationChecksum != NULL) && (strcmp(memberValues.locationChecksum, locationChecksum) != 0))) { dispose = "lock file has been moved."; } } else { dispose = "invalid executionID."; } if (dispose) { Log(LGPFX" %s discarding %s from %s': %s\n", __FUNCTION__, UTF8(fileList[i]), UTF8(lockDir), dispose); Unicode_Free(memberValues.memberName); err = RemoveLockingFile(lockDir, fileList[i]); if (err != 0) { break; } continue; } } ptr = &memberValues; } /* Locking file looks good; see what happens */ err = (*func)(lockDir, fileList[i], ptr, myValues); if (ptr == &memberValues) { Unicode_Free(memberValues.memberName); } if (err != 0) { break; } } bail: for (i = 0; i < numEntries; i++) { Unicode_Free(fileList[i]); } free(fileList); free(locationChecksum); free(myExecutionID); return err; }
VixError Vix_TranslateSystemError(int systemError) // IN { VixError err = VIX_E_FAIL; #ifdef _WIN32 Unicode msg; switch (systemError) { case ERROR_ACCESS_DENIED: err = VIX_E_FILE_ACCESS_ERROR; break; case ERROR_INVALID_NAME: err = VIX_E_FILE_NAME_INVALID; break; case ERROR_FILENAME_EXCED_RANGE: err = VIX_E_FILE_NAME_TOO_LONG; break; case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_BAD_PATHNAME: case ERROR_DIRECTORY: case ERROR_BUFFER_OVERFLOW: err = VIX_E_FILE_NOT_FOUND; break; case ERROR_DIR_NOT_EMPTY: err = VIX_E_DIRECTORY_NOT_EMPTY; break; case ERROR_TOO_MANY_OPEN_FILES: case ERROR_NO_MORE_FILES: case ERROR_WRITE_PROTECT: case ERROR_WRITE_FAULT: case ERROR_READ_FAULT: case ERROR_SHARING_VIOLATION: case ERROR_SEEK: case ERROR_CANNOT_MAKE: Log("%s: system error = %d\n", __FUNCTION__, systemError); err = VIX_E_FILE_ERROR; break; case ERROR_HANDLE_DISK_FULL: case ERROR_DISK_FULL: err = VIX_E_DISK_FULL; break; case ERROR_FILE_EXISTS: case ERROR_ALREADY_EXISTS: err = VIX_E_FILE_ALREADY_EXISTS; break; case ERROR_BUSY: case ERROR_PATH_BUSY: err = VIX_E_OBJECT_IS_BUSY; break; case ERROR_INVALID_PARAMETER: err = VIX_E_INVALID_ARG; break; case ERROR_NOT_SUPPORTED: err = VIX_E_NOT_SUPPORTED; break; case ERROR_NO_DATA: case ERROR_INVALID_DATA: err = VIX_E_NOT_FOUND; break; case ERROR_NOT_ENOUGH_MEMORY: err = VIX_E_OUT_OF_MEMORY; break; default: err = VIX_E_FAIL; } msg = Win32U_FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, systemError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), NULL); Log("Foundry operation failed with system error: %s (%d), translated to %"FMT64"d\n", msg, systemError, err); Unicode_Free(msg); #else // linux, other *nix err = Vix_TranslateErrno(systemError); #endif return err; } // Vix_TranslateSystemError
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; }