#define _CRT_SECURE_NO_DEPRECATE #include <time.h> #include <windows.h> #include "filewriter.h" #include "filewriter_immediate.h" #include "filewriter_sync.h" #include "filewriter_async.h" #include "filewriter_mt.h" int FileWriter::mMaxMemSize = 1024 * 1024; FileWriter::Type FileWriter::mType = IMMEDIATE; std::list<FileWriter*> FileWriter::mActiveFileWriters; BufferCallbackFunc FileWriter::mReadCallback = NULL; BufferCallbackFunc FileWriter::mWriteCallback = NULL; bool FileWriter::mAbortRequested = false; bool FileWriter::mUseSetFileValid = true; bool FileWriter::mUsePreAllocation = true; class CriticalSection { public : CriticalSection () { ::InitializeCriticalSection(&mCriticalSection); } ~CriticalSection () { ::DeleteCriticalSection(&mCriticalSection); } void Enter() { ::EnterCriticalSection(&mCriticalSection); } void Leave() { ::LeaveCriticalSection(&mCriticalSection); } private : CRITICAL_SECTION mCriticalSection; }; static CriticalSection sAbortCriticalSection; void FileWriter::SetMaxMemSize(int inMaxMemSize) { mMaxMemSize = inMaxMemSize; } void FileWriter::SetType(Type inType) { mType = inType; } FileWriter* FileWriter::GetFileWriter(const std::string& inName, long long inFileSize) { FileWriter* file_writer = NULL; // We need a critical section here, because the abort thread might kick in in and // start to access the active writers list before we are done updating it. sAbortCriticalSection.Enter(); { // Cleanup finished FileWriters first if (!mActiveFileWriters.empty()) { std::list<FileWriter*>::iterator i = mActiveFileWriters.begin(); while (i != mActiveFileWriters.end()) { std::list<FileWriter*>::iterator next = i; ++next; if ((*i)->IsFinished()) { delete *i; mActiveFileWriters.erase(i); } i = next; } } // Now see if we're going to make a new one if (!mAbortRequested) { if (mType == IMMEDIATE) file_writer = FileWriterImmediate::GetFileWriter(inName, inFileSize); else if (mType == SYNC) file_writer = FileWriterSync::GetFileWriter(inName, inFileSize); else if (mType == ASYNC) file_writer = FileWriterASync::GetFileWriter(inName, inFileSize); else if (mType == MT) file_writer = FileWriterMT::GetFileWriter(inName, inFileSize); if (file_writer) mActiveFileWriters.push_back(file_writer); } } sAbortCriticalSection.Leave(); // Abort has been requested, we'll stall the sync thread until the app is closed if (mAbortRequested) Sleep(INFINITE); return file_writer; } void FileWriter::Finalize() { if (mType == IMMEDIATE) FileWriterImmediate::Finalize(); else if (mType == SYNC) FileWriterSync::Finalize(); else if (mType == ASYNC) FileWriterASync::Finalize(); else if (mType == MT) FileWriterMT::Finalize(); // Subclasses have to make sure all FileWriters are finished at this point. // We need a critical section here, because the abort thread might kick in in and // start to access the active writers list before we are done updating it. sAbortCriticalSection.Enter(); { for (std::list<FileWriter*>::iterator i = mActiveFileWriters.begin(); i != mActiveFileWriters.end(); ++i) delete *i; mActiveFileWriters.clear(); } sAbortCriticalSection.Leave(); } void FileWriter::AbortSync() { // Critical section to make sure we don't request an abort while the Sync Thread is manipulating the active writers list sAbortCriticalSection.Enter(); { mAbortRequested = true; } sAbortCriticalSection.Leave(); bool all_acknowledged = false; while (!all_acknowledged) { all_acknowledged = true; sAbortCriticalSection.Enter(); { for (std::list<FileWriter*>::iterator i = mActiveFileWriters.begin(); i != mActiveFileWriters.end(); ++i) if (!(*i)->AbortAcknowledged()) all_acknowledged = false; } sAbortCriticalSection.Leave(); ::Sleep(1); } sAbortCriticalSection.Enter(); { for (std::list<FileWriter*>::iterator i = mActiveFileWriters.begin(); i != mActiveFileWriters.end(); ++i) (*i)->HandleAbort(); } sAbortCriticalSection.Leave(); // Cleanup to allow FileWriters to delete unfinished files if (mType == IMMEDIATE) FileWriterImmediate::Finalize(); else if (mType == SYNC) FileWriterSync::Finalize(); else if (mType == ASYNC) FileWriterASync::Finalize(); else if (mType == MT) FileWriterMT::Finalize(); } FileWriter::FileWriter(const std::string& inName, long long inFileSize) : mName(inName), mFileSize(inFileSize), mAbortAcknowledged(false) { } FileWriter::SfvdProto FileWriter::GetSetFileValidDataFunction() { // SetFileValidData is a Windows XP function, which we import when available so we can still run on Windows 2000. static SfvdProto SetFileValidDataFunction = NULL; static bool initialized = false; if (!initialized) { HMODULE kernel32_handle = ::GetModuleHandle("kernel32"); SetFileValidDataFunction = (SfvdProto) ::GetProcAddress(kernel32_handle, "SetFileValidData"); initialized = true; } return SetFileValidDataFunction; } bool FileWriter::IsAborting() { if (mAbortRequested) { mAbortAcknowledged = true; return true; } return false; } HANDLE FileWriter::CreateFile(bool inWriteable, bool inDoOverlappedIO, int inModTime) { int flags = FILE_FLAG_SEQUENTIAL_SCAN; if (!inWriteable) flags |= FILE_ATTRIBUTE_READONLY; if (inDoOverlappedIO) flags |= FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING; HANDLE handle = ::CreateFile(mName.c_str(), FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES, 0, NULL, CREATE_ALWAYS, flags, NULL); if (handle != INVALID_HANDLE_VALUE && mUsePreAllocation && mFileSize > 0) { // Allocate 5% extra, to take care of any line-ending expansion we may need to do // If not used, the extra bit will be truncated in the CloseFile function below. long long extended_file_size = (long long) (1.05 * mFileSize); int file_size_low = (int)(extended_file_size); long file_size_high = (long)(extended_file_size >> 32); ::SetFilePointer(handle, file_size_low, &file_size_high, FILE_BEGIN); ::SetEndOfFile(handle); if (mUseSetFileValid && GetSetFileValidDataFunction()) GetSetFileValidDataFunction()(handle, extended_file_size); ::SetFilePointer(handle, 0, NULL, FILE_BEGIN); } if (inModTime > 0) ChModTime(handle, inModTime); return handle; } bool FileWriter::CloseFile(HANDLE& ioHandle, long long inWritten) { bool res = true; // We need a critical section here, as this can be called from both the sync thread and the abort thread if (ioHandle != INVALID_HANDLE_VALUE) { if (inWritten > 0) { int file_size_low = (int)(inWritten); long file_size_high = (long)(inWritten >> 32); ::SetFilePointer(ioHandle, file_size_low, &file_size_high, FILE_BEGIN); } // Just to make sure the initial file_size wasn't too large, we truncate at the current file pointer before closing. ::SetEndOfFile(ioHandle); res = ::CloseHandle(ioHandle) != 0; ioHandle = INVALID_HANDLE_VALUE; } return res; } void FileWriter::DeleteFile() { DWORD attr = ::GetFileAttributes(mName.c_str()); attr &= ~FILE_ATTRIBUTE_READONLY; ::SetFileAttributes(mName.c_str(), attr); ::DeleteFile(mName.c_str()); } FILETIME FileWriter::FileTimeFromEpoch(int inEpoch) { // Due to the insane way Windows handles file-stamp times, and especially // Daylight Saving Time, we are going to need to perform some arcane // transformations to make sure we write the correct timestamp. // See http://search.cpan.org/~shay/Win32-UTCFileTime-1.49/lib/Win32/UTCFileTime.pm // for some background. LONGLONG time = inEpoch; // First, convert the epoch to a time_info struct, set the tm_isdst member to a // negative number, and call mktime which will set tm_isdst to the appropriate value // for the epoch date. tm* time_info = gmtime((__time64_t*) &time); time_info->tm_isdst = -1; mktime(time_info); bool epoch_is_dst = (time_info->tm_isdst != 0); // Next, we need to find out if DST is in effect at the current date, which // we can extract from GetTimeZoneInformation() TIME_ZONE_INFORMATION time_zone_info; bool now_is_dst = (GetTimeZoneInformation(&time_zone_info) == TIME_ZONE_ID_DAYLIGHT); // Now, if the epoch and current DST are different, we need to adjust by one hour // in either direction. It really boggles the mind. if (epoch_is_dst != now_is_dst) { if (now_is_dst) time -= 3600; else time += 3600; } // And finally, we can transform the epoch into a filetime by adding the number of seconds // between January 1, 1601 and January 1, 1970 and multiplying to the 100 nanoseconds scale // Windows uses. time += 11644473600; time *= 10000000; FILETIME ft; ft.dwLowDateTime = (DWORD) (time & 0xffffffff); ft.dwHighDateTime = (DWORD) (time >> 32); return ft; } bool FileWriter::ChModTime(HANDLE inHandle, int inModTime) { FILETIME ft = FileTimeFromEpoch(inModTime); return (SetFileTime(inHandle, NULL, NULL, &ft) != 0); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 6420 | Frank Compagner |
A number of improvements: - p4fs now supports the global -s (scripted output) option. - p4fs and P4fsV now support the modtime client option. - P4CHARSET is now correctly handled (though no full Unicode support yet). - Increased the maximum command line length for p4fs to the Windows maximum 32768. - Improved error handling. - Fixed a crash when cancelling a sync using the async or multithreaded writers. - P4fsV progressbar now behaves well when passing more than one filespec - P4fsV will now offer the option to overwrite any locally changed (but not checked out) files when it finds these during a sync (cannot clobber ...). - Made the P4fsV error dialog resizeable. - P4fsV Windows layout fixed so it works properly with all Windows style setings. - Ooh, and prettier icons too. |
||
#2 | 6280 | Frank Compagner | Added support for +w filetype | ||
#1 | 6187 | Frank Compagner | Added p4fs project |