Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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
}
Exemplo n.º 3
0
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;
}