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; }
FileIOResult FileIO_CloseAndUnlink(FileIODescriptor *fd) // IN: { char *path; FileIOResult ret; ASSERT(fd); ASSERT(FileIO_IsValid(fd)); path = Unicode_Duplicate(fd->fileName); ret = FileIO_Close(fd); if (FileIO_IsSuccess(ret)) { if (File_UnlinkIfExists(path) == -1) { ret = FILEIO_ERROR; } } free(path); return ret; }
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; }