diff options
Diffstat (limited to 'attic/include/boost/afio/v2/detail/impl/afio_iocp.ipp')
-rw-r--r-- | attic/include/boost/afio/v2/detail/impl/afio_iocp.ipp | 1957 |
1 files changed, 1957 insertions, 0 deletions
diff --git a/attic/include/boost/afio/v2/detail/impl/afio_iocp.ipp b/attic/include/boost/afio/v2/detail/impl/afio_iocp.ipp new file mode 100644 index 00000000..012d4aaa --- /dev/null +++ b/attic/include/boost/afio/v2/detail/impl/afio_iocp.ipp @@ -0,0 +1,1957 @@ +/* async_file_io +Provides a threadpool and asynchronous file i/o infrastructure based on Boost.ASIO, Boost.Iostreams and filesystem +(C) 2013-2014 Niall Douglas http://www.nedprod.com/ +File Created: Mar 2013 +*/ + +#ifdef WIN32 +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 6262) // Excessive stack usage +#endif + +BOOST_AFIO_V2_NAMESPACE_BEGIN + +namespace detail { + // Helper for opening files. Lightweight means open with no access, it can be faster. + static inline std::pair<NTSTATUS, HANDLE> ntcreatefile(handle_ptr dirh, BOOST_AFIO_V2_NAMESPACE::path path, file_flags flags, bool overlapped=true) noexcept + { + DWORD access=FILE_READ_ATTRIBUTES|SYNCHRONIZE, attribs=FILE_ATTRIBUTE_NORMAL; + DWORD fileshare=FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE; + DWORD creatdisp=0, ntflags=0x4000/*FILE_OPEN_FOR_BACKUP_INTENT*/; + if(!overlapped) + ntflags|=0x20/*FILE_SYNCHRONOUS_IO_NONALERT*/; + if(flags==file_flags::none) + + ntflags|=0x00200000/*FILE_OPEN_REPARSE_POINT*/; + + else + { + if(!!(flags & file_flags::int_opening_link)) + ntflags|=0x00200000/*FILE_OPEN_REPARSE_POINT*/; + if(!!(flags & file_flags::int_opening_dir)) + { + /* NOTE TO SELF: Never, ever, ever again request FILE_DELETE_CHILD perms. It subtly breaks renaming, enumeration + and a ton of other stuff in a really weird way. */ + ntflags|=0x01/*FILE_DIRECTORY_FILE*/; + access|=FILE_LIST_DIRECTORY|FILE_TRAVERSE; // FILE_READ_DATA|FILE_EXECUTE + // Match POSIX where read perms are sufficient to use this handle as a relative base for creating new entries + //access|=FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY; // FILE_WRITE_DATA|FILE_APPEND_DATA // Fails where user is not administrator + if(!!(flags & file_flags::read)) access|=GENERIC_READ; + // Write access probably means he wants to delete or rename self + if(!!(flags & file_flags::write)) access|=GENERIC_WRITE|DELETE; + // Windows doesn't like opening directories without buffering. + if(!!(flags & file_flags::os_direct)) flags=flags & ~file_flags::os_direct; + } + else + { + ntflags|=0x040/*FILE_NON_DIRECTORY_FILE*/; + if(!!(flags & file_flags::append)) access|=FILE_APPEND_DATA|DELETE; + else + { + if(!!(flags & file_flags::read)) access|=GENERIC_READ; + if(!!(flags & file_flags::write)) access|=GENERIC_WRITE|DELETE; + } + if(!!(flags & file_flags::will_be_sequentially_accessed)) + ntflags|=0x00000004/*FILE_SEQUENTIAL_ONLY*/; + else if(!!(flags & file_flags::will_be_randomly_accessed)) + ntflags|=0x00000800/*FILE_RANDOM_ACCESS*/; + if(!!(flags & file_flags::temporary_file)) + attribs|=FILE_ATTRIBUTE_TEMPORARY; + } + if(!!(flags & file_flags::delete_on_close) && (!!(flags & file_flags::create_only_if_not_exist) || !!(flags & file_flags::int_file_share_delete))) + { + ntflags|=0x00001000/*FILE_DELETE_ON_CLOSE*/; + access|=DELETE; + } + if(!!(flags & file_flags::int_file_share_delete)) + access|=DELETE; + } + if(!!(flags & file_flags::create_only_if_not_exist)) + { + creatdisp|=0x00000002/*FILE_CREATE*/; + } + else if(!!(flags & file_flags::create)) + { + creatdisp|=0x00000003/*FILE_OPEN_IF*/; + } + else if(!!(flags & file_flags::truncate)) creatdisp|=0x00000005/*FILE_OVERWRITE_IF*/; + else creatdisp|=0x00000001/*FILE_OPEN*/; + if(!!(flags & file_flags::os_direct)) ntflags|=0x00000008/*FILE_NO_INTERMEDIATE_BUFFERING*/; + if(!!(flags & file_flags::always_sync)) ntflags|=0x00000002/*FILE_WRITE_THROUGH*/; + + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + HANDLE h=nullptr; + IO_STATUS_BLOCK isb={ 0 }; + OBJECT_ATTRIBUTES oa={sizeof(OBJECT_ATTRIBUTES)}; + oa.RootDirectory=dirh ? dirh->native_handle() : nullptr; + UNICODE_STRING _path; + // If relative path, or symlinked DOS path, use case insensitive + bool isSymlinkedDosPath=(path.native().size()>3 && path.native()[0]=='\\' && path.native()[1]=='?' && path.native()[2]=='?' && path.native()[3]=='\\'); + oa.Attributes=(path.native()[0]!='\\' || isSymlinkedDosPath) ? 0x40/*OBJ_CASE_INSENSITIVE*/ : 0; + //if(!!(flags & file_flags::int_opening_link)) + // oa.Attributes|=0x100/*OBJ_OPENLINK*/; + _path.Buffer=const_cast<path::value_type *>(path.c_str()); + _path.MaximumLength=(_path.Length=(USHORT) (path.native().size()*sizeof(path::value_type)))+sizeof(path::value_type); + oa.ObjectName=&_path; + LARGE_INTEGER AllocationSize={0}; + return std::make_pair(NtCreateFile(&h, access, &oa, &isb, &AllocationSize, attribs, fileshare, + creatdisp, ntflags, NULL, 0), h); + } + + struct win_actual_lock_file; + struct win_lock_file : public lock_file<win_actual_lock_file> + { + asio::windows::object_handle ev; + win_lock_file(handle *_h=nullptr) : lock_file<win_actual_lock_file>(_h), ev(_h->parent()->threadsource()->io_service()) + { + HANDLE evh; + BOOST_AFIO_ERRHWIN(INVALID_HANDLE_VALUE!=(evh=CreateEvent(nullptr, true, false, nullptr))); + ev.assign(evh); + } + }; + + // Two modes of calling, either a handle or a leafname + dir handle + static inline bool isDeletedFile(path::string_type leafname) + { + if(leafname.size()==64+6 && !leafname.compare(0, 6, L".afiod")) + { + // Could be one of our "deleted" files, is he ".afiod" + all hex? + for(size_t n=6; n<leafname.size(); n++) + { + auto c=leafname[n]; + if(!((c>='0' && c<='9') || (c>='a' && c<='f'))) + return false; + } + return true; + } + return false; + } + static inline bool isDeletedFile(handle_ptr h) + { + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + IO_STATUS_BLOCK isb={ 0 }; + BOOST_AFIO_TYPEALIGNMENT(8) FILE_ALL_INFORMATION fai; + NTSTATUS ntstat=NtQueryInformationFile(h->native_handle(), &isb, &fai.StandardInformation, sizeof(fai.StandardInformation), FileStandardInformation); + // If the query succeeded and delete is pending, filter out this entry + if(!ntstat && fai.StandardInformation.DeletePending) + return true; + else + return false; + } + + static inline path MountPointFromHandle(HANDLE h) + { + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769]; + IO_STATUS_BLOCK isb={ 0 }; + NTSTATUS ntstat; + UNICODE_STRING *objectpath=(UNICODE_STRING *) buffer; + ULONG length; + ntstat=NtQueryObject(h, ObjectNameInformation, objectpath, sizeof(buffer), &length); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h, FALSE, NULL); + BOOST_AFIO_ERRHNT(ntstat); + // Now get the subpath of our handle within its mount + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer2[sizeof(FILE_NAME_INFORMATION)/sizeof(path::value_type)+32769]; + FILE_NAME_INFORMATION *fni=(FILE_NAME_INFORMATION *) buffer2; + ntstat=NtQueryInformationFile(h, &isb, fni, sizeof(buffer2), FileNameInformation); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h, FALSE, NULL); + BOOST_AFIO_ERRHNT(ntstat); + // The mount path will be the remainder of the objectpath after removing the + // common ending + size_t remainder=(objectpath->Length-fni->FileNameLength)/sizeof(path::value_type); + return path::string_type(objectpath->Buffer, remainder); + } + + static inline path::string_type VolumeNameFromMountPoint(path mountpoint) + { + HANDLE vh; + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769]; + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type volumename[64]; + // Enumerate every volume in the system and the device it maps until we find ours + BOOST_AFIO_ERRHWIN(INVALID_HANDLE_VALUE!=(vh=FindFirstVolume(volumename, sizeof(volumename)/sizeof(volumename[0])))); + auto unvh=Undoer([&vh]{FindVolumeClose(vh);}); + size_t volumenamelen; + do + { + DWORD len; + volumenamelen=wcslen(volumename); + volumename[volumenamelen-1]=0; // remove the trailing backslash + BOOST_AFIO_ERRHWIN((len=QueryDosDevice(volumename+4, buffer, sizeof(buffer)/sizeof(buffer[0])))); + if(!mountpoint.native().compare(0, len, buffer)) + return path::string_type(volumename, volumenamelen-1); + } while(FindNextVolume(vh, volumename, sizeof(volumename)/sizeof(volumename[0]))); + return path::string_type(); + } +} + +BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC filesystem::path normalise_path(path p, path_normalise type) +{ + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + bool isSymlinkedDosPath=(p.native().size()>3 && p.native()[0]=='\\' && p.native()[1]=='?' && p.native()[2]=='?' && p.native()[3]=='\\'); + bool needToOpen=!isSymlinkedDosPath || path_normalise::guid_all==type; + // Path is probably \Device\Harddisk... + NTSTATUS ntstat; + HANDLE h=nullptr; + if(needToOpen) + { + // Open with no rights and no access + std::tie(ntstat, h)=detail::ntcreatefile(handle_ptr(), p, file_flags::none, false); + BOOST_AFIO_ERRHNTFN(ntstat, [&p]{return p;}); + } + auto unh=detail::Undoer([&h]{if(h) CloseHandle(h);}); + path mountpoint; + path::string_type volumename; + if(isSymlinkedDosPath) + { + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[64]; + mountpoint=path(p.native().substr(0, p.native().find('\\', 4)+1), path::direct()); + BOOST_AFIO_ERRHWIN(GetVolumeNameForVolumeMountPoint(mountpoint.c_str()+4, buffer, 64)); + volumename=buffer; + // Trim the mount point's ending slash + filesystem::path::string_type &me=const_cast<filesystem::path::string_type &>(mountpoint.native()); + me.resize(me.size()-1); + } + else + { + mountpoint=detail::MountPointFromHandle(h); + volumename=detail::VolumeNameFromMountPoint(mountpoint); + } + if(path_normalise::guid_volume==type) + return filesystem::path(volumename)/p.native().substr(mountpoint.native().size()); + else if(path_normalise::guid_all==type) + { + FILE_OBJECTID_BUFFER fob; + DWORD out; + BOOST_AFIO_ERRHWIN(DeviceIoControl(h, FSCTL_CREATE_OR_GET_OBJECT_ID, nullptr, 0, &fob, sizeof(fob), &out, nullptr)); + GUID *guid=(GUID *) &fob.ObjectId; + path::value_type buffer[64]; + swprintf_s(buffer, 64, L"{%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x}", guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + return filesystem::path(volumename)/filesystem::path::string_type(buffer, 38); + } + else + { + bool needsExtendedPrefix=false; + // Are there any illegal Win32 characters in here? + static BOOST_CONSTEXPR_OR_CONST char reserved_chars[]="\"*/:<>?|"; + for(size_t n=isSymlinkedDosPath ? p.native().find('\\', 5) : 0; !needsExtendedPrefix && n<p.native().size(); n++) + { + if(p.native()[n]>=1 && p.native()[n]<=31) + needsExtendedPrefix=true; + for(size_t x=0; x<sizeof(reserved_chars); x++) + if(p.native()[n]==reserved_chars[x]) + { + needsExtendedPrefix=true; + break; + } + } + if(!needsExtendedPrefix) + { + // Are any segments of the filename a reserved name? + static constexpr const wchar_t *reserved_names[]={L"CON", L"PRN", L"AUX", L"NUL", L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9", L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9"}; + for(auto &i : p) + { + auto s=i.stem().native(); + if(s[0]==' ' || s[s.size()-1]=='.') + { + needsExtendedPrefix=true; + break; + } + for(size_t n=0; n<sizeof(reserved_names)/sizeof(reserved_names[0]); n++) + { + if(s==reserved_names[n]) + { + needsExtendedPrefix=true; + break; + } + } + if(needsExtendedPrefix) + break; + } + } + + DWORD len; + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769]; + if(volumename.back()!='\\') volumename.push_back('\\'); + BOOST_AFIO_ERRHWIN(GetVolumePathNamesForVolumeName(volumename.c_str(), buffer, sizeof(buffer)/sizeof(buffer[0]), &len)); + // Read the returned DOS mount points into a vector and sort them by size + std::vector<path::string_type> dosmountpoints; + for(path::value_type *x=buffer; *x; x+=wcslen(x)+1) + dosmountpoints.push_back(path::string_type(x)); + if(dosmountpoints.empty()) + BOOST_AFIO_THROW(std::runtime_error("No Win32 mount points returned for volume path")); + std::sort(dosmountpoints.begin(), dosmountpoints.end(), [](const path::string_type &a, const path::string_type &b){return a.size()<b.size();}); + filesystem::path ret(dosmountpoints.front()); + ret/=p.native().substr(mountpoint.native().size()+1); + if(ret.native().size()>=260) + needsExtendedPrefix=true; + if(needsExtendedPrefix) + return filesystem::path("\\\\?")/ret; + else + return ret; + } +} + +namespace detail +{ + struct async_io_handle_windows : public handle + { + std::unique_ptr<asio::windows::random_access_handle> h; + void *myid; + bool has_been_added, SyncOnClose; + HANDLE sectionh; + typedef spinlock<bool> pathlock_t; + mutable pathlock_t pathlock; BOOST_AFIO_V2_NAMESPACE::path _path; + std::unique_ptr<win_lock_file> lockfile; + + static HANDLE int_checkHandle(HANDLE h, const BOOST_AFIO_V2_NAMESPACE::path &path) + { + BOOST_AFIO_ERRHWINFN(INVALID_HANDLE_VALUE!=h, [&path]{return path;}); + return h; + } + async_io_handle_windows(dispatcher *_parent, const BOOST_AFIO_V2_NAMESPACE::path &path, file_flags flags) : handle(_parent, flags), + myid(nullptr), has_been_added(false), SyncOnClose(false), sectionh(nullptr), _path(path) { } + inline async_io_handle_windows(dispatcher *_parent, const BOOST_AFIO_V2_NAMESPACE::path &path, file_flags flags, bool _SyncOnClose, HANDLE _h); + inline handle_ptr int_verifymyinode(); + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void close() override final + { + BOOST_AFIO_DEBUG_PRINT("D %p\n", this); + if(sectionh) + { + BOOST_AFIO_ERRHWINFN(CloseHandle(sectionh), [this]{return path();}); + sectionh=nullptr; + } + if(h) + { + // Windows doesn't provide an async fsync so do it synchronously + if(SyncOnClose && write_count_since_fsync()) + BOOST_AFIO_ERRHWINFN(FlushFileBuffers(myid), [this]{return path();}); + h->close(); + h.reset(); + } + // Deregister AFTER close of file handle + if(has_been_added) + { + parent()->int_del_io_handle(myid); + has_been_added=false; + } + myid=nullptr; + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC open_states is_open() const override final + { + if(!myid) + return open_states::closed; + return available_to_directory_cache() ? open_states::opendir : open_states::open; + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void *native_handle() const override final { return myid; } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path(bool refresh=false) override final + { + if(refresh) + { + if(!myid) + { +#if 0 + auto mycontainer=container(); + if(mycontainer) + mycontainer->path(true); +#endif + } + else + { + BOOST_AFIO_V2_NAMESPACE::path newpath; + if(!isDeletedFile(shared_from_this())) + { + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + HANDLE h=myid; + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769]; + UNICODE_STRING *volumepath=(UNICODE_STRING *) buffer; + ULONG bufferlength; + NTSTATUS ntstat=NtQueryObject(h, ObjectNameInformation, volumepath, sizeof(buffer), &bufferlength); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h, FALSE, NULL); + BOOST_AFIO_ERRHNT(ntstat); + newpath=path::string_type(volumepath->Buffer, volumepath->Length/sizeof(path::value_type)); + } + bool changed=false; + BOOST_AFIO_V2_NAMESPACE::path oldpath; + { + lock_guard<pathlock_t> g(pathlock); + if((changed=(newpath!=_path))) + { + oldpath=std::move(_path); + _path=newpath; + } + } + if(changed) + { + // Currently always zap the container if path changes + if(this->dirh) + this->dirh.reset(); + // Need to update the directory cache + if(available_to_directory_cache()) + parent()->int_directory_cached_handle_path_changed(oldpath, newpath, shared_from_this()); +#if 0 + // Perhaps also need to update my container + auto containerh(container()); + if(containerh) + containerh->path(true); +#endif + } + } + } + lock_guard<pathlock_t> g(pathlock); + return _path; + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path() const override final + { + lock_guard<pathlock_t> g(pathlock); + return _path; + } + + // You can't use shared_from_this() in a constructor so ... + void do_add_io_handle_to_parent() + { + if(myid) + { + // Canonicalise my path + auto newpath=path(true); + parent()->int_add_io_handle(myid, shared_from_this()); + has_been_added=true; + // If I'm the right sort of directory, register myself with the dircache. path(true) may have + // already done this, but it's not much harm to repeat myself. + if(available_to_directory_cache()) + parent()->int_directory_cached_handle_path_changed(BOOST_AFIO_V2_NAMESPACE::path(), newpath, shared_from_this()); + } + if(!!(flags() & file_flags::hold_parent_open) && !(flags() & file_flags::int_hold_parent_open_nested)) + dirh=int_verifymyinode(); + } + ~async_io_handle_windows() + { + async_io_handle_windows::close(); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC directory_entry direntry(metadata_flags wanted) override final + { + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + stat_t stat(nullptr); + IO_STATUS_BLOCK isb={ 0 }; + NTSTATUS ntstat; + + HANDLE h=myid; + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[sizeof(FILE_ALL_INFORMATION)/sizeof(path::value_type)+32769]; + buffer[0]=0; + FILE_ALL_INFORMATION &fai=*(FILE_ALL_INFORMATION *)buffer; + FILE_FS_SECTOR_SIZE_INFORMATION ffssi={0}; + bool needInternal=!!(wanted&metadata_flags::ino); + bool needBasic=(!!(wanted&metadata_flags::type) || !!(wanted&metadata_flags::atim) || !!(wanted&metadata_flags::mtim) || !!(wanted&metadata_flags::ctim) || !!(wanted&metadata_flags::birthtim) || !!(wanted&metadata_flags::sparse) || !!(wanted&metadata_flags::compressed) || !!(wanted&metadata_flags::reparse_point)); + bool needStandard=(!!(wanted&metadata_flags::nlink) || !!(wanted&metadata_flags::size) || !!(wanted&metadata_flags::allocated) || !!(wanted&metadata_flags::blocks)); + // It's not widely known that the NT kernel supplies a stat() equivalent i.e. get me everything in a single syscall + // However fetching FileAlignmentInformation which comes with FILE_ALL_INFORMATION is slow as it touches the device driver, + // so only use if we need more than one item + if(((int) needInternal+(int) needBasic+(int) needStandard)>=2) + { + ntstat=NtQueryInformationFile(h, &isb, &fai, sizeof(buffer), FileAllInformation); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h, FALSE, NULL); + BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();}); + } + else + { + if(needInternal) + { + ntstat=NtQueryInformationFile(h, &isb, &fai.InternalInformation, sizeof(fai.InternalInformation), FileInternalInformation); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h, FALSE, NULL); + BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();}); + } + if(needBasic) + { + ntstat=NtQueryInformationFile(h, &isb, &fai.BasicInformation, sizeof(fai.BasicInformation), FileBasicInformation); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h, FALSE, NULL); + BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();}); + } + if(needStandard) + { + ntstat=NtQueryInformationFile(h, &isb, &fai.StandardInformation, sizeof(fai.StandardInformation), FileStandardInformation); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h, FALSE, NULL); + BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();}); + } + } + if(!!(wanted&metadata_flags::blocks) || !!(wanted&metadata_flags::blksize)) + { + ntstat=NtQueryVolumeInformationFile(h, &isb, &ffssi, sizeof(ffssi), FileFsSectorSizeInformation); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h, FALSE, NULL); + //BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();}); + if(0/*STATUS_SUCCESS*/!=ntstat) + { + // Windows XP and Vista don't support the FILE_FS_SECTOR_SIZE_INFORMATION + // API, so we'll just hardcode 512 bytes + ffssi.PhysicalBytesPerSectorForPerformance=512; + } + } + if(!!(wanted&metadata_flags::ino)) { stat.st_ino=fai.InternalInformation.IndexNumber.QuadPart; } + if(!!(wanted&metadata_flags::type)) + { + ULONG ReparsePointTag=fai.EaInformation.ReparsePointTag; + // We need to get its reparse tag to see if it's a symlink + if(fai.BasicInformation.FileAttributes&FILE_ATTRIBUTE_REPARSE_POINT && !ReparsePointTag) + { + BOOST_AFIO_TYPEALIGNMENT(8) char buffer[sizeof(REPARSE_DATA_BUFFER)+32769]; + DWORD written=0; + REPARSE_DATA_BUFFER *rpd=(REPARSE_DATA_BUFFER *) buffer; + memset(rpd, 0, sizeof(*rpd)); + BOOST_AFIO_ERRHWINFN(DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, rpd, sizeof(buffer), &written, NULL), [this]{return path();}); + ReparsePointTag=rpd->ReparseTag; + } + stat.st_type=windows_nt_kernel::to_st_type(fai.BasicInformation.FileAttributes, ReparsePointTag); + } + if(!!(wanted&metadata_flags::nlink)) { stat.st_nlink=(int16_t) fai.StandardInformation.NumberOfLinks; } + if(!!(wanted&metadata_flags::atim)) { stat.st_atim=to_timepoint(fai.BasicInformation.LastAccessTime); } + if(!!(wanted&metadata_flags::mtim)) { stat.st_mtim=to_timepoint(fai.BasicInformation.LastWriteTime); } + if(!!(wanted&metadata_flags::ctim)) { stat.st_ctim=to_timepoint(fai.BasicInformation.ChangeTime); } + if(!!(wanted&metadata_flags::size)) { stat.st_size=fai.StandardInformation.EndOfFile.QuadPart; } + if(!!(wanted&metadata_flags::allocated)) { stat.st_allocated=fai.StandardInformation.AllocationSize.QuadPart; } + if(!!(wanted&metadata_flags::blocks)) { stat.st_blocks=fai.StandardInformation.AllocationSize.QuadPart/ffssi.PhysicalBytesPerSectorForPerformance; } + if(!!(wanted&metadata_flags::blksize)) { stat.st_blksize=(uint16_t) ffssi.PhysicalBytesPerSectorForPerformance; } + if(!!(wanted&metadata_flags::birthtim)) { stat.st_birthtim=to_timepoint(fai.BasicInformation.CreationTime); } + if(!!(wanted&metadata_flags::sparse)) { stat.st_sparse=!!(fai.BasicInformation.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE); } + if(!!(wanted&metadata_flags::compressed)) { stat.st_compressed=!!(fai.BasicInformation.FileAttributes & FILE_ATTRIBUTE_COMPRESSED); } + if(!!(wanted&metadata_flags::reparse_point)) { stat.st_reparse_point=!!(fai.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); } + return directory_entry(const_cast<async_io_handle_windows *>(this)->path(true).filename().native(), stat, wanted); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path target() override final + { + if(!opened_as_symlink()) + return path(); + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + using windows_nt_kernel::REPARSE_DATA_BUFFER; + BOOST_AFIO_TYPEALIGNMENT(8) char buffer[sizeof(REPARSE_DATA_BUFFER)+32769]; + DWORD written=0; + REPARSE_DATA_BUFFER *rpd=(REPARSE_DATA_BUFFER *) buffer; + memset(rpd, 0, sizeof(*rpd)); + BOOST_AFIO_ERRHWIN(DeviceIoControl(myid, FSCTL_GET_REPARSE_POINT, NULL, 0, rpd, sizeof(buffer), &written, NULL)); + switch(rpd->ReparseTag) + { + case IO_REPARSE_TAG_MOUNT_POINT: + return BOOST_AFIO_V2_NAMESPACE::path(path::string_type(rpd->MountPointReparseBuffer.PathBuffer+rpd->MountPointReparseBuffer.SubstituteNameOffset/sizeof(BOOST_AFIO_V2_NAMESPACE::path::value_type), rpd->MountPointReparseBuffer.SubstituteNameLength/sizeof(BOOST_AFIO_V2_NAMESPACE::path::value_type)), BOOST_AFIO_V2_NAMESPACE::path::direct()); + case IO_REPARSE_TAG_SYMLINK: + return BOOST_AFIO_V2_NAMESPACE::path(path::string_type(rpd->SymbolicLinkReparseBuffer.PathBuffer+rpd->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(BOOST_AFIO_V2_NAMESPACE::path::value_type), rpd->SymbolicLinkReparseBuffer.SubstituteNameLength/sizeof(BOOST_AFIO_V2_NAMESPACE::path::value_type)), BOOST_AFIO_V2_NAMESPACE::path::direct()); + } + BOOST_AFIO_THROW(system_error(EINVAL, generic_category())); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::unique_ptr<mapped_file> map_file(size_t length, off_t offset, bool read_only) override final + { + if(length==(size_t)-1) + length=0; + if(!sectionh) + { + lock_guard<pathlock_t> g(pathlock); + if(!sectionh) + { + HANDLE h; + DWORD prot=!!(flags() & file_flags::write) ? PAGE_READWRITE : PAGE_READONLY; + if(INVALID_HANDLE_VALUE!=(h=CreateFileMapping(myid, NULL, prot, 0, 0, nullptr))) + sectionh=h; + } + } + if(sectionh) + { + DWORD prot=FILE_MAP_READ; + if(!read_only && !!(flags() & file_flags::write)) + prot=FILE_MAP_WRITE; + void *mapaddr=nullptr; + if((mapaddr=MapViewOfFile(sectionh, prot, (DWORD)(offset>>32), (DWORD)(offset&0xffffffff), length))) + { + return detail::make_unique<mapped_file>(shared_from_this(), mapaddr, length, offset); + } + } + return nullptr; + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void link(const path_req &_req) override final + { + if(!myid) + BOOST_AFIO_THROW(std::invalid_argument("Currently cannot hard link a closed file by handle.")); + path_req req(_req); + auto newdirh=decode_relative_path<async_file_io_dispatcher_windows, async_io_handle_windows>(req); + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + IO_STATUS_BLOCK isb={ 0 }; + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769]; + FILE_LINK_INFORMATION *fni=(FILE_LINK_INFORMATION *) buffer; + fni->ReplaceIfExists=false; + fni->RootDirectory=newdirh ? newdirh->native_handle() : nullptr; + fni->FileNameLength=(ULONG)(req.path.native().size()*sizeof(path::value_type)); + memcpy(fni->FileName, req.path.c_str(), fni->FileNameLength); + BOOST_AFIO_ERRHNTFN(NtSetInformationFile(myid, &isb, fni, sizeof(FILE_LINK_INFORMATION)+fni->FileNameLength, FileLinkInformation), [this]{return path();}); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlink() override final + { + if(!myid) + BOOST_AFIO_THROW(std::invalid_argument("Currently cannot unlink a closed file by handle.")); + windows_nt_kernel::init(); + using namespace windows_nt_kernel; +#if 0 + // Despite what is claimed on the internet, NtDeleteFile does NOT delete the file immediately. + // Moreover, it cannot delete directories nor junction points, so we'll go the more proper path. + OBJECT_ATTRIBUTES oa={sizeof(OBJECT_ATTRIBUTES)}; + oa.RootDirectory=myid; + UNICODE_STRING _path; + path::value_type c=0; + _path.Buffer=&c; + _path.MaximumLength=(_path.Length=0)+sizeof(path::value_type); + oa.ObjectName=&_path; + //if(opened_as_symlink()) + // oa.Attributes=0x100/*OBJ_OPENLINK*/; // Seems to dislike deleting junctions points without this + NTSTATUS ntstat=NtDeleteFile(&oa); + // Returns this error if we are deleting a junction point + if(ntstat!=0xC0000278/*STATUS_IO_REPARSE_DATA_INVALID*/) + BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();}); +#else + IO_STATUS_BLOCK isb={ 0 }; + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769+sizeof(FILE_NAME_INFORMATION)/sizeof(path::value_type)]; + // This is done in two steps to stop annoying temporary failures + // Firstly, get where I am within my volume, NtQueryObject returns too much so simply fetch my current name + // Then try to rename myself to the closest to the root as possible with a .afiodXXXXX crypto random name + // If I fail to rename myself there, try the next directory up, usually at some point I'll find some directory + // I'm allowed write to + FILE_NAME_INFORMATION *fni=(FILE_NAME_INFORMATION *) buffer; + BOOST_AFIO_V2_NAMESPACE::path mypath; + bool success=false; + do + { + auto _randomname(".afiod"+utils::random_string(32 /* 128 bits */)); + filesystem::path::string_type randomname(_randomname.begin(), _randomname.end()); + mypath=path(true); + BOOST_AFIO_ERRHNTFN(NtQueryInformationFile(myid, &isb, fni, sizeof(buffer), FileNameInformation), [this]{return path(); }); + filesystem::path myloc(filesystem::path::string_type(fni->FileName, fni->FileNameLength/sizeof(path::value_type))), prefix(mypath.native()); + const_cast<filesystem::path::string_type &>(prefix.native()).resize(prefix.native().size()-myloc.native().size()); + auto try_rename=[&]{ + FILE_RENAME_INFORMATION *fri=(FILE_RENAME_INFORMATION *) buffer; + fri->ReplaceIfExists=false; + fri->RootDirectory=nullptr; // rename to the same directory + size_t n=prefix.make_preferred().native().size(); + memcpy(fri->FileName, prefix.c_str(), prefix.native().size()*sizeof(path::value_type)); + fri->FileName[n++]='\\'; + memcpy(fri->FileName+n, randomname.c_str(), randomname.size()*sizeof(path::value_type)); + n+=randomname.size(); + fri->FileName[n]=0; + fri->FileNameLength=(ULONG) (n*sizeof(path::value_type)); + NTSTATUS ntstat=NtSetInformationFile(myid, &isb, fri, sizeof(FILE_RENAME_INFORMATION)+fri->FileNameLength, FileRenameInformation); + // Access denied may come back if we can't rename to the location or we are renaming a directory + // and that directory contains open files. We can't tell the difference, so keep going + if(!ntstat) + success=true; +#if 0 + else + std::wcout << "unlink() failed to rename to " << fri->FileName << " due to " << ntstat << std::endl; +#endif + return ntstat; + }; + if(try_rename()) + { + myloc=myloc.parent_path(); + for(auto &fragment : myloc) + { + prefix/=fragment; + if(fragment.native().size()>1 && !try_rename()) + break; + } + } + } while(!success && mypath!=path(true)); + // By this point maybe the file/directory was renamed, maybe it was not. + FILE_BASIC_INFORMATION fbi={ 0 }; + fbi.FileAttributes=FILE_ATTRIBUTE_HIDDEN; + NtSetInformationFile(myid, &isb, &fbi, sizeof(fbi), FileBasicInformation); + FILE_DISPOSITION_INFORMATION fdi={ true }; + BOOST_AFIO_ERRHNTFN(NtSetInformationFile(myid, &isb, &fdi, sizeof(fdi), FileDispositionInformation), [this]{return path();}); +#endif + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void atomic_relink(const path_req &_req) override final + { + if(!myid) + BOOST_AFIO_THROW(std::invalid_argument("Currently cannot relink a closed file by handle.")); + path_req req(_req); + auto newdirh=decode_relative_path<async_file_io_dispatcher_windows, async_io_handle_windows>(req); + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + IO_STATUS_BLOCK isb={ 0 }; + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769]; + FILE_RENAME_INFORMATION *fni=(FILE_RENAME_INFORMATION *) buffer; + fni->ReplaceIfExists=true; + fni->RootDirectory=newdirh ? newdirh->native_handle() : nullptr; + fni->FileNameLength=(ULONG)(req.path.native().size()*sizeof(path::value_type)); + memcpy(fni->FileName, req.path.c_str(), fni->FileNameLength); + BOOST_AFIO_ERRHNTFN(NtSetInformationFile(myid, &isb, fni, sizeof(FILE_RENAME_INFORMATION)+fni->FileNameLength, FileRenameInformation), [this]{return path();}); + } + }; + inline async_io_handle_windows::async_io_handle_windows(dispatcher *_parent, + const BOOST_AFIO_V2_NAMESPACE::path &path, file_flags flags, bool _SyncOnClose, HANDLE _h) + : handle(_parent, flags), + h(new asio::windows::random_access_handle(_parent->p->pool->io_service(), int_checkHandle(_h, path))), myid(_h), + has_been_added(false), SyncOnClose(_SyncOnClose), sectionh(nullptr), _path(path) + { + if(!!(flags & file_flags::os_lockable)) + lockfile=process_lockfile_registry::open<win_lock_file>(this); + } + + class async_file_io_dispatcher_windows : public dispatcher + { + template<class Impl, class Handle> friend handle_ptr detail::decode_relative_path(path_req &req, bool force_absolute); + friend class dispatcher; + friend class directory_entry; + friend void directory_entry::_int_fetch(metadata_flags wanted, handle_ptr dirh); + friend struct async_io_handle_windows; + size_t pagesize; + handle_ptr decode_relative_path(path_req &req, bool force_absolute=false) + { + return detail::decode_relative_path<async_file_io_dispatcher_windows, async_io_handle_windows>(req, force_absolute); + } + + // Called in unknown thread + completion_returntype dodir(size_t id, future<> op, path_req req) + { + req.flags=fileflags(req.flags)|file_flags::int_opening_dir|file_flags::read; + // TODO FIXME: Currently file_flags::create may duplicate handles in the dirhcache + if(!(req.flags & file_flags::unique_directory_handle) && !!(req.flags & file_flags::read) && !(req.flags & file_flags::write) && !(req.flags & (file_flags::create|file_flags::create_only_if_not_exist))) + { + path_req req2(req); + // Return a copy of the one in the dir cache if available as a fast path + decode_relative_path(req2, true); + try + { + return std::make_pair(true, p->get_handle_to_dir(this, id, req2, &async_file_io_dispatcher_windows::dofile)); + } + catch(...) + { + // fall through + } + } + // This will add itself to the dir cache if it's eligible + return dofile(id, op, req); + } + // Called in unknown thread + completion_returntype dounlink(bool is_dir, size_t id, future<> op, path_req req) + { + req.flags=fileflags(req.flags); + // Deleting the input op? + if(req.path.empty()) + { + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + if(p->is_open()!=handle::open_states::closed) + { + p->unlink(); + return std::make_pair(true, op.get_handle()); + } + } + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + auto dirh=decode_relative_path(req); +#if 0 + OBJECT_ATTRIBUTES oa={sizeof(OBJECT_ATTRIBUTES)}; + oa.RootDirectory=dirh ? dirh->native_handle() : nullptr; + UNICODE_STRING _path; + // If relative path, or symlinked DOS path, use case insensitive + bool isSymlinkedDosPath=(req.path.native().size()>3 && req.path.native()[0]=='\\' && req.path.native()[1]=='?' && req.path.native()[2]=='?' && req.path.native()[3]=='\\'); + oa.Attributes=(req.path.native()[0]!='\\' || isSymlinkedDosPath) ? 0x40/*OBJ_CASE_INSENSITIVE*/ : 0; + _path.Buffer=const_cast<path::value_type *>(req.path.c_str()); + _path.MaximumLength=(_path.Length=(USHORT) (req.path.native().size()*sizeof(path::value_type)))+sizeof(path::value_type); + oa.ObjectName=&_path; + BOOST_AFIO_ERRHNTFN(NtDeleteFile(&oa), [&req]{return req.path;}); +#else + // Open the file with DeleteOnClose, renaming it before closing the handle. + NTSTATUS ntstat; + HANDLE temph; + req.flags=req.flags|file_flags::delete_on_close|file_flags::int_file_share_delete; + std::tie(ntstat, temph)=ntcreatefile(dirh, req.path, req.flags, false); + BOOST_AFIO_ERRHNTFN(ntstat, [&req]{return req.path;}); + auto untemph=detail::Undoer([&temph]{if(temph) CloseHandle(temph);}); + IO_STATUS_BLOCK isb={ 0 }; + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769]; + FILE_RENAME_INFORMATION *fni=(FILE_RENAME_INFORMATION *) buffer; + fni->ReplaceIfExists=false; + fni->RootDirectory=nullptr; // same directory + auto randompath(".afiod"+utils::random_string(32 /* 128 bits */)); + fni->FileNameLength=(ULONG)(randompath.size()*sizeof(path::value_type)); + for(size_t n=0; n<randompath.size(); n++) + fni->FileName[n]=randompath[n]; + fni->FileName[randompath.size()]=0; + ntstat=NtSetInformationFile(temph, &isb, fni, sizeof(FILE_RENAME_INFORMATION)+fni->FileNameLength, FileRenameInformation); + // Access denied may come back if the directory contains any files with open handles + // If that happens, ignore and mark to delete anyway + //if(ntstat==0xC0000022/*STATUS_ACCESS_DENIED*/) + FILE_BASIC_INFORMATION fbi={ 0 }; + fbi.FileAttributes=FILE_ATTRIBUTE_HIDDEN; + NtSetInformationFile(temph, &isb, &fbi, sizeof(fbi), FileBasicInformation); + FILE_DISPOSITION_INFORMATION fdi={ true }; + BOOST_AFIO_ERRHNTFN(NtSetInformationFile(temph, &isb, &fdi, sizeof(fdi), FileDispositionInformation), [this]{return path(); }); +#endif + return std::make_pair(true, op.get_handle()); + } + // Called in unknown thread + completion_returntype dormdir(size_t id, future<> op, path_req req) + { + req.flags=fileflags(req.flags)|file_flags::int_opening_dir; + return dounlink(true, id, std::move(op), std::move(req)); + } + public: + private: + // Called in unknown thread + completion_returntype dofile(size_t id, future<> op, path_req req) + { + NTSTATUS status=0; + HANDLE h=nullptr; + req.flags=fileflags(req.flags); + handle_ptr dirh=decode_relative_path(req); + std::tie(status, h)=ntcreatefile(dirh, req.path, req.flags); + if(dirh) + req.path=dirh->path()/req.path; + BOOST_AFIO_ERRHNTFN(status, [&req]{return req.path;}); + // If writing and SyncOnClose and NOT synchronous, turn on SyncOnClose + auto ret=std::make_shared<async_io_handle_windows>(this, req.path, req.flags, + (file_flags::sync_on_close|file_flags::write)==(req.flags & (file_flags::sync_on_close|file_flags::write|file_flags::always_sync)), + h); + static_cast<async_io_handle_windows *>(ret.get())->do_add_io_handle_to_parent(); + // If creating a file or directory or link and he wants compression, try that now + if(!!(req.flags & file_flags::create_compressed) && (!!(req.flags & file_flags::create_only_if_not_exist) || !!(req.flags & file_flags::create))) + { + DWORD bytesout=0; + USHORT val=COMPRESSION_FORMAT_DEFAULT; + BOOST_AFIO_ERRHWINFN(DeviceIoControl(ret->native_handle(), FSCTL_SET_COMPRESSION, &val, sizeof(val), nullptr, 0, &bytesout, nullptr), [&req]{return req.path;}); + } + if(!(req.flags & file_flags::int_opening_dir) && !(req.flags & file_flags::int_opening_link)) + { + // If opening existing file for write, try to convert to sparse, ignoring any failures + if(!(req.flags & file_flags::no_sparse) && !!(req.flags & file_flags::write)) + { +#if defined(__MINGW32__) && !defined(__MINGW64__) && !defined(__MINGW64_VERSION_MAJOR) + // Mingw32 currently lacks the FILE_SET_SPARSE_BUFFER structure + typedef struct _FILE_SET_SPARSE_BUFFER { + BOOLEAN SetSparse; + } FILE_SET_SPARSE_BUFFER, *PFILE_SET_SPARSE_BUFFER; +#endif + DWORD bytesout=0; + FILE_SET_SPARSE_BUFFER fssb={true}; + DeviceIoControl(ret->native_handle(), FSCTL_SET_SPARSE, &fssb, sizeof(fssb), nullptr, 0, &bytesout, nullptr); + } + } + return std::make_pair(true, ret); + } + // Called in unknown thread + completion_returntype dormfile(size_t id, future<> op, path_req req) + { + return dounlink(true, id, std::move(op), std::move(req)); + } + // Called in unknown thread + void boost_asio_symlink_completion_handler(size_t id, handle_ptr h, std::shared_ptr<std::unique_ptr<path::value_type[]>> buffer, const error_code &ec, size_t bytes_transferred) + { + if(ec) + { + exception_ptr e; + // boost::system::system_error makes no attempt to ask windows for what the error code means :( + try + { + BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();}); + } + catch(...) + { + e=current_exception(); + } + complete_async_op(id, h, e); + } + else + { + complete_async_op(id, h); + } + } + // Called in unknown thread + completion_returntype dosymlink(size_t id, future<> op, path_req req) + { + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + req.flags=fileflags(req.flags); + req.flags=req.flags|file_flags::int_opening_link; + // For safety, best not create unless doesn't exist + if(!!(req.flags&file_flags::create)) + { + req.flags=req.flags&~file_flags::create; + req.flags=req.flags|file_flags::create_only_if_not_exist; + } + // If not creating, simply open + if(!(req.flags&file_flags::create_only_if_not_exist)) + return dofile(id, op, req); + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + using windows_nt_kernel::REPARSE_DATA_BUFFER; + // Our new object needs write access and if we're linking a directory, create a directory + req.flags=req.flags|file_flags::write; + if(!!(h->flags()&file_flags::int_opening_dir)) + req.flags=req.flags|file_flags::int_opening_dir; + completion_returntype ret=dofile(id, op, req); + assert(ret.first); + path destpath(h->path(true)); + size_t destpathbytes=destpath.native().size()*sizeof(path::value_type); + size_t buffersize=sizeof(REPARSE_DATA_BUFFER)+destpathbytes*2+256; + auto buffer=std::make_shared<std::unique_ptr<path::value_type[]>>(new path::value_type[buffersize]); + REPARSE_DATA_BUFFER *rpd=(REPARSE_DATA_BUFFER *) buffer->get(); + memset(rpd, 0, sizeof(*rpd)); + // Create a directory junction for directories and symbolic links for files + if(!!(h->flags()&file_flags::int_opening_dir)) + rpd->ReparseTag=IO_REPARSE_TAG_MOUNT_POINT; + else + rpd->ReparseTag=IO_REPARSE_TAG_SYMLINK; + if(destpath.native().size()>5 && isalpha(destpath.native()[4]) && destpath.native()[5]==':') + { + memcpy(rpd->MountPointReparseBuffer.PathBuffer, destpath.c_str(), destpathbytes+sizeof(path::value_type)); + rpd->MountPointReparseBuffer.SubstituteNameOffset=0; + rpd->MountPointReparseBuffer.SubstituteNameLength=(USHORT)destpathbytes; + rpd->MountPointReparseBuffer.PrintNameOffset=(USHORT)(destpathbytes+sizeof(path::value_type)); + rpd->MountPointReparseBuffer.PrintNameLength=(USHORT)(destpathbytes-4); + memcpy(rpd->MountPointReparseBuffer.PathBuffer+rpd->MountPointReparseBuffer.PrintNameOffset/sizeof(path::value_type), destpath.c_str()+4, rpd->MountPointReparseBuffer.PrintNameLength+sizeof(path::value_type)); + } + else + { + memcpy(rpd->MountPointReparseBuffer.PathBuffer, destpath.c_str(), destpathbytes+sizeof(path::value_type)); + rpd->MountPointReparseBuffer.SubstituteNameOffset=0; + rpd->MountPointReparseBuffer.SubstituteNameLength=(USHORT)destpathbytes; + rpd->MountPointReparseBuffer.PrintNameOffset=(USHORT)(destpathbytes+sizeof(path::value_type)); + rpd->MountPointReparseBuffer.PrintNameLength=(USHORT)destpathbytes; + memcpy(rpd->MountPointReparseBuffer.PathBuffer+rpd->MountPointReparseBuffer.PrintNameOffset/sizeof(path::value_type), h->path().c_str(), rpd->MountPointReparseBuffer.PrintNameLength+sizeof(path::value_type)); + } + size_t headerlen=offsetof(REPARSE_DATA_BUFFER, MountPointReparseBuffer); + size_t reparsebufferheaderlen=offsetof(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer)-headerlen; + rpd->ReparseDataLength=(USHORT)(rpd->MountPointReparseBuffer.SubstituteNameLength+rpd->MountPointReparseBuffer.PrintNameLength+2*sizeof(path::value_type)+reparsebufferheaderlen); + auto dirop(ret.second); + asio::windows::overlapped_ptr ol(threadsource()->io_service(), [this, id, + BOOST_AFIO_LAMBDA_MOVE_CAPTURE(dirop), + BOOST_AFIO_LAMBDA_MOVE_CAPTURE(buffer)](const error_code &ec, size_t bytes){ boost_asio_symlink_completion_handler(id, std::move(dirop), std::move(buffer), ec, bytes);}); + DWORD bytesout=0; + BOOL ok=DeviceIoControl(ret.second->native_handle(), FSCTL_SET_REPARSE_POINT, rpd, (DWORD)(rpd->ReparseDataLength+headerlen), NULL, 0, &bytesout, ol.get()); + DWORD errcode=GetLastError(); + if(!ok && ERROR_IO_PENDING!=errcode) + { + //std::cerr << "ERROR " << errcode << std::endl; + error_code ec(errcode, asio::error::get_system_category()); + ol.complete(ec, ol.get()->InternalHigh); + } + else + ol.release(); + // Indicate we're not finished yet + return std::make_pair(false, h); + } + // Called in unknown thread + completion_returntype dormsymlink(size_t id, future<> op, path_req req) + { + req.flags=fileflags(req.flags)|file_flags::int_opening_link; + return dounlink(true, id, std::move(op), std::move(req)); + } + // Called in unknown thread + completion_returntype dozero(size_t id, future<> op, std::vector<std::pair<off_t, off_t>> ranges) + { +#if defined(__MINGW32__) && !defined(__MINGW64__) && !defined(__MINGW64_VERSION_MAJOR) + // Mingw32 currently lacks the FILE_ZERO_DATA_INFORMATION structure and FSCTL_SET_ZERO_DATA + typedef struct _FILE_ZERO_DATA_INFORMATION { + LARGE_INTEGER FileOffset; + LARGE_INTEGER BeyondFinalZero; + } FILE_ZERO_DATA_INFORMATION, *PFILE_ZERO_DATA_INFORMATION; +#define FSCTL_SET_ZERO_DATA CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 50, METHOD_BUFFERED, FILE_WRITE_DATA) +#endif + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + assert(p); + auto buffers=std::make_shared<std::vector<FILE_ZERO_DATA_INFORMATION>>(); + buffers->reserve(ranges.size()); + off_t bytes=0; + for(auto &i : ranges) + { + FILE_ZERO_DATA_INFORMATION fzdi; + fzdi.FileOffset.QuadPart=i.first; + fzdi.BeyondFinalZero.QuadPart=i.first+i.second; + buffers->push_back(std::move(fzdi)); + bytes+=i.second; + } + auto bytes_to_transfer=std::make_shared<std::pair<atomic<bool>, atomic<off_t>>>(); + bytes_to_transfer->first=false; + bytes_to_transfer->second=bytes; + auto completion_handler=[this, id, h, bytes_to_transfer, buffers](const error_code &ec, size_t bytes, off_t thisbytes) + { + if(ec) + { + exception_ptr e; + // boost::system::system_error makes no attempt to ask windows for what the error code means :( + try + { + BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();}); + } + catch(...) + { + e=current_exception(); + bool exp=false; + // If someone else has already returned an error, simply exit + if(!bytes_to_transfer->first.compare_exchange_strong(exp, true)) + return; + } + complete_async_op(id, h, e); + } + else if(!(bytes_to_transfer->second-=thisbytes)) + { + complete_async_op(id, h); + } + }; + for(auto &i : *buffers) + { + asio::windows::overlapped_ptr ol(threadsource()->io_service(), std::bind(completion_handler, std::placeholders::_1, std::placeholders::_2, i.BeyondFinalZero.QuadPart-i.FileOffset.QuadPart)); + DWORD bytesout=0; + BOOL ok=DeviceIoControl(p->native_handle(), FSCTL_SET_ZERO_DATA, &i, sizeof(i), nullptr, 0, &bytesout, ol.get()); + DWORD errcode=GetLastError(); + if(!ok && ERROR_IO_PENDING!=errcode) + { + //std::cerr << "ERROR " << errcode << std::endl; + error_code ec(errcode, asio::error::get_system_category()); + ol.complete(ec, ol.get()->InternalHigh); + } + else + ol.release(); + } + // Indicate we're not finished yet + return std::make_pair(false, h); + } + // Called in unknown thread + completion_returntype dosync(size_t id, future<> op, future<>) + { + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + assert(p); + off_t bytestobesynced=p->write_count_since_fsync(); + if(bytestobesynced) + BOOST_AFIO_ERRHWINFN(FlushFileBuffers(p->native_handle()), [p]{return p->path();}); + p->byteswrittenatlastfsync+=bytestobesynced; + return std::make_pair(true, h); + } + // Called in unknown thread + completion_returntype doclose(size_t id, future<> op, future<>) + { + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + assert(p); + if(!!(p->flags() & file_flags::int_opening_dir) && !(p->flags() & file_flags::unique_directory_handle) && !!(p->flags() & file_flags::read) && !(p->flags() & file_flags::write)) + { + // As this is a directory which may be a fast directory enumerator, ignore close + } + else + { + p->close(); + } + return std::make_pair(true, h); + } + // Called in unknown thread + void boost_asio_readwrite_completion_handler(bool is_write, size_t id, handle_ptr h, std::shared_ptr<std::tuple<atomic<bool>, atomic<size_t>, detail::io_req_impl<true>>> bytes_to_transfer, std::tuple<off_t, size_t, size_t, size_t> pars, const error_code &ec, size_t bytes_transferred) + { + if(!this->p->filters_buffers.empty()) + { + for(auto &i: this->p->filters_buffers) + { + if(i.first==OpType::Unknown || (!is_write && i.first==OpType::read) || (is_write && i.first==OpType::write)) + { + i.second(is_write ? OpType::write : OpType::read, h.get(), std::get<2>(*bytes_to_transfer), std::get<0>(pars), std::get<1>(pars), std::get<2>(pars), ec, bytes_transferred); + } + } + } + if(ec) + { + exception_ptr e; + // boost::system::system_error makes no attempt to ask windows for what the error code means :( + try + { + BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();}); + } + catch(...) + { + e=current_exception(); + bool exp=false; + // If someone else has already returned an error, simply exit + if(!std::get<0>(*bytes_to_transfer).compare_exchange_strong(exp, true)) + return; + } + complete_async_op(id, h, e); + } + else + { + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + if(is_write) + p->byteswritten+=bytes_transferred; + else + p->bytesread+=bytes_transferred; + size_t togo=(std::get<1>(*bytes_to_transfer)-=std::get<3>(pars)); + if(!togo) // bytes_this_chunk may not equal bytes_transferred if final 4Kb chunk of direct file + complete_async_op(id, h); + if(togo>((size_t)1<<(8*sizeof(size_t)-1))) + BOOST_AFIO_THROW_FATAL(std::runtime_error("IOCP returned more bytes than we asked for. This is probably memory corruption.")); + BOOST_AFIO_DEBUG_PRINT("H %u e=%u togo=%u bt=%u bc=%u\n", (unsigned) id, (unsigned) ec.value(), (unsigned) togo, (unsigned) bytes_transferred, (unsigned) std::get<3>(pars)); + } + //std::cout << "id=" << id << " total=" << bytes_to_transfer->second << " this=" << bytes_transferred << std::endl; + } + template<bool iswrite> void doreadwrite(size_t id, handle_ptr h, detail::io_req_impl<iswrite> req, async_io_handle_windows *p) + { + // asio::async_read_at() seems to have a bug and only transfers 64Kb per buffer + // asio::windows::random_access_handle::async_read_some_at() clearly bothers + // with the first buffer only. Same goes for both async write functions. + // + // So we implement by hand and skip ASIO altogether. + size_t amount=0; + for(auto &b: req.buffers) + { + amount+=asio::buffer_size(b); + } + auto bytes_to_transfer=std::make_shared<std::tuple<atomic<bool>, atomic<size_t>, detail::io_req_impl<true>>>(); + //mingw choked on atomic<T>::operator=, thought amount was atomic&, so changed to store to avoid issue + std::get<1>(*bytes_to_transfer).store(amount); + std::get<2>(*bytes_to_transfer)=req; + // Are we using direct i/o, because then we get the magic scatter/gather special functions? + if(!!(p->flags() & file_flags::os_direct)) + { + // Yay we can use the direct i/o scatter gather functions which are far more efficient + size_t pages=amount/pagesize, thisbufferoffset; + std::vector<FILE_SEGMENT_ELEMENT> elems(1+pages); + auto bufferit=req.buffers.begin(); + thisbufferoffset=0; + for(size_t n=0; n<pages; n++) + { + // Be careful of 32 bit void * sign extension here ... + elems[n].Alignment=((size_t) asio::buffer_cast<const void *>(*bufferit))+thisbufferoffset; + thisbufferoffset+=pagesize; + if(thisbufferoffset>=asio::buffer_size(*bufferit)) + { + ++bufferit; + thisbufferoffset=0; + } + } + elems[pages].Alignment=0; + auto t(std::make_tuple(req.where, 0, req.buffers.size(), amount)); + asio::windows::overlapped_ptr ol(threadsource()->io_service(), [this, id, h, bytes_to_transfer, + BOOST_AFIO_LAMBDA_MOVE_CAPTURE(t)](const error_code &ec, size_t bytes) { + boost_asio_readwrite_completion_handler(iswrite, id, std::move(h), + bytes_to_transfer, std::move(t), ec, bytes); }); + ol.get()->Offset=(DWORD) (req.where & 0xffffffff); + ol.get()->OffsetHigh=(DWORD) ((req.where>>32) & 0xffffffff); + BOOL ok=iswrite ? WriteFileGather + (p->native_handle(), &elems.front(), (DWORD) amount, NULL, ol.get()) + : ReadFileScatter + (p->native_handle(), &elems.front(), (DWORD) amount, NULL, ol.get()); + DWORD errcode=GetLastError(); + if(!ok && ERROR_IO_PENDING!=errcode) + { + //std::cerr << "ERROR " << errcode << std::endl; + error_code ec(errcode, asio::error::get_system_category()); + ol.complete(ec, ol.get()->InternalHigh); + } + else + ol.release(); + } + else + { + size_t offset=0, n=0; + for(auto &b: req.buffers) + { + auto t(std::make_tuple(req.where+offset, n, 1, asio::buffer_size(b))); + asio::windows::overlapped_ptr ol(threadsource()->io_service(), [this, id, h, bytes_to_transfer, + BOOST_AFIO_LAMBDA_MOVE_CAPTURE(t)](const error_code &ec, size_t bytes) { + boost_asio_readwrite_completion_handler(iswrite, id, std::move(h), + bytes_to_transfer, std::move(t), ec, bytes); }); + ol.get()->Offset=(DWORD) ((req.where+offset) & 0xffffffff); + ol.get()->OffsetHigh=(DWORD) (((req.where+offset)>>32) & 0xffffffff); + DWORD bytesmoved=0; + BOOL ok=iswrite ? WriteFile + (p->native_handle(), asio::buffer_cast<const void *>(b), (DWORD) asio::buffer_size(b), &bytesmoved, ol.get()) + : ReadFile + (p->native_handle(), (LPVOID) asio::buffer_cast<const void *>(b), (DWORD) asio::buffer_size(b), &bytesmoved, ol.get()); + DWORD errcode=GetLastError(); + if(!ok && ERROR_IO_PENDING!=errcode) + { + //std::cerr << "ERROR " << errcode << std::endl; + error_code ec(errcode, asio::error::get_system_category()); + ol.complete(ec, ol.get()->InternalHigh); + } + else + ol.release(); + offset+=asio::buffer_size(b); + n++; + } + } + } + // Called in unknown thread + completion_returntype doread(size_t id, future<> op, detail::io_req_impl<false> req) + { + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + assert(p); + BOOST_AFIO_DEBUG_PRINT("R %u %p (%c) @ %u, b=%u\n", (unsigned) id, h.get(), p->path().native().back(), (unsigned) req.where, (unsigned) req.buffers.size()); +#ifdef DEBUG_PRINTING + for(auto &b: req.buffers) + { BOOST_AFIO_DEBUG_PRINT(" R %u: %p %u\n", (unsigned) id, asio::buffer_cast<const void *>(b), (unsigned) asio::buffer_size(b)); } +#endif + doreadwrite(id, h, req, p); + // Indicate we're not finished yet + return std::make_pair(false, h); + } + // Called in unknown thread + completion_returntype dowrite(size_t id, future<> op, detail::io_req_impl<true> req) + { + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + assert(p); + BOOST_AFIO_DEBUG_PRINT("W %u %p (%c) @ %u, b=%u\n", (unsigned) id, h.get(), p->path().native().back(), (unsigned) req.where, (unsigned) req.buffers.size()); +#ifdef DEBUG_PRINTING + for(auto &b: req.buffers) + { BOOST_AFIO_DEBUG_PRINT(" W %u: %p %u\n", (unsigned) id, asio::buffer_cast<const void *>(b), (unsigned) asio::buffer_size(b)); } +#endif + doreadwrite(id, h, req, p); + // Indicate we're not finished yet + return std::make_pair(false, h); + } + // Called in unknown thread + completion_returntype dotruncate(size_t id, future<> op, off_t _newsize) + { + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + assert(p); + BOOST_AFIO_DEBUG_PRINT("T %u %p (%c)\n", (unsigned) id, h.get(), p->path().native().back()); +#if 1 + BOOST_AFIO_ERRHWINFN(wintruncate(p->native_handle(), _newsize), [p]{return p->path();}); +#else + // This is a bit tricky ... overlapped files ignore their file position except in this one + // case, but clearly here we have a race condition. No choice but to rinse and repeat I guess. + LARGE_INTEGER size={0}, newsize={0}; + newsize.QuadPart=_newsize; + while(size.QuadPart!=newsize.QuadPart) + { + BOOST_AFIO_ERRHWINFN(SetFilePointerEx(p->native_handle(), newsize, NULL, FILE_BEGIN), [p]{return p->path();}); + BOOST_AFIO_ERRHWINFN(SetEndOfFile(p->native_handle()), [p]{return p->path();}); + BOOST_AFIO_ERRHWINFN(GetFileSizeEx(p->native_handle(), &size), [p]{return p->path();}); + } +#endif + return std::make_pair(true, h); + } + // Called in unknown thread + struct enumerate_state : std::enable_shared_from_this<enumerate_state> + { + size_t id; + future<> op; + std::shared_ptr<promise<std::pair<std::vector<directory_entry>, bool>>> ret; + std::unique_ptr<windows_nt_kernel::FILE_ID_FULL_DIR_INFORMATION[]> buffer; + enumerate_req req; + enumerate_state(size_t _id, future<> _op, std::shared_ptr<promise<std::pair<std::vector<directory_entry>, bool>>> _ret, + enumerate_req _req) : id(_id), op(std::move(_op)), ret(std::move(_ret)), req(std::move(_req)) { reallocate_buffer(); } + void reallocate_buffer() + { + using windows_nt_kernel::FILE_ID_FULL_DIR_INFORMATION; + buffer=std::unique_ptr<FILE_ID_FULL_DIR_INFORMATION[]>(new FILE_ID_FULL_DIR_INFORMATION[req.maxitems]); + } + }; + typedef std::shared_ptr<enumerate_state> enumerate_state_t; + void boost_asio_enumerate_completion_handler(enumerate_state_t state, const error_code &ec, size_t bytes_transferred) + { + using namespace windows_nt_kernel; + size_t id=state->id; + handle_ptr h(state->op.get_handle()); + std::shared_ptr<promise<std::pair<std::vector<directory_entry>, bool>>> &ret=state->ret; + std::unique_ptr<FILE_ID_FULL_DIR_INFORMATION[]> &buffer=state->buffer; + enumerate_req &req=state->req; + if(ec && ERROR_MORE_DATA==ec.value() && bytes_transferred<(sizeof(FILE_ID_FULL_DIR_INFORMATION)+buffer.get()->FileNameLength)) + { + // Bump maxitems by one and reschedule. + req.maxitems++; + state->reallocate_buffer(); + doenumerate(state); + return; + } + if(ec && ERROR_MORE_DATA!=ec.value()) + { + if(ERROR_NO_MORE_FILES==ec.value()) + { + ret->set_value(std::make_pair(std::vector<directory_entry>(), false)); + complete_async_op(id, h); + return; + } + exception_ptr e; + // boost::system::system_error makes no attempt to ask windows for what the error code means :( + try + { + BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();}); + } + catch(...) + { + e=current_exception(); + } + ret->set_exception(e); + complete_async_op(id, h, e); + } + else + { + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + using windows_nt_kernel::to_st_type; + using windows_nt_kernel::to_timepoint; + std::vector<directory_entry> _ret; + _ret.reserve(req.maxitems); + bool thisbatchdone=(sizeof(FILE_ID_FULL_DIR_INFORMATION)*req.maxitems-bytes_transferred)>sizeof(FILE_ID_FULL_DIR_INFORMATION); + directory_entry item; + // This is what windows returns with each enumeration + item.have_metadata=directory_entry::metadata_fastpath(); + bool needmoremetadata=!!(req.metadata&~item.have_metadata); + bool done=false; + for(FILE_ID_FULL_DIR_INFORMATION *ffdi=buffer.get(); !done; ffdi=(FILE_ID_FULL_DIR_INFORMATION *)((size_t) ffdi + ffdi->NextEntryOffset)) + { + if(!ffdi->NextEntryOffset) done=true; + size_t length=ffdi->FileNameLength/sizeof(path::value_type); + if(length<=2 && '.'==ffdi->FileName[0]) + if(1==length || '.'==ffdi->FileName[1]) + continue; + path::string_type leafname(ffdi->FileName, length); + if(req.filtering==enumerate_req::filter::fastdeleted && isDeletedFile(leafname)) + continue; + item.leafname=std::move(leafname); + item.stat.st_ino=ffdi->FileId.QuadPart; + item.stat.st_type=to_st_type(ffdi->FileAttributes, ffdi->ReparsePointTag); + item.stat.st_atim=to_timepoint(ffdi->LastAccessTime); + item.stat.st_mtim=to_timepoint(ffdi->LastWriteTime); + item.stat.st_ctim=to_timepoint(ffdi->ChangeTime); + item.stat.st_size=ffdi->EndOfFile.QuadPart; + item.stat.st_allocated=ffdi->AllocationSize.QuadPart; + item.stat.st_birthtim=to_timepoint(ffdi->CreationTime); + item.stat.st_sparse=!!(ffdi->FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE); + item.stat.st_compressed=!!(ffdi->FileAttributes & FILE_ATTRIBUTE_COMPRESSED); + item.stat.st_reparse_point=!!(ffdi->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); + _ret.push_back(std::move(item)); + } + if(needmoremetadata) + { + for(auto &i: _ret) + { + try + { + i.fetch_metadata(h, req.metadata); + } + catch(...) { } // File may have vanished between enumerate and now + } + } + ret->set_value(std::make_pair(std::move(_ret), !thisbatchdone)); + complete_async_op(id, h); + } + } + // Called in unknown thread + void doenumerate(enumerate_state_t state) + { + size_t id=state->id; + handle_ptr h(state->op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + using namespace windows_nt_kernel; + std::unique_ptr<FILE_ID_FULL_DIR_INFORMATION[]> &buffer=state->buffer; + enumerate_req &req=state->req; + NTSTATUS ntstat; + UNICODE_STRING _glob={ 0 }; + if(!req.glob.empty()) + { + _glob.Buffer=const_cast<path::value_type *>(req.glob.c_str()); + _glob.MaximumLength=(_glob.Length=(USHORT) (req.glob.native().size()*sizeof(path::value_type)))+sizeof(path::value_type); + } + asio::windows::overlapped_ptr ol(threadsource()->io_service(), [this, state](const error_code &ec, size_t bytes) + { + if(!state) + abort(); + boost_asio_enumerate_completion_handler(state, ec, bytes); + }); + bool done; + do + { + // 2015-01-03 ned: I've been battling this stupid memory corruption bug for a while now, and it's really a bug in NT because they're probably not + // testing asynchronous direction enumeration so hence this evil workaround. Basically 32 bit kernels will corrupt memory if + // ApcContext is the i/o status block, and they demand ApcContext to be null or else! Unfortunately 64 bit kernels, including + // when a 32 bit process is running under WoW64, pass ApcContext not IoStatusBlock as the completion port overlapped output, + // so on 64 bit kernels we have no choice and must always set ApcContext to IoStatusBlock! + // + // So, if I'm a 32 bit build, check IsWow64Process() to see if I'm really 32 bit and don't set ApcContext, else set ApcContext. + void *ApcContext=ol.get(); +#ifndef _WIN64 + BOOL isWow64=false; + if(IsWow64Process(GetCurrentProcess(), &isWow64), !isWow64) + ApcContext=nullptr; +#endif + ntstat=NtQueryDirectoryFile(p->native_handle(), ol.get()->hEvent, NULL, ApcContext, (PIO_STATUS_BLOCK) ol.get(), + buffer.get(), (ULONG)(sizeof(FILE_ID_FULL_DIR_INFORMATION)*req.maxitems), + FileIdFullDirectoryInformation, FALSE, req.glob.empty() ? NULL : &_glob, req.restart); + if(STATUS_BUFFER_OVERFLOW==ntstat) + { + req.maxitems++; + state->reallocate_buffer(); + done=false; + } + else done=true; + } while(!done); + if(STATUS_PENDING!=ntstat) + { + //std::cerr << "ERROR " << errcode << std::endl; + SetWin32LastErrorFromNtStatus(ntstat); + error_code ec(GetLastError(), asio::error::get_system_category()); + ol.complete(ec, ol.get()->InternalHigh); + } + else + ol.release(); + } + // Called in unknown thread + completion_returntype doenumerate(size_t id, future<> op, enumerate_req req, std::shared_ptr<promise<std::pair<std::vector<directory_entry>, bool>>> ret) + { + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + + // A bit messy this, but necessary + enumerate_state_t state=std::make_shared<enumerate_state>( + id, + std::move(op), + std::move(ret), + std::move(req) + ); + doenumerate(std::move(state)); + + // Indicate we're not finished yet + return std::make_pair(false, handle_ptr()); + } + // Called in unknown thread + completion_returntype doextents(size_t id, future<> op, std::shared_ptr<promise<std::vector<std::pair<off_t, off_t>>>> ret, size_t entries) + { +#if defined(__MINGW32__) && !defined(__MINGW64__) && !defined(__MINGW64_VERSION_MAJOR) + // Mingw32 currently lacks the FILE_ALLOCATED_RANGE_BUFFER structure and FSCTL_QUERY_ALLOCATED_RANGES + typedef struct _FILE_ALLOCATED_RANGE_BUFFER { + LARGE_INTEGER FileOffset; + LARGE_INTEGER Length; + } FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER; +#define FSCTL_QUERY_ALLOCATED_RANGES CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51, METHOD_NEITHER, FILE_READ_DATA) +#endif + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + assert(p); + auto buffers=std::make_shared<std::vector<FILE_ALLOCATED_RANGE_BUFFER>>(entries); + auto completion_handler=[this, id, op, ret, entries, buffers](const error_code &ec, size_t bytes) + { + handle_ptr h(op.get_handle()); + if(ec) + { + if(ERROR_MORE_DATA==ec.value()) + { + doextents(id, std::move(op), std::move(ret), entries*2); + return; + } + exception_ptr e; + // boost::system::system_error makes no attempt to ask windows for what the error code means :( + try + { + BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();}); + } + catch(...) + { + e=current_exception(); + } + ret->set_exception(e); + complete_async_op(id, h, e); + } + else + { + std::vector<std::pair<off_t, off_t>> out(bytes/sizeof(FILE_ALLOCATED_RANGE_BUFFER)); + for(size_t n=0; n<out.size(); n++) + { + out[n].first=buffers->data()[n].FileOffset.QuadPart; + out[n].second=buffers->data()[n].Length.QuadPart; + } + ret->set_value(std::move(out)); + complete_async_op(id, h); + } + }; + asio::windows::overlapped_ptr ol(threadsource()->io_service(), completion_handler); + do + { + DWORD bytesout=0; + // Search entire file + buffers->front().FileOffset.QuadPart=0; + buffers->front().Length.QuadPart=((off_t)1<<63)-1; // Microsoft claims this is 1<<64-1024 for NTFS, but I get bad parameter error with anything higher than 1<<63-1. + BOOL ok=DeviceIoControl(p->native_handle(), FSCTL_QUERY_ALLOCATED_RANGES, buffers->data(), sizeof(FILE_ALLOCATED_RANGE_BUFFER), buffers->data(), (DWORD)(buffers->size()*sizeof(FILE_ALLOCATED_RANGE_BUFFER)), &bytesout, ol.get()); + DWORD errcode=GetLastError(); + if(!ok && ERROR_IO_PENDING!=errcode) + { + if(ERROR_INSUFFICIENT_BUFFER==errcode || ERROR_MORE_DATA==errcode) + { + buffers->resize(buffers->size()*2); + continue; + } + //std::cerr << "ERROR " << errcode << std::endl; + error_code ec(errcode, asio::error::get_system_category()); + ol.complete(ec, ol.get()->InternalHigh); + } + else + ol.release(); + break; + } while(true); + + // Indicate we're not finished yet + return std::make_pair(false, h); + } + // Called in unknown thread + completion_returntype doextents(size_t id, future<> op, std::shared_ptr<promise<std::vector<std::pair<off_t, off_t>>>> ret) + { + return doextents(id, op, std::move(ret), 16); + } + // Called in unknown thread + completion_returntype dostatfs(size_t id, future<> op, fs_metadata_flags req, std::shared_ptr<promise<statfs_t>> out) + { + try + { + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + assert(p); + statfs_t ret; + + // Probably not worth doing these asynchronously, so execute synchronously + BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769]; + IO_STATUS_BLOCK isb={ 0 }; + NTSTATUS ntstat; + if(!!(req&fs_metadata_flags::flags) || !!(req&fs_metadata_flags::namemax) || !!(req&fs_metadata_flags::fstypename)) + { + FILE_FS_ATTRIBUTE_INFORMATION *ffai=(FILE_FS_ATTRIBUTE_INFORMATION *) buffer; + ntstat=NtQueryVolumeInformationFile(h->native_handle(), &isb, ffai, sizeof(buffer), FileFsAttributeInformation); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h->native_handle(), FALSE, NULL); + BOOST_AFIO_ERRHNTFN(ntstat, [h]{return h->path();}); + if(!!(req&fs_metadata_flags::flags)) + { + ret.f_flags.rdonly=!!(ffai->FileSystemAttributes & FILE_READ_ONLY_VOLUME); + ret.f_flags.acls=!!(ffai->FileSystemAttributes & FILE_PERSISTENT_ACLS); + ret.f_flags.xattr=!!(ffai->FileSystemAttributes & FILE_NAMED_STREAMS); + ret.f_flags.compression=!!(ffai->FileSystemAttributes & FILE_VOLUME_IS_COMPRESSED); + ret.f_flags.extents=!!(ffai->FileSystemAttributes & FILE_SUPPORTS_SPARSE_FILES); + ret.f_flags.filecompression=!!(ffai->FileSystemAttributes & FILE_FILE_COMPRESSION); + } + if(!!(req&fs_metadata_flags::namemax)) ret.f_namemax=ffai->MaximumComponentNameLength; + if(!!(req&fs_metadata_flags::fstypename)) + { + ret.f_fstypename.resize(ffai->FileSystemNameLength/sizeof(path::value_type)); + for(size_t n=0; n<ffai->FileSystemNameLength/sizeof(path::value_type); n++) + ret.f_fstypename[n]=(char) ffai->FileSystemName[n]; + } + } + if(!!(req&fs_metadata_flags::bsize) || !!(req&fs_metadata_flags::blocks) || !!(req&fs_metadata_flags::bfree) || !!(req&fs_metadata_flags::bavail)) + { + FILE_FS_FULL_SIZE_INFORMATION *fffsi=(FILE_FS_FULL_SIZE_INFORMATION *) buffer; + ntstat=NtQueryVolumeInformationFile(h->native_handle(), &isb, fffsi, sizeof(buffer), FileFsFullSizeInformation); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h->native_handle(), FALSE, NULL); + BOOST_AFIO_ERRHNTFN(ntstat, [h]{return h->path();}); + if(!!(req&fs_metadata_flags::bsize)) ret.f_bsize=fffsi->BytesPerSector*fffsi->SectorsPerAllocationUnit; + if(!!(req&fs_metadata_flags::blocks)) ret.f_blocks=fffsi->TotalAllocationUnits.QuadPart; + if(!!(req&fs_metadata_flags::bfree)) ret.f_bfree=fffsi->ActualAvailableAllocationUnits.QuadPart; + if(!!(req&fs_metadata_flags::bavail)) ret.f_bavail=fffsi->CallerAvailableAllocationUnits.QuadPart; + } + if(!!(req&fs_metadata_flags::fsid)) + { + FILE_FS_OBJECTID_INFORMATION *ffoi=(FILE_FS_OBJECTID_INFORMATION *) buffer; + ntstat=NtQueryVolumeInformationFile(h->native_handle(), &isb, ffoi, sizeof(buffer), FileFsObjectIdInformation); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h->native_handle(), FALSE, NULL); + if(0/*STATUS_SUCCESS*/==ntstat) + { + // FAT32 doesn't support filing system id, so sink error + memcpy(&ret.f_fsid, ffoi->ObjectId, sizeof(ret.f_fsid)); + } + } + if(!!(req&fs_metadata_flags::iosize)) + { + FILE_FS_SECTOR_SIZE_INFORMATION *ffssi=(FILE_FS_SECTOR_SIZE_INFORMATION *) buffer; + ntstat=NtQueryVolumeInformationFile(h->native_handle(), &isb, ffssi, sizeof(buffer), FileFsSectorSizeInformation); + if(STATUS_PENDING==ntstat) + ntstat=NtWaitForSingleObject(h->native_handle(), FALSE, NULL); + if(0/*STATUS_SUCCESS*/!=ntstat) + { + // Windows XP and Vista don't support the FILE_FS_SECTOR_SIZE_INFORMATION + // API, so we'll just hardcode 512 bytes + ffssi->PhysicalBytesPerSectorForPerformance=512; + } + ret.f_iosize=ffssi->PhysicalBytesPerSectorForPerformance; + } + if(!!(req&fs_metadata_flags::mntfromname) || !!(req&fs_metadata_flags::mntonname)) + { + // Irrespective we need the mount path before figuring out the mounted device + ret.f_mntonname=MountPointFromHandle(h->native_handle()); + if(!!(req&fs_metadata_flags::mntfromname)) + { + path::string_type volumename=VolumeNameFromMountPoint(ret.f_mntonname); + ret.f_mntfromname.reserve(volumename.size()); + for(size_t n=0; n<volumename.size(); n++) + ret.f_mntfromname.push_back((char) volumename[n]); + } + } + out->set_value(std::move(ret)); + return std::make_pair(true, h); + } + catch(...) + { + out->set_exception(current_exception()); + throw; + } + } + // Called in unknown thread + completion_returntype dolock(size_t id, future<> op, lock_req req) + { + handle_ptr h(op.get_handle()); + async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get()); + assert(p); + if(!p->lockfile) + BOOST_AFIO_THROW(std::invalid_argument("This file handle was not opened with OSLockable.")); + return p->lockfile->lock(id, std::move(op), std::move(req)); + } + + public: + async_file_io_dispatcher_windows(std::shared_ptr<thread_source> threadpool, file_flags flagsforce, file_flags flagsmask) : dispatcher(threadpool, flagsforce, flagsmask), pagesize(utils::page_sizes().front()) + { + } + + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> dir(const std::vector<path_req> &reqs) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: reqs) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::dir, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dodir); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmdir(const std::vector<path_req> &reqs) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: reqs) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::rmdir, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dormdir); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> file(const std::vector<path_req> &reqs) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: reqs) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::file, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dofile); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmfile(const std::vector<path_req> &reqs) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: reqs) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::rmfile, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dormfile); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> symlink(const std::vector<path_req> &reqs, const std::vector<future<>> &targets) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: reqs) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + std::vector<future<>> ops(targets); + ops.resize(reqs.size()); + for(size_t n=0; n<reqs.size(); n++) + { + if(!ops[n].valid()) + ops[n]=reqs[n].precondition; + } + return chain_async_ops((int) detail::OpType::symlink, ops, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dosymlink); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmsymlink(const std::vector<path_req> &reqs) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: reqs) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::rmsymlink, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dormsymlink); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> sync(const std::vector<future<>> &ops) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: ops) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::sync, ops, async_op_flags::none, &async_file_io_dispatcher_windows::dosync); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> zero(const std::vector<future<>> &ops, const std::vector<std::vector<std::pair<off_t, off_t>>> &ranges) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: ops) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::zero, ops, ranges, async_op_flags::none, &async_file_io_dispatcher_windows::dozero); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> close(const std::vector<future<>> &ops) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: ops) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::close, ops, async_op_flags::none, &async_file_io_dispatcher_windows::doclose); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> read(const std::vector<detail::io_req_impl<false>> &reqs) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: reqs) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::read, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::doread); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> write(const std::vector<detail::io_req_impl<true>> &reqs) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: reqs) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::write, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dowrite); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> truncate(const std::vector<future<>> &ops, const std::vector<off_t> &sizes) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: ops) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } + if(ops.size()!=sizes.size()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); +#endif + return chain_async_ops((int) detail::OpType::truncate, ops, sizes, async_op_flags::none, &async_file_io_dispatcher_windows::dotruncate); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<std::pair<std::vector<directory_entry>, bool>>> enumerate(const std::vector<enumerate_req> &reqs) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: reqs) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::enumerate, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::doenumerate); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<std::vector<std::pair<off_t, off_t>>>> extents(const std::vector<future<>> &ops) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: ops) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::extents, ops, async_op_flags::none, &async_file_io_dispatcher_windows::doextents); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<statfs_t>> statfs(const std::vector<future<>> &ops, const std::vector<fs_metadata_flags> &reqs) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: ops) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); + } + if(ops.size()!=reqs.size()) + BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid.")); +#endif + return chain_async_ops((int) detail::OpType::statfs, ops, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dostatfs); + } + BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> lock(const std::vector<lock_req> &reqs) override final + { +#if BOOST_AFIO_VALIDATE_INPUTS + for(auto &i: reqs) + { + if(!i.validate()) + BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); + } +#endif + return chain_async_ops((int) detail::OpType::lock, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dolock); + } + }; + + inline handle_ptr async_io_handle_windows::int_verifymyinode() + { + handle_ptr dirh(container()); + if(!dirh) + { + path_req req(path(true)); + dirh=parent()->int_get_handle_to_containing_dir(static_cast<async_file_io_dispatcher_windows *>(parent()), 0, req, &async_file_io_dispatcher_windows::dofile); + } + return dirh; + } + + struct win_actual_lock_file : public actual_lock_file + { + HANDLE h; + OVERLAPPED ol; + static BOOST_CONSTEXPR_OR_CONST off_t magicbyte=(1ULL<<62)-1; + static bool win32_lockmagicbyte(HANDLE h, DWORD flags) + { + OVERLAPPED ol={0}; + ol.Offset=(DWORD) (magicbyte & 0xffffffff); + ol.OffsetHigh=(DWORD) ((magicbyte>>32) & 0xffffffff); + DWORD len_low=1, len_high=0; + return LockFileEx(h, LOCKFILE_FAIL_IMMEDIATELY|flags, 0, len_low, len_high, &ol)!=0; + } + static bool win32_unlockmagicbyte(HANDLE h) + { + OVERLAPPED ol={0}; + ol.Offset=(DWORD) (magicbyte & 0xffffffff); + ol.OffsetHigh=(DWORD) ((magicbyte>>32) & 0xffffffff); + DWORD len_low=1, len_high=0; + return UnlockFileEx(h, 0, len_low, len_high, &ol)!=0; + } + win_actual_lock_file(BOOST_AFIO_V2_NAMESPACE::path p) : actual_lock_file(std::move(p)), h(nullptr) + { + memset(&ol, 0, sizeof(ol)); + bool done=false; + do + { + NTSTATUS status=0; + for(size_t n=0; n<10; n++) + { + // TODO FIXME: Lock file needs to copy exact security descriptor from its original + std::tie(status, h)=ntcreatefile(handle_ptr(), lockfilepath, file_flags::create|file_flags::read_write|file_flags::temporary_file|file_flags::int_file_share_delete); + // This may fail with STATUS_DELETE_PENDING, if so sleep and loop + if(!status) + break; + else if(((NTSTATUS) 0xC0000056)/*STATUS_DELETE_PENDING*/==status) + this_thread::sleep_for(chrono::milliseconds(100)); + else + BOOST_AFIO_ERRHNT(status); + } + BOOST_AFIO_ERRHNT(status); + // We can't use DeleteOnClose for Samba shares because he'll delete on close even if + // other processes have a handle open. We hence read lock the same final byte as POSIX considers + // it (i.e. 1<<62-1). If it fails then the other has a write lock and is about to delete. + if(!(done=win32_lockmagicbyte(h, 0))) + CloseHandle(h); + } while(!done); + } + ~win_actual_lock_file() + { + // Lock POSIX magic byte for write access. Win32 doesn't permit conversion of shared to exclusive locks + win32_unlockmagicbyte(h); + if(win32_lockmagicbyte(h, LOCKFILE_EXCLUSIVE_LOCK)) + { + // If this lock succeeded then I am potentially the last with this file open + // so unlink myself, which will work with my open file handle as I was opened with FILE_SHARE_DELETE. + // All further opens will now fail with STATUS_DELETE_PENDING + path::string_type escapedpath(lockfilepath.filesystem_path().native()); + BOOST_AFIO_ERRHWIN(DeleteFile(escapedpath.c_str())); + } + BOOST_AFIO_ERRHWIN(CloseHandle(h)); + } + dispatcher::completion_returntype lock(size_t id, future<> op, lock_req req, void *_thislockfile) override final + { + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + win_lock_file *thislockfile=(win_lock_file *) _thislockfile; + auto completion_handler=[this, id, op, req](const error_code &ec) + { + handle_ptr h(op.get_handle()); + if(ec) + { + exception_ptr e; + // boost::system::system_error makes no attempt to ask windows for what the error code means :( + try + { + BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();}); + } + catch(...) + { + e=current_exception(); + } + op.parent()->complete_async_op(id, h, e); + } + else + { + op.parent()->complete_async_op(id, h); + } + }; + // (1<<62)-1 byte used by POSIX for last use detection + static BOOST_CONSTEXPR_OR_CONST off_t magicbyte=(1ULL<<62)-1; + if(req.offset==magicbyte) + BOOST_AFIO_THROW(std::invalid_argument("offset cannot be (1<<62)-1")); + // If we straddle the magic byte then clamp to just under it + if(req.offset<magicbyte && req.offset+req.length>magicbyte) + req.length=std::min(req.offset+req.length, magicbyte)-req.offset; + + NTSTATUS ntstat=0; + LARGE_INTEGER offset; offset.QuadPart=req.offset; + LARGE_INTEGER length; length.QuadPart=req.length; + if(req.type==lock_req::Type::unlock) + { + ntstat=NtUnlockFile(h, (PIO_STATUS_BLOCK) &ol, &offset, &length, 0); + //printf("UL %u ntstat=%u\n", id, ntstat); + } + else + { + ntstat=NtLockFile(h, thislockfile->ev.native_handle(), nullptr, nullptr, (PIO_STATUS_BLOCK) &ol, &offset, &length, 0, + false, req.type==lock_req::Type::write_lock); + //printf("L %u ntstat=%u\n", id, ntstat); + } + if(STATUS_PENDING!=ntstat) + { + SetWin32LastErrorFromNtStatus(ntstat); + error_code ec(GetLastError(), asio::error::get_system_category()); + completion_handler(ec); + } + else + thislockfile->ev.async_wait(completion_handler); + // Indicate we're not finished yet + return std::make_pair(false, handle_ptr()); + } + }; +} // namespace +BOOST_AFIO_V2_NAMESPACE_END + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif |