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 }
static int DnD_TryInitVmblock(const char *vmbFsName, // IN const char *vmbMntPoint, // IN const char *vmbDevice, // IN mode_t vmbDeviceMode, // IN Bool (*verifyBlock)(int fd)) // IN { #if defined NO_SETMNTENT || defined NO_ENDMNTENT NOT_IMPLEMENTED(); errno = ENOSYS; return -1; #else Bool found = FALSE; int blockFd = -1; char *realMntPoint; MNTHANDLE fp; DECLARE_MNTINFO(mnt); ASSERT(vmbFsName); ASSERT(vmbMntPoint); ASSERT(vmbDevice); /* Resolve desired mount point in case it is symlinked somewhere */ realMntPoint = Posix_RealPath(vmbMntPoint); if (!realMntPoint) { /* * If resolve failed for some reason try to fall back to * original mount point specification. */ realMntPoint = Util_SafeStrdup(vmbMntPoint); } /* Make sure the vmblock file system is mounted. */ fp = OPEN_MNTFILE("r"); if (fp == NULL) { LOG(1, ("%s: could not open mount file\n", __func__)); goto out; } while (GETNEXT_MNTINFO(fp, mnt)) { /* * In the future we can publish the mount point in VMDB so that the UI * can use it rather than enforcing the VMBLOCK_MOUNT_POINT check here. */ if (strcmp(MNTINFO_FSTYPE(mnt), vmbFsName) == 0 && strcmp(MNTINFO_MNTPT(mnt), realMntPoint) == 0) { found = TRUE; break; } } (void) CLOSE_MNTFILE(fp); if (found) { /* Open device node for communication with vmblock. */ blockFd = Posix_Open(vmbDevice, vmbDeviceMode); if (blockFd < 0) { LOG(1, ("%s: Can not open blocker device (%s)\n", __func__, strerror(errno))); } else { LOG(4, ("%s: Opened blocker device at %s\n", __func__, VMBLOCK_DEVICE)); if (verifyBlock && !verifyBlock(blockFd)) { LOG(4, ("%s: Blocker device at %s did not pass checks, closing.\n", __func__, VMBLOCK_DEVICE)); close(blockFd); blockFd = -1; } } } out: free(realMntPoint); return blockFd; #endif }
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; }