示例#1
0
void File::Touch(StringIn filename, FILETIME fileTime)
{
#if 0
	DWORD creationDisposition = OPEN_ALWAYS;
	HANDLE hFile = CreateFileW(CStringw(filename), GENERIC_READ | GENERIC_WRITE, 0/*share*/, NULL,
		creationDisposition,
		0, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		IO::StringWriter str;
		str << "Touch failed for " << filename;
		raise(IOException(str.str()));
	}

	BOOL success;
	FILE_BASIC_INFO info;
	success = GetFileInformationByHandleEx(hFile, FileBasicInfo, &info, sizeof(info));
	if (!success)
	{
		raise_(Exception::FromHResult(HRESULT_FROM_WIN32(GetLastError())));
	}

	info.LastWriteTime.QuadPart = *(uint64*)&fileTime;
	info.LastAccessTime.QuadPart = *(uint64*)&fileTime;
	success = SetFileInformationByHandle(hFile, FileBasicInfo, &info, sizeof(info));
	if (!success)
	{
		raise_(Exception::FromHResult(HRESULT_FROM_WIN32(GetLastError())));
	}

	CloseHandle(hFile);
#endif
}
示例#2
0
int fchmod(int fd, mode_t mode) {
  HANDLE h = (HANDLE)_get_osfhandle(fd);
  if (h == INVALID_HANDLE_VALUE) {
    return -1;
  }

  FILE_ATTRIBUTE_TAG_INFO attr{};
  if (!GetFileInformationByHandleEx(
          h, FileAttributeTagInfo, &attr, sizeof(attr))) {
    return -1;
  }

  if (mode & _S_IWRITE) {
    attr.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
  } else {
    attr.FileAttributes |= FILE_ATTRIBUTE_READONLY;
  }

  if (!SetFileInformationByHandle(
          h, FileAttributeTagInfo, &attr, sizeof(attr))) {
    return -1;
  }

  return 0;
}
示例#3
0
static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem,
    PVOID FileContext, UINT32 FileAttributes,
    UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime,
    FSP_FSCTL_FILE_INFO *FileInfo)
{
    HANDLE Handle = HandleFromContext(FileContext);
    FILE_BASIC_INFO BasicInfo = { 0 };

    if (INVALID_FILE_ATTRIBUTES == FileAttributes)
        FileAttributes = 0;
    else if (0 == FileAttributes)
        FileAttributes = FILE_ATTRIBUTE_NORMAL;

    BasicInfo.FileAttributes = FileAttributes;
    BasicInfo.CreationTime.QuadPart = CreationTime;
    BasicInfo.LastAccessTime.QuadPart = LastAccessTime;
    BasicInfo.LastWriteTime.QuadPart = LastWriteTime;
    //BasicInfo.ChangeTime = ChangeTime;

    if (!SetFileInformationByHandle(Handle,
        FileBasicInfo, &BasicInfo, sizeof BasicInfo))
        return FspNtStatusFromWin32(GetLastError());

    return GetFileInfoInternal(Handle, FileInfo);
}
 BOOL WINAPI_DECL SetFileTime(
     _In_      HANDLE hFile,
     _In_opt_  const FILETIME *lpCreationTime,
     _In_opt_  const FILETIME *lpLastAccessTime,
     _In_opt_  const FILETIME *lpLastWriteTime
     )
 {
     FILE_BASIC_INFO basicInfo;
     BOOL b = GetFileInformationByHandleEx(
         hFile, 
         FileBasicInfo, 
         &basicInfo, 
         sizeof(basicInfo));
     if (b == FALSE)
         return FALSE;
     if (lpCreationTime)
         basicInfo.CreationTime = *(LARGE_INTEGER const *)lpCreationTime;
     if (lpLastAccessTime)
         basicInfo.LastAccessTime = *(LARGE_INTEGER const *)lpLastAccessTime;
     if (lpLastWriteTime)
         basicInfo.LastWriteTime = *(LARGE_INTEGER const *)lpLastAccessTime;
     b = SetFileInformationByHandle(
         hFile, 
         FileBasicInfo, 
         &basicInfo, 
         sizeof(basicInfo));
     return b;
 }
示例#5
0
static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
    PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize,
    FSP_FSCTL_FILE_INFO *FileInfo)
{
    HANDLE Handle = HandleFromContext(FileContext);
    FILE_BASIC_INFO BasicInfo = { 0 };
    FILE_ALLOCATION_INFO AllocationInfo = { 0 };
    FILE_ATTRIBUTE_TAG_INFO AttributeTagInfo;

    if (ReplaceFileAttributes)
    {
        if (0 == FileAttributes)
            FileAttributes = FILE_ATTRIBUTE_NORMAL;

        BasicInfo.FileAttributes = FileAttributes;
        if (!SetFileInformationByHandle(Handle,
            FileBasicInfo, &BasicInfo, sizeof BasicInfo))
            return FspNtStatusFromWin32(GetLastError());
    }
    else if (0 != FileAttributes)
    {
        if (!GetFileInformationByHandleEx(Handle,
            FileAttributeTagInfo, &AttributeTagInfo, sizeof AttributeTagInfo))
            return FspNtStatusFromWin32(GetLastError());

        BasicInfo.FileAttributes = FileAttributes | AttributeTagInfo.FileAttributes;
        if (BasicInfo.FileAttributes ^ FileAttributes)
        {
            if (!SetFileInformationByHandle(Handle,
                FileBasicInfo, &BasicInfo, sizeof BasicInfo))
                return FspNtStatusFromWin32(GetLastError());
        }
    }

    if (!SetFileInformationByHandle(Handle,
        FileAllocationInfo, &AllocationInfo, sizeof AllocationInfo))
        return FspNtStatusFromWin32(GetLastError());

    return GetFileInfoInternal(Handle, FileInfo);
}
示例#6
0
static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem,
    PVOID FileContext, UINT64 NewSize, BOOLEAN SetAllocationSize,
    FSP_FSCTL_FILE_INFO *FileInfo)
{
    HANDLE Handle = HandleFromContext(FileContext);
    FILE_ALLOCATION_INFO AllocationInfo;
    FILE_END_OF_FILE_INFO EndOfFileInfo;

    if (SetAllocationSize)
    {
        /*
         * This file system does not maintain AllocationSize, although NTFS clearly can.
         * However it must always be FileSize <= AllocationSize and NTFS will make sure
         * to truncate the FileSize if it sees an AllocationSize < FileSize.
         *
         * If OTOH a very large AllocationSize is passed, the call below will increase
         * the AllocationSize of the underlying file, although our file system does not
         * expose this fact. This AllocationSize is only temporary as NTFS will reset
         * the AllocationSize of the underlying file when it is closed.
         */

        AllocationInfo.AllocationSize.QuadPart = NewSize;

        if (!SetFileInformationByHandle(Handle,
            FileAllocationInfo, &AllocationInfo, sizeof AllocationInfo))
            return FspNtStatusFromWin32(GetLastError());
    }
    else
    {
        EndOfFileInfo.EndOfFile.QuadPart = NewSize;

        if (!SetFileInformationByHandle(Handle,
            FileEndOfFileInfo, &EndOfFileInfo, sizeof EndOfFileInfo))
            return FspNtStatusFromWin32(GetLastError());
    }

    return GetFileInfoInternal(Handle, FileInfo);
}
示例#7
0
static NTSTATUS SetDelete(FSP_FILE_SYSTEM *FileSystem,
    PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile)
{
    HANDLE Handle = HandleFromContext(FileContext);
    FILE_DISPOSITION_INFO DispositionInfo;

    DispositionInfo.DeleteFile = DeleteFile;

    if (!SetFileInformationByHandle(Handle,
        FileDispositionInfo, &DispositionInfo, sizeof DispositionInfo))
        return FspNtStatusFromWin32(GetLastError());

    return STATUS_SUCCESS;
}
示例#8
0
bool tr_sys_file_truncate(tr_sys_file_t handle, uint64_t size, tr_error** error)
{
    TR_ASSERT(handle != TR_BAD_SYS_FILE);

    FILE_END_OF_FILE_INFO info;
    info.EndOfFile.QuadPart = size;

    bool ret = SetFileInformationByHandle(handle, FileEndOfFileInfo, &info, sizeof(info));

    if (!ret)
    {
        set_system_error(error, GetLastError());
    }

    return ret;
}
示例#9
0
static int do_SetEndOfFile(int argc, wchar_t **argv)
{
    if (argc != 3)
        fail("usage: SetEndOfFile FileName Length");
    HANDLE h = CreateFileW(argv[1],
        GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
    if (INVALID_HANDLE_VALUE == h)
        errprint(0);
    else
    {
        FILE_END_OF_FILE_INFO EofInfo;
        EofInfo.EndOfFile.QuadPart = wcstoull(argv[2], 0, 0);
        BOOL r = SetFileInformationByHandle(h, FileEndOfFileInfo, &EofInfo, sizeof EofInfo);
        errprint(r);
        CloseHandle(h);
    }
    return 0;
}
示例#10
0
static int do_RenameStream(int argc, wchar_t **argv)
{
    if (argc != 4 || ':' != argv[2][0])
        fail("usage: RenameStream FileName:ExistingStreamName :NewStreamName ReplaceIfExists");
    fail("not implemented!");
#if 0
    HANDLE h = CreateFileW(argv[1],
        DELETE | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
    if (INVALID_HANDLE_VALUE == h)
        errprint(0);
    else
    {
        typedef struct _FILE_RENAME_INFORMATION {
            BOOLEAN ReplaceIfExists;
            HANDLE RootDirectory;
            ULONG FileNameLength;
            WCHAR FileName[1];
        } FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
        size_t namesize = wcslen(argv[2]) * sizeof(wchar_t);
        size_t infosize = sizeof(FILE_RENAME_INFORMATION) + namesize;
        FILE_RENAME_INFORMATION *RenameInfo = _alloca(infosize);
        memset(RenameInfo, 0, infosize);
        RenameInfo->ReplaceIfExists = !!(wcstoul(argv[3], 0, 0));
        RenameInfo->RootDirectory = 0;
        RenameInfo->FileNameLength = namesize;
        memcpy(RenameInfo->FileName, argv[2], namesize);
        /*
         * This seems to fail with ERROR_INVALID_HANDLE on Win32 and ERROR_INVALID_NAME on Win64.
         * Do not have the time to figure the exact reasons why right now.
         */
        BOOL r = SetFileInformationByHandle(h, FileRenameInfo, &RenameInfo, infosize);
        errprint(r);
        CloseHandle(h);
    }
#endif
    return 0;
}
示例#11
0
	boost::intrusive_ptr<file> file_pool::open_file(void* st, std::string const& p
		, file_storage::iterator fe, file_storage const& fs, int m, error_code& ec)
	{
		TORRENT_ASSERT(st != 0);
		TORRENT_ASSERT(is_complete(p));
		TORRENT_ASSERT((m & file::rw_mask) == file::read_only
			|| (m & file::rw_mask) == file::read_write);
		mutex::scoped_lock l(m_mutex);
		file_set::iterator i = m_files.find(std::make_pair(st, fs.file_index(*fe)));
		if (i != m_files.end())
		{
			lru_file_entry& e = i->second;
			e.last_use = time_now();

			if (e.key != st && ((e.mode & file::rw_mask) != file::read_only
				|| (m & file::rw_mask) != file::read_only))
			{
				// this means that another instance of the storage
				// is using the exact same file.
#if BOOST_VERSION >= 103500
				ec = errors::file_collision;
#endif
				return boost::intrusive_ptr<file>();
			}

			e.key = st;
			// if we asked for a file in write mode,
			// and the cached file is is not opened in
			// write mode, re-open it
			if ((((e.mode & file::rw_mask) != file::read_write)
				&& ((m & file::rw_mask) == file::read_write))
				|| (e.mode & file::no_buffer) != (m & file::no_buffer)
				|| (e.mode & file::random_access) != (m & file::random_access))
			{
				// close the file before we open it with
				// the new read/write privilages
				TORRENT_ASSERT(e.file_ptr->refcount() == 1);

#if TORRENT_CLOSE_MAY_BLOCK
				mutex::scoped_lock l(m_closer_mutex);
				m_queued_for_close.push_back(e.file_ptr);
				l.unlock();
				e.file_ptr = new file;
#else
				e.file_ptr->close();
#endif
				std::string full_path = combine_path(p, fs.file_path(*fe));
				if (!e.file_ptr->open(full_path, m, ec))
				{
					m_files.erase(i);
					return boost::intrusive_ptr<file>();
				}
#ifdef TORRENT_WINDOWS
// file prio is supported on vista and up
#if _WIN32_WINNT >= 0x0600
				if (m_low_prio_io)
				{
					// TODO: load this function dynamically from Kernel32.dll
					FILE_IO_PRIORITY_HINT_INFO priorityHint;
					priorityHint.PriorityHint = IoPriorityHintLow;
					SetFileInformationByHandle(e.file_ptr->native_handle(),
						FileIoPriorityHintInfo, &priorityHint, sizeof(PriorityHint));
				}
#endif
#endif
				TORRENT_ASSERT(e.file_ptr->is_open());
				e.mode = m;
			}
			TORRENT_ASSERT((e.mode & file::no_buffer) == (m & file::no_buffer));
			return e.file_ptr;
		}
		// the file is not in our cache
		if ((int)m_files.size() >= m_size)
		{
			// the file cache is at its maximum size, close
			// the least recently used (lru) file from it
			remove_oldest();
		}
		lru_file_entry e;
		e.file_ptr.reset(new (std::nothrow)file);
		if (!e.file_ptr)
		{
			ec = error_code(ENOMEM, get_posix_category());
			return e.file_ptr;
		}
		std::string full_path = combine_path(p, fs.file_path(*fe));
		if (!e.file_ptr->open(full_path, m, ec))
			return boost::intrusive_ptr<file>();
		e.mode = m;
		e.key = st;
		m_files.insert(std::make_pair(std::make_pair(st, fs.file_index(*fe)), e));
		TORRENT_ASSERT(e.file_ptr->is_open());
		return e.file_ptr;
	}
示例#12
0
bool os_create_reflink(const std::string &linkname, const std::string &fname)
{
	HANDLE source_handle = CreateFileW(ConvertToWchar(fname).c_str(),
		GENERIC_READ, FILE_SHARE_READ, NULL,
		OPEN_EXISTING, 0, NULL);

	if (source_handle == INVALID_HANDLE_VALUE)
		return false;

	BY_HANDLE_FILE_INFORMATION source_info;
	if (!GetFileInformationByHandle(source_handle, &source_info))
	{
		CloseHandle(source_handle);
		return false;
	}

	LARGE_INTEGER source_size;
	source_size.LowPart = source_info.nFileSizeLow;
	source_size.HighPart = source_info.nFileSizeHigh;

	ULONG ret_bytes;
	FSCTL_GET_INTEGRITY_INFORMATION_BUFFER get_integrity_info;
	if (!DeviceIoControl(source_handle, FSCTL_GET_INTEGRITY_INFORMATION, NULL, 0, 
		&get_integrity_info, sizeof(get_integrity_info), &ret_bytes, NULL))
	{
		CloseHandle(source_handle);
		return false;
	}

	HANDLE dest_handle = CreateFileW(ConvertToWchar(linkname).c_str(),
		GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL,
		CREATE_ALWAYS, 0, source_handle);

	if (dest_handle == INVALID_HANDLE_VALUE)
	{
		CloseHandle(source_handle);
		return false;
	}

	bool has_error = false;
	if ((source_info.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)>0
		&& !DeviceIoControl(dest_handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &ret_bytes, NULL))
	{
		has_error = true;
	}

	FSCTL_SET_INTEGRITY_INFORMATION_BUFFER set_integrity_info = {
		get_integrity_info.ChecksumAlgorithm,
		get_integrity_info.Reserved,
		get_integrity_info.Flags };
	
	if (!has_error
		&& !DeviceIoControl(dest_handle, FSCTL_SET_INTEGRITY_INFORMATION,
			&set_integrity_info, sizeof(set_integrity_info), NULL, 0, NULL, NULL))
	{
		has_error = true;
	}

	FILE_END_OF_FILE_INFO eof_info = { source_size };
	if(!has_error
		&& !SetFileInformationByHandle(dest_handle, FileEndOfFileInfo, &eof_info, sizeof(eof_info) ) )
	{
		has_error = true;
	}

	int64 cluster_size = get_integrity_info.ClusterSizeInBytes;

	int64 total_reflink = source_size.QuadPart;

	if(total_reflink%cluster_size!=0)
		total_reflink = ((total_reflink) / cluster_size + 1 )*cluster_size;

	int64 max_reflink = cluster_size == 0 ? total_reflink : (4LL * 1024 * 1024 * 1024 - cluster_size);

	reflink::DUPLICATE_EXTENTS_DATA reflink_data;
	reflink_data.FileHandle = source_handle;

	int64 reflinked = 0;
	while (!has_error
		&& reflinked < total_reflink)
	{
		int64 curr_reflink = (std::min)(total_reflink - reflinked, max_reflink);

		reflink_data.ByteCount.QuadPart = curr_reflink;
		reflink_data.SourceFileOffset.QuadPart = reflinked;
		reflink_data.TargetFileOffset.QuadPart = reflinked;

		if (!DeviceIoControl(dest_handle, reflink::FSCTL_DUPLICATE_EXTENTS_TO_FILE,
			&reflink_data, sizeof(reflink_data), NULL, 0, &ret_bytes, NULL))
		{
			has_error = true;
		}

		reflinked += curr_reflink;
	}

	DWORD err = GetLastError();
	CloseHandle(source_handle);
	CloseHandle(dest_handle);
	
	if (has_error)
	{		
		DeleteFileW(ConvertToWchar(linkname).c_str());
		SetLastError(err);
		return false;
	}

	return true;
}
示例#13
0
/**
 * Accepts a HashRequest structure and attempts to calculate the requested hash
 * of the provided file using synchronous IO.
 */
int HashFileWithSyncIO(HashRequest* request, HashProgressCallback* callback) {
    /* Variables to hold our options. */
    char doCRC32 = 0;
    char doMD5 = 0;
    char doSHA1 = 0;
    char doED2k = 0;

    /* Standard variables (same between platforms) */
    CRC32_Context crc32;
    MD4_Context ed2k;
    MD5_Context md5;
    SHA1_Context sha1;
    uint32_t bytesRead = 0;
    uint32_t ed2kBlockIdx = 0;
    uint32_t ed2kBlocks = 0;
    uint32_t ed2kHashLength = 0;
    uint8_t  ed2kLoopIdx = 0;
    uint32_t progressLoopCount = 0;
    uint64_t totalBytesRead = 0;
    unsigned char* ed2kHashes = NULL;
    unsigned char* fileData = NULL;

    /* Platform specific variables */
    HANDLE file = NULL;
    WIN32_FILE_ATTRIBUTE_DATA fileAttributeData = { 0 };
    BOOL readFailed = FALSE;
    FILE_IO_PRIORITY_HINT_INFO priorityHint = { 0 };


    /* Simple guard condition. If we have no request, we can't process. */
    if (request == NULL) {
        return -1;
    }

    /* clear the result buffer */
    SecureZeroMemory(&request->result, 56);

    /* Set our options */
    doCRC32 = request->options & OPTION_CRC32;
    doMD5 = request->options & OPTION_MD5;
    doSHA1 = request->options & OPTION_SHA1;
    doED2k = request->options & OPTION_ED2K;

    /* If they didn't pass any valid options (or passed 0) for the options,
     * return since we can't calculate a hash without knowing which hashing
     * algorithm(s) to use. */
    if (!doCRC32 && !doMD5 && !doSHA1 && !doED2k) {
        return -2;
    }

    file = CreateFileW(
        request->filename,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL);
    if (file == INVALID_HANDLE_VALUE) {
        return -4;
    }

    /* Set our file priority hint to background. This is only a hint to Windows
     * that our file IO should be secondary to other scheduled IO. If there is
     * no other IO at the time, it will run at full speed. */
    priorityHint.PriorityHint = IoPriorityHintVeryLow;
    SetFileInformationByHandle(
        file,
        FileIoPriorityHintInfo,
        &priorityHint,
        sizeof(priorityHint));

    /* Set up our hashes, also calculate the space needed for the ED2k hash if
     * we were requested to produce an ED2k hash result. */
    if (doED2k) {
        BOOL result = 0;
        uint64_t fileSize = 0;

        result = GetFileAttributesExW(
            request->filename,
            GetFileExInfoStandard,
            &fileAttributeData);

        if (!result) {
            CloseHandle(file);
            return -5;
        }

        /* Convert the file size from two 32-bit DWORDS to a uint64_t. */
        fileSize =
            (((uint64_t)fileAttributeData.nFileSizeHigh) << 32) |
            fileAttributeData.nFileSizeLow;

        /* Determine the number of blocks needed for calculating the file's
         * ED2k's hash. If the file isn't an even multiple of BLOCKSIZE then we
         * add one more block. */
         ed2kBlocks = (uint32_t)((uint64_t)fileSize / BLOCKSIZE);
         if (fileSize % BLOCKSIZE > 0) {
            ++ed2kBlocks;
         }

        /* Only allocate an array if we have more than one block to hash.
         * Files that are smaller in size than BLOCKSIZE simply use the normal
         * computed MD4 hash. */
        if (ed2kBlocks > 1) {
            ed2kHashLength = ed2kBlocks * 16;
            ed2kHashes = (unsigned char*)HeapAlloc(
                GetProcessHeap(),
                HEAP_ZERO_MEMORY,
                ed2kHashLength);
            if (ed2kHashes == NULL) {
                CloseHandle(file);
                return -6;
            }
        }
    }
    if (doCRC32) { CRC32_init(&crc32); }
    if (doMD5) { MD5_init(&md5); }
    if (doSHA1) { SHA1_init(&sha1); }

    /* Allocate the file buffer. The BUFFERSIZE constant is a clean multiple of
     * the BLOCKSIZE for ED2k hashing, which will make for easier looping. */
    fileData = (unsigned char*)HeapAlloc(
        GetProcessHeap(),
        HEAP_ZERO_MEMORY,
        BUFFERSIZE);
    if (fileData == NULL) {
        CloseHandle(file);
        if (doED2k && ed2kHashes) {
            HeapFree(GetProcessHeap(), 0, ed2kHashes);
        }

        return -7;
    }

    do {
        if (!ReadFile(file, fileData, BUFFERSIZE, &bytesRead, NULL)) {
            readFailed = TRUE;
            break;
        }

        if (bytesRead == 0) {
            break;
        }

        /* Update the total bytes read and inform the callback of our progress
         * if it's time. Don't forget to bail out if they request it. */
        totalBytesRead += bytesRead;
        if (callback && progressLoopCount % 10 == 0) {
            int32_t result = callback(request->tag, totalBytesRead);
            if (result != 0) {
                CloseHandle(file);
                HeapFree(GetProcessHeap(), 0, fileData);
                if (doED2k && ed2kHashes) {
                    HeapFree(GetProcessHeap(), 0, ed2kHashes);
                }

                return -9;
            }
        }

        ++progressLoopCount;

        /* Update the hashes with the file data. */
        if (doED2k) {
            /* If we've looped 10 times, finish the current MD4 hash, update
             * the block counter and set the hash to be initialized again.
             * Also, if the BUFFERSIZE is ever changed, the loop index will
             * need to be adjusted as well. */
            if (ed2kLoopIdx == 10) {
                MD4_final(&ed2k, &ed2kHashes[ed2kBlockIdx * 16]);
                ++ed2kBlockIdx;
                ed2kLoopIdx = 0;
            }

            /* If this is the first loop (or we've just finished a hash)
             * initialize the MD4 hash for the next block. */
            if (ed2kLoopIdx == 0) {
                MD4_init(&ed2k);
            }

            ++ed2kLoopIdx;
            MD4_update(&ed2k, fileData, bytesRead);
        }
        if (doCRC32) { CRC32_update(&crc32, fileData, bytesRead); }
        if (doMD5) { MD5_update(&md5, fileData, bytesRead); }
        if (doSHA1) { SHA1_update(&sha1, fileData, bytesRead); }
    } while (bytesRead != 0);

    /* Free our file buffer and close the file since we're done with it. */
    HeapFree(GetProcessHeap(), 0, fileData);
    CloseHandle(file);

    /* If we have a callback, call them one last time informing them of our
     * final progress (even if we failed). We'll ignore the request to cancel
     * since we'll be done in no time anyway. */
    if (callback) {
        callback(request->tag, totalBytesRead);
    }

    /* If we had a read failure, free up the ed2k hash (if we have one) and
     * inform our caller that we had a problem. */
    if (readFailed) {
        if (doED2k && ed2kHashes) {
            HeapFree(GetProcessHeap(), 0, ed2kHashes);
        }

        return -8;
    }

    /* Finalize all of the hashes that were selected and store the results in
     * the request result buffer. The order of the hashes are:
     *     0 - 15: ED2k
     *    16 - 19: CRC32
     *    20 - 35: MD5
     *    36 - 55: SHA1 */
    if (doED2k) {
        /* If we just had one block to process directly store the result of the
         * block in the result buffer. */
        if (ed2kBlocks == 1) {
            MD4_final(&ed2k, &request->result[0]);
        } else {
            /* Check to see if we were in the middle of a loop and finalize the
             * final pending block if we were. */
            if (ed2kLoopIdx > 0) {
                MD4_final(&ed2k, &ed2kHashes[ed2kBlockIdx * 16]);
            }

            /* Calculate the MD4 hash of the concatenated hashes from each ED2k
             * block. The resulting hash is the final ED2k hash. */
            MD4_init(&ed2k);
            MD4_update(&ed2k, ed2kHashes, ed2kHashLength);
            MD4_final(&ed2k, &request->result[0]);

            /* Don't forget to free our ED2k buffer. */
            HeapFree(GetProcessHeap(), 0, ed2kHashes);
        }
    }
    if (doCRC32) { CRC32_final(&crc32, &request->result[16]); }
    if (doMD5) { MD5_final(&md5, &request->result[20]); }
    if (doSHA1) { SHA1_final(&sha1, &request->result[36]); }

    return 0;
}