1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-12-23 01:11:07 +01:00

libutil: Implement HANDLE-based lseek for Windows

For windows we should live fully in the HANDLE land instead
of converting back-n-forth (which sometimes is destructive).
Using native API is much better for this.
This commit is contained in:
Sergei Zimmerman 2025-12-17 23:47:53 +03:00
parent f274a7273a
commit 89dc57f6aa
No known key found for this signature in database
5 changed files with 60 additions and 22 deletions

View file

@ -388,14 +388,13 @@ TEST(openFileEnsureBeneathNoSymlinks, works)
TEST(createAnonymousTempFile, works) TEST(createAnonymousTempFile, works)
{ {
auto fd = createAnonymousTempFile(); auto fd = createAnonymousTempFile();
auto fd_ = fromDescriptorReadOnly(fd.get());
writeFull(fd.get(), "test"); writeFull(fd.get(), "test");
lseek(fd_, 0, SEEK_SET); lseek(fd.get(), 0, SEEK_SET);
FdSource source{fd.get()}; FdSource source{fd.get()};
EXPECT_EQ(source.drain(), "test"); EXPECT_EQ(source.drain(), "test");
lseek(fd_, 0, SEEK_END); lseek(fd.get(), 0, SEEK_END);
writeFull(fd.get(), "test"); writeFull(fd.get(), "test");
lseek(fd_, 0, SEEK_SET); lseek(fd.get(), 0, SEEK_SET);
EXPECT_EQ(source.drain(), "testtest"); EXPECT_EQ(source.drain(), "testtest");
} }
@ -406,9 +405,8 @@ TEST(createAnonymousTempFile, works)
TEST(FdSource, restartWorks) TEST(FdSource, restartWorks)
{ {
auto fd = createAnonymousTempFile(); auto fd = createAnonymousTempFile();
auto fd_ = fromDescriptorReadOnly(fd.get());
writeFull(fd.get(), "hello world"); writeFull(fd.get(), "hello world");
lseek(fd_, 0, SEEK_SET); lseek(fd.get(), 0, SEEK_SET);
FdSource source{fd.get()}; FdSource source{fd.get()};
EXPECT_EQ(source.drain(), "hello world"); EXPECT_EQ(source.drain(), "hello world");
source.restart(); source.restart();

View file

@ -261,4 +261,14 @@ Descriptor openFileEnsureBeneathNoSymlinks(Descriptor dirFd, const CanonPath & p
MakeError(EndOfFile, Error); MakeError(EndOfFile, Error);
#ifdef _WIN32
/**
* Windows specific replacement for POSIX `lseek` that operates on a `HANDLE` and not
* a file descriptor.
*/
off_t lseek(Descriptor fd, off_t offset, int whence);
#endif
} // namespace nix } // namespace nix

View file

@ -1,4 +1,5 @@
#include "nix/util/nar-accessor.hh" #include "nix/util/nar-accessor.hh"
#include "nix/util/file-descriptor.hh"
#include "nix/util/archive.hh" #include "nix/util/archive.hh"
#include <map> #include <map>
@ -281,7 +282,7 @@ GetNarBytes seekableGetNarBytes(const Path & path)
GetNarBytes seekableGetNarBytes(Descriptor fd) GetNarBytes seekableGetNarBytes(Descriptor fd)
{ {
return [fd](uint64_t offset, uint64_t length) { return [fd](uint64_t offset, uint64_t length) {
if (::lseek(fromDescriptorReadOnly(fd), offset, SEEK_SET) == -1) if (lseek(fd, offset, SEEK_SET) == -1)
throw SysError("seeking in file"); throw SysError("seeking in file");
std::string buf(length, 0); std::string buf(length, 0);

View file

@ -1,4 +1,5 @@
#include "nix/util/serialise.hh" #include "nix/util/serialise.hh"
#include "nix/util/file-descriptor.hh"
#include "nix/util/compression.hh" #include "nix/util/compression.hh"
#include "nix/util/signals.hh" #include "nix/util/signals.hh"
#include "nix/util/util.hh" #include "nix/util/util.hh"
@ -207,8 +208,7 @@ void FdSource::restart()
throw Error("can't seek to the start of a file"); throw Error("can't seek to the start of a file");
buffer.reset(); buffer.reset();
read = bufPosIn = bufPosOut = 0; read = bufPosIn = bufPosOut = 0;
int fd_ = fromDescriptorReadOnly(fd); if (lseek(fd, 0, SEEK_SET) == -1)
if (lseek(fd_, 0, SEEK_SET) == -1)
throw SysError("seeking to the start of a file"); throw SysError("seeking to the start of a file");
} }

View file

@ -5,13 +5,12 @@
#include "nix/util/windows-error.hh" #include "nix/util/windows-error.hh"
#include "nix/util/file-path.hh" #include "nix/util/file-path.hh"
#ifdef _WIN32 #include <fileapi.h>
# include <fileapi.h> #include <error.h>
# include <error.h> #include <namedpipeapi.h>
# include <namedpipeapi.h> #include <namedpipeapi.h>
# include <namedpipeapi.h> #define WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN #include <windows.h>
# include <windows.h>
namespace nix { namespace nix {
@ -46,16 +45,16 @@ void writeFull(HANDLE handle, std::string_view s, bool allowInterrupts)
if (allowInterrupts) if (allowInterrupts)
checkInterrupt(); checkInterrupt();
DWORD res; DWORD res;
# if _WIN32_WINNT >= 0x0600 #if _WIN32_WINNT >= 0x0600
auto path = handleToPath(handle); // debug; do it before because handleToPath changes lasterror auto path = handleToPath(handle); // debug; do it before because handleToPath changes lasterror
if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) { if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) {
throw WinError("writing to file %1%:%2%", handle, path); throw WinError("writing to file %1%:%2%", handle, path);
} }
# else #else
if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) { if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) {
throw WinError("writing to file %1%", handle); throw WinError("writing to file %1%", handle);
} }
# endif #endif
if (res > 0) if (res > 0)
s.remove_prefix(res); s.remove_prefix(res);
} }
@ -120,7 +119,7 @@ void Pipe::create()
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
# if _WIN32_WINNT >= 0x0600 #if _WIN32_WINNT >= 0x0600
std::wstring windows::handleToFileName(HANDLE handle) std::wstring windows::handleToFileName(HANDLE handle)
{ {
@ -149,7 +148,37 @@ Path windows::handleToPath(HANDLE handle)
return os_string_to_string(handleToFileName(handle)); return os_string_to_string(handleToFileName(handle));
} }
# endif #endif
off_t lseek(HANDLE h, off_t offset, int whence)
{
DWORD method;
switch (whence) {
case SEEK_SET:
method = FILE_BEGIN;
break;
case SEEK_CUR:
method = FILE_CURRENT;
break;
case SEEK_END:
method = FILE_END;
break;
default:
throw Error("lseek: invalid whence %d", whence);
}
LARGE_INTEGER li;
li.QuadPart = offset;
LARGE_INTEGER newPos;
if (!SetFilePointerEx(h, li, &newPos, method)) {
/* Convert to a POSIX error, since caller code works with this as if it were
a POSIX lseek. */
errno = std::error_code(GetLastError(), std::system_category()).default_error_condition().value();
return -1;
}
return newPos.QuadPart;
}
} // namespace nix } // namespace nix
#endif