123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302 |
- #include "file.h"
- #include "flock.h"
- #include "fstat.h"
- #include "sysstat.h"
- #include "align.h"
- #include "info.h"
- #include <array>
- #include <util/string/util.h>
- #include <util/string/cast.h>
- #include <util/string/builder.h>
- #include <util/stream/hex.h>
- #include <util/stream/format.h>
- #include <util/random/random.h>
- #include <util/generic/size_literals.h>
- #include <util/generic/string.h>
- #include <util/generic/ylimits.h>
- #include <util/generic/yexception.h>
- #include <util/datetime/base.h>
- #include <errno.h>
- #if defined(_unix_)
- #include <fcntl.h>
- #if defined(_linux_) && (!defined(_android_) || __ANDROID_API__ >= 21) && !defined(FALLOC_FL_KEEP_SIZE)
- #include <linux/falloc.h>
- #endif
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/mman.h>
- #elif defined(_win_)
- #include "winint.h"
- #include "fs_win.h"
- #include <io.h>
- #endif
- #if defined(_bionic_)
- #include <sys/sendfile.h>
- #define HAVE_POSIX_FADVISE 0
- #define HAVE_SYNC_FILE_RANGE 0
- #elif defined(_linux_)
- #include <sys/sendfile.h>
- #define HAVE_POSIX_FADVISE 1
- #define HAVE_SYNC_FILE_RANGE 1
- #elif defined(__FreeBSD__) && !defined(WITH_VALGRIND)
- #include <sys/param.h>
- #define HAVE_POSIX_FADVISE (__FreeBSD_version >= 900501)
- #define HAVE_SYNC_FILE_RANGE 0
- #else
- #define HAVE_POSIX_FADVISE 0
- #define HAVE_SYNC_FILE_RANGE 0
- #endif
- static bool IsStupidFlagCombination(EOpenMode oMode) {
- // ForAppend will actually not be applied in the following combinations:
- return (oMode & (CreateAlways | ForAppend)) == (CreateAlways | ForAppend) || (oMode & (TruncExisting | ForAppend)) == (TruncExisting | ForAppend) || (oMode & (CreateNew | ForAppend)) == (CreateNew | ForAppend);
- }
- TFileHandle::TFileHandle(const TString& fName, EOpenMode oMode) noexcept {
- ui32 fcMode = 0;
- EOpenMode createMode = oMode & MaskCreation;
- Y_VERIFY(!IsStupidFlagCombination(oMode), "oMode %d makes no sense", static_cast<int>(oMode));
- if (!(oMode & MaskRW)) {
- oMode |= RdWr;
- }
- if (!(oMode & AMask)) {
- oMode |= ARW;
- }
- #ifdef _win_
- switch (createMode) {
- case OpenExisting:
- fcMode = OPEN_EXISTING;
- break;
- case TruncExisting:
- fcMode = TRUNCATE_EXISTING;
- break;
- case OpenAlways:
- fcMode = OPEN_ALWAYS;
- break;
- case CreateNew:
- fcMode = CREATE_NEW;
- break;
- case CreateAlways:
- fcMode = CREATE_ALWAYS;
- break;
- default:
- abort();
- break;
- }
- ui32 faMode = 0;
- if (oMode & RdOnly) {
- faMode |= GENERIC_READ;
- }
- if (oMode & WrOnly) {
- // WrOnly or RdWr
- faMode |= GENERIC_WRITE;
- }
- if (oMode & ::ForAppend) {
- faMode |= GENERIC_WRITE;
- faMode |= FILE_APPEND_DATA;
- faMode &= ~FILE_WRITE_DATA;
- }
- bool inheritHandle = !(oMode & CloseOnExec);
- ui32 shMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
- ui32 attrMode = FILE_ATTRIBUTE_NORMAL;
- if ((createMode == OpenExisting || createMode == OpenAlways) && ((oMode & AMask) == (oMode & AR))) {
- attrMode |= FILE_ATTRIBUTE_READONLY;
- }
- if (oMode & Seq) {
- attrMode |= FILE_FLAG_SEQUENTIAL_SCAN;
- }
- if (oMode & Temp) {
- // we use TTempFile instead of FILE_FLAG_DELETE_ON_CLOSE
- attrMode |= FILE_ATTRIBUTE_TEMPORARY;
- }
- if (oMode & Transient) {
- attrMode |= FILE_FLAG_DELETE_ON_CLOSE;
- }
- if ((oMode & (Direct | DirectAligned)) && (oMode & WrOnly)) {
- // WrOnly or RdWr
- attrMode |= /*FILE_FLAG_NO_BUFFERING |*/ FILE_FLAG_WRITE_THROUGH;
- }
- Fd_ = NFsPrivate::CreateFileWithUtf8Name(fName, faMode, shMode, fcMode, attrMode, inheritHandle);
- if ((oMode & ::ForAppend) && (Fd_ != INVALID_FHANDLE)) {
- ::SetFilePointer(Fd_, 0, 0, FILE_END);
- }
- #elif defined(_unix_)
- switch (createMode) {
- case OpenExisting:
- fcMode = 0;
- break;
- case TruncExisting:
- fcMode = O_TRUNC;
- break;
- case OpenAlways:
- fcMode = O_CREAT;
- break;
- case CreateNew:
- fcMode = O_CREAT | O_EXCL;
- break;
- case CreateAlways:
- fcMode = O_CREAT | O_TRUNC;
- break;
- default:
- abort();
- break;
- }
- if ((oMode & RdOnly) && (oMode & WrOnly)) {
- fcMode |= O_RDWR;
- } else if (oMode & RdOnly) {
- fcMode |= O_RDONLY;
- } else if (oMode & WrOnly) {
- fcMode |= O_WRONLY;
- }
- if (oMode & ::ForAppend) {
- fcMode |= O_APPEND;
- }
- if (oMode & CloseOnExec) {
- fcMode |= O_CLOEXEC;
- }
- /* I don't now about this for unix...
- if (oMode & Temp) {
- }
- */
- #if defined(_freebsd_)
- if (oMode & (Direct | DirectAligned)) {
- fcMode |= O_DIRECT;
- }
- if (oMode & Sync) {
- fcMode |= O_SYNC;
- }
- #elif defined(_linux_)
- if (oMode & DirectAligned) {
- /*
- * O_DIRECT in Linux requires aligning request size and buffer address
- * to size of hardware sector (see hw_sector_size or ioctl BLKSSZGET).
- * Usually 512 bytes, but modern hardware works better with 4096 bytes.
- */
- fcMode |= O_DIRECT;
- }
- if (oMode & Sync) {
- fcMode |= O_SYNC;
- }
- #endif
- #if defined(_linux_)
- fcMode |= O_LARGEFILE;
- #endif
- ui32 permMode = 0;
- if (oMode & AXOther) {
- permMode |= S_IXOTH;
- }
- if (oMode & AWOther) {
- permMode |= S_IWOTH;
- }
- if (oMode & AROther) {
- permMode |= S_IROTH;
- }
- if (oMode & AXGroup) {
- permMode |= S_IXGRP;
- }
- if (oMode & AWGroup) {
- permMode |= S_IWGRP;
- }
- if (oMode & ARGroup) {
- permMode |= S_IRGRP;
- }
- if (oMode & AXUser) {
- permMode |= S_IXUSR;
- }
- if (oMode & AWUser) {
- permMode |= S_IWUSR;
- }
- if (oMode & ARUser) {
- permMode |= S_IRUSR;
- }
- do {
- Fd_ = ::open(fName.data(), fcMode, permMode);
- } while (Fd_ == -1 && errno == EINTR);
- #if HAVE_POSIX_FADVISE
- if (Fd_ >= 0) {
- if (oMode & NoReuse) {
- ::posix_fadvise(Fd_, 0, 0, POSIX_FADV_NOREUSE);
- }
- if (oMode & Seq) {
- ::posix_fadvise(Fd_, 0, 0, POSIX_FADV_SEQUENTIAL);
- }
- if (oMode & NoReadAhead) {
- ::posix_fadvise(Fd_, 0, 0, POSIX_FADV_RANDOM);
- }
- }
- #endif
- //temp file
- if (Fd_ >= 0 && (oMode & Transient)) {
- unlink(fName.data());
- }
- #else
- #error unsupported platform
- #endif
- }
- bool TFileHandle::Close() noexcept {
- bool isOk = true;
- #ifdef _win_
- if (Fd_ != INVALID_FHANDLE) {
- isOk = (::CloseHandle(Fd_) != 0);
- }
- if (!isOk) {
- Y_VERIFY(GetLastError() != ERROR_INVALID_HANDLE,
- "must not quietly close invalid handle");
- }
- #elif defined(_unix_)
- if (Fd_ != INVALID_FHANDLE) {
- isOk = (::close(Fd_) == 0 || errno == EINTR);
- }
- if (!isOk) {
- // Do not quietly close bad descriptor,
- // because often it means double close
- // that is disasterous
- Y_VERIFY(errno != EBADF, "must not quietly close bad descriptor: fd=%d", int(Fd_));
- }
- #else
- #error unsupported platform
- #endif
- Fd_ = INVALID_FHANDLE;
- return isOk;
- }
- static inline i64 DoSeek(FHANDLE h, i64 offset, SeekDir origin) noexcept {
- if (h == INVALID_FHANDLE) {
- return -1L;
- }
- #if defined(_win_)
- static ui32 dir[] = {FILE_BEGIN, FILE_CURRENT, FILE_END};
- LARGE_INTEGER pos;
- pos.QuadPart = offset;
- pos.LowPart = ::SetFilePointer(h, pos.LowPart, &pos.HighPart, dir[origin]);
- if (pos.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
- pos.QuadPart = -1;
- }
- return pos.QuadPart;
- #elif defined(_unix_)
- static int dir[] = {SEEK_SET, SEEK_CUR, SEEK_END};
- #if defined(_sun_)
- return ::llseek(h, (offset_t)offset, dir[origin]);
- #else
- return ::lseek(h, (off_t)offset, dir[origin]);
- #endif
- #else
- #error unsupported platform
- #endif
- }
- i64 TFileHandle::GetPosition() const noexcept {
- return DoSeek(Fd_, 0, sCur);
- }
- i64 TFileHandle::Seek(i64 offset, SeekDir origin) noexcept {
- return DoSeek(Fd_, offset, origin);
- }
- i64 TFileHandle::GetLength() const noexcept {
- // XXX: returns error code, but does not set errno
- if (!IsOpen()) {
- return -1L;
- }
- return GetFileLength(Fd_);
- }
- bool TFileHandle::Resize(i64 length) noexcept {
- if (!IsOpen()) {
- return false;
- }
- i64 currentLength = GetLength();
- if (length == currentLength) {
- return true;
- }
- #if defined(_win_)
- i64 currentPosition = GetPosition();
- if (currentPosition == -1L) {
- return false;
- }
- Seek(length, sSet);
- if (!::SetEndOfFile(Fd_)) {
- return false;
- }
- if (currentPosition < length) {
- Seek(currentPosition, sSet);
- }
- return true;
- #elif defined(_unix_)
- return (0 == ftruncate(Fd_, (off_t)length));
- #else
- #error unsupported platform
- #endif
- }
- bool TFileHandle::Reserve(i64 length) noexcept {
- // FIXME this should reserve disk space with fallocate
- if (!IsOpen()) {
- return false;
- }
- i64 currentLength = GetLength();
- if (length <= currentLength) {
- return true;
- }
- if (!Resize(length)) {
- return false;
- }
- #if defined(_win_)
- if (!::SetFileValidData(Fd_, length)) {
- Resize(currentLength);
- return false;
- }
- #elif defined(_unix_)
- // No way to implement this under FreeBSD. Just do nothing
- #else
- #error unsupported platform
- #endif
- return true;
- }
- bool TFileHandle::FallocateNoResize(i64 length) noexcept {
- if (!IsOpen()) {
- return false;
- }
- #if defined(_linux_) && (!defined(_android_) || __ANDROID_API__ >= 21)
- return !fallocate(Fd_, FALLOC_FL_KEEP_SIZE, 0, length);
- #else
- Y_UNUSED(length);
- return true;
- #endif
- }
- // Pair for FallocateNoResize
- bool TFileHandle::ShrinkToFit() noexcept {
- if (!IsOpen()) {
- return false;
- }
- #if defined(_linux_) && (!defined(_android_) || __ANDROID_API__ >= 21)
- return !ftruncate(Fd_, (off_t)GetLength());
- #else
- return true;
- #endif
- }
- bool TFileHandle::Flush() noexcept {
- if (!IsOpen()) {
- return false;
- }
- #if defined(_win_)
- bool ok = ::FlushFileBuffers(Fd_) != 0;
- /*
- * FlushFileBuffers fails if hFile is a handle to the console output.
- * That is because the console output is not buffered.
- * The function returns FALSE, and GetLastError returns ERROR_INVALID_HANDLE.
- */
- return ok || GetLastError() == ERROR_INVALID_HANDLE;
- #elif defined(_unix_)
- int ret = ::fsync(Fd_);
- /*
- * Ignore EROFS, EINVAL - fd is bound to a special file
- * (PIPE, FIFO, or socket) which does not support synchronization.
- * Fail in case of EIO, ENOSPC, EDQUOT - data might be lost.
- */
- return ret == 0 || errno == EROFS || errno == EINVAL
- #if defined(_darwin_)
- // ENOTSUP fd does not refer to a vnode
- || errno == ENOTSUP
- #endif
- ;
- #else
- #error unsupported platform
- #endif
- }
- bool TFileHandle::FlushData() noexcept {
- #if defined(_linux_)
- if (!IsOpen()) {
- return false;
- }
- int ret = ::fdatasync(Fd_);
- // Same loginc in error handling as for fsync above.
- return ret == 0 || errno == EROFS || errno == EINVAL;
- #else
- return Flush();
- #endif
- }
- i32 TFileHandle::Read(void* buffer, ui32 byteCount) noexcept {
- // FIXME size and return must be 64-bit
- if (!IsOpen()) {
- return -1;
- }
- #if defined(_win_)
- DWORD bytesRead = 0;
- if (::ReadFile(Fd_, buffer, byteCount, &bytesRead, nullptr)) {
- return bytesRead;
- }
- return -1;
- #elif defined(_unix_)
- i32 ret;
- do {
- ret = ::read(Fd_, buffer, byteCount);
- } while (ret == -1 && errno == EINTR);
- return ret;
- #else
- #error unsupported platform
- #endif
- }
- i32 TFileHandle::Write(const void* buffer, ui32 byteCount) noexcept {
- if (!IsOpen()) {
- return -1;
- }
- #if defined(_win_)
- DWORD bytesWritten = 0;
- if (::WriteFile(Fd_, buffer, byteCount, &bytesWritten, nullptr)) {
- return bytesWritten;
- }
- return -1;
- #elif defined(_unix_)
- i32 ret;
- do {
- ret = ::write(Fd_, buffer, byteCount);
- } while (ret == -1 && errno == EINTR);
- return ret;
- #else
- #error unsupported platform
- #endif
- }
- i32 TFileHandle::Pread(void* buffer, ui32 byteCount, i64 offset) const noexcept {
- #if defined(_win_)
- OVERLAPPED io;
- Zero(io);
- DWORD bytesRead = 0;
- io.Offset = (ui32)offset;
- io.OffsetHigh = (ui32)(offset >> 32);
- if (::ReadFile(Fd_, buffer, byteCount, &bytesRead, &io)) {
- return bytesRead;
- }
- if (::GetLastError() == ERROR_HANDLE_EOF) {
- return 0;
- }
- return -1;
- #elif defined(_unix_)
- i32 ret;
- do {
- ret = ::pread(Fd_, buffer, byteCount, offset);
- } while (ret == -1 && errno == EINTR);
- return ret;
- #else
- #error unsupported platform
- #endif
- }
- i32 TFileHandle::Pwrite(const void* buffer, ui32 byteCount, i64 offset) const noexcept {
- #if defined(_win_)
- OVERLAPPED io;
- Zero(io);
- DWORD bytesWritten = 0;
- io.Offset = (ui32)offset;
- io.OffsetHigh = (ui32)(offset >> 32);
- if (::WriteFile(Fd_, buffer, byteCount, &bytesWritten, &io)) {
- return bytesWritten;
- }
- return -1;
- #elif defined(_unix_)
- i32 ret;
- do {
- ret = ::pwrite(Fd_, buffer, byteCount, offset);
- } while (ret == -1 && errno == EINTR);
- return ret;
- #else
- #error unsupported platform
- #endif
- }
- FHANDLE TFileHandle::Duplicate() const noexcept {
- if (!IsOpen()) {
- return INVALID_FHANDLE;
- }
- #if defined(_win_)
- FHANDLE dupHandle;
- if (!::DuplicateHandle(GetCurrentProcess(), Fd_, GetCurrentProcess(), &dupHandle, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
- return INVALID_FHANDLE;
- }
- return dupHandle;
- #elif defined(_unix_)
- return ::dup(Fd_);
- #else
- #error unsupported platform
- #endif
- }
- int TFileHandle::Duplicate2Posix(int dstHandle) const noexcept {
- if (!IsOpen()) {
- return -1;
- }
- #if defined(_win_)
- FHANDLE dupHandle = Duplicate();
- if (dupHandle == INVALID_FHANDLE) {
- _set_errno(EMFILE);
- return -1;
- }
- int posixHandle = _open_osfhandle((intptr_t)dupHandle, 0);
- if (posixHandle == -1) {
- CloseHandle(dupHandle);
- return -1;
- }
- if (dup2(posixHandle, dstHandle) == -1) {
- dstHandle = -1;
- }
- _close(posixHandle);
- return dstHandle;
- #elif defined(_unix_)
- while (dup2(Fd_, dstHandle) == -1) {
- if (errno != EINTR) {
- return -1;
- }
- }
- return dstHandle;
- #else
- #error unsupported platform
- #endif
- }
- bool TFileHandle::LinkTo(const TFileHandle& fh) const noexcept {
- #if defined(_unix_)
- while (dup2(fh.Fd_, Fd_) == -1) {
- if (errno != EINTR) {
- return false;
- }
- }
- return true;
- #elif defined(_win_)
- TFileHandle nh(fh.Duplicate());
- if (!nh.IsOpen()) {
- return false;
- }
- //not thread-safe
- nh.Swap(*const_cast<TFileHandle*>(this));
- return true;
- #else
- #error unsupported
- #endif
- }
- int TFileHandle::Flock(int op) noexcept {
- return ::Flock(Fd_, op);
- }
- bool TFileHandle::SetDirect() {
- #ifdef _linux_
- const long flags = fcntl(Fd_, F_GETFL);
- const int r = fcntl(Fd_, F_SETFL, flags | O_DIRECT);
- return !r;
- #endif
- return false;
- }
- void TFileHandle::ResetDirect() {
- #ifdef _linux_
- long flags = fcntl(Fd_, F_GETFL);
- fcntl(Fd_, F_SETFL, flags & ~O_DIRECT);
- #endif
- }
- i64 TFileHandle::CountCache(i64 offset, i64 length) const noexcept {
- #ifdef _linux_
- const i64 pageSize = NSystemInfo::GetPageSize();
- constexpr size_t vecSize = 512; // Fetch up to 2MiB at once
- const i64 batchSize = vecSize * pageSize;
- std::array<ui8, vecSize> vec;
- void* ptr = nullptr;
- i64 res = 0;
- if (!IsOpen()) {
- return -1;
- }
- if (!length) {
- length = GetLength();
- length -= Min(length, offset);
- }
- if (!length) {
- return 0;
- }
- const i64 begin = AlignDown(offset, pageSize);
- const i64 end = AlignUp(offset + length, pageSize);
- const i64 size = end - begin;
- /*
- * Since fincode is not implemented yet use mmap and mincore.
- * This is not so effective and scalable for frequent usage.
- */
- ptr = ::mmap(
- (caddr_t) nullptr,
- size,
- PROT_READ,
- MAP_SHARED | MAP_NORESERVE,
- Fd_,
- begin);
- if (MAP_FAILED == ptr) {
- return -1;
- }
- for (i64 base = begin; base < end; base += batchSize) {
- const size_t batch = Min(vecSize, size_t((end - base) / pageSize));
- void* batchPtr = static_cast<caddr_t>(ptr) + (base - begin);
- if (::mincore(batchPtr, batch * pageSize, vec.data())) {
- res = -1;
- break;
- }
- for (size_t i = 0; i < batch; i++) {
- // count uptodate complete pages in cache
- if (vec[i] & 1) {
- res += pageSize;
- }
- }
- if (base == begin && (vec[0] & 1)) {
- // cut head of first page
- res -= offset - begin;
- }
- if ((end - base) <= batchSize && (vec[batch - 1] & 1)) {
- // cut tail of last page
- res -= size - (offset - begin) - length;
- }
- }
- ::munmap(ptr, size);
- return res;
- #else
- Y_UNUSED(offset);
- Y_UNUSED(length);
- return -1;
- #endif
- }
- void TFileHandle::PrefetchCache(i64 offset, i64 length, bool wait) const noexcept {
- #ifdef _linux_
- #if HAVE_POSIX_FADVISE
- // POSIX_FADV_WILLNEED starts reading upto read_ahead_kb in background
- ::posix_fadvise(Fd_, offset, length, POSIX_FADV_WILLNEED);
- #endif
- if (wait) {
- TFileHandle devnull("/dev/null", OpenExisting | WrOnly | CloseOnExec);
- off_t end = length ? (offset + length) : GetLength();
- off_t pos = offset;
- ssize_t ret;
- do {
- ret = ::sendfile((FHANDLE)devnull, Fd_, &pos, end - pos);
- } while (pos < end && (ret > 0 || errno == EINTR));
- }
- #else
- Y_UNUSED(offset);
- Y_UNUSED(length);
- Y_UNUSED(wait);
- #endif
- }
- void TFileHandle::EvictCache(i64 offset, i64 length) const noexcept {
- #if HAVE_POSIX_FADVISE
- /*
- * This tries to evicts only unmaped, clean, complete pages.
- */
- ::posix_fadvise(Fd_, offset, length, POSIX_FADV_DONTNEED);
- #else
- Y_UNUSED(offset);
- Y_UNUSED(length);
- #endif
- }
- bool TFileHandle::FlushCache(i64 offset, i64 length, bool wait) noexcept {
- #if HAVE_SYNC_FILE_RANGE
- int flags = SYNC_FILE_RANGE_WRITE;
- if (wait) {
- flags |= SYNC_FILE_RANGE_WAIT_AFTER;
- }
- int ret = ::sync_file_range(Fd_, offset, length, flags);
- return ret == 0 || errno == EROFS;
- #else
- Y_UNUSED(offset);
- Y_UNUSED(length);
- if (wait) {
- return FlushData();
- }
- return true;
- #endif
- }
- TString DecodeOpenMode(ui32 mode0) {
- ui32 mode = mode0;
- TStringBuilder r;
- #define F(flag) \
- if ((mode & flag) == flag) { \
- mode &= ~flag; \
- if (r) { \
- r << TStringBuf("|"); \
- } \
- r << TStringBuf(#flag); \
- }
- F(RdWr)
- F(RdOnly)
- F(WrOnly)
- F(CreateAlways)
- F(CreateNew)
- F(OpenAlways)
- F(TruncExisting)
- F(ForAppend)
- F(Transient)
- F(CloseOnExec)
- F(Temp)
- F(Sync)
- F(Direct)
- F(DirectAligned)
- F(Seq)
- F(NoReuse)
- F(NoReadAhead)
- F(AX)
- F(AR)
- F(AW)
- F(ARW)
- F(AXOther)
- F(AWOther)
- F(AROther)
- F(AXGroup)
- F(AWGroup)
- F(ARGroup)
- F(AXUser)
- F(AWUser)
- F(ARUser)
- #undef F
- if (mode != 0) {
- if (r) {
- r << TStringBuf("|");
- }
- r << Hex(mode);
- }
- if (!r) {
- return "0";
- }
- return r;
- }
- class TFile::TImpl: public TAtomicRefCount<TImpl> {
- public:
- inline TImpl(FHANDLE fd, const TString& fname = TString())
- : Handle_(fd)
- , FileName_(fname)
- {
- }
- inline TImpl(const TString& fName, EOpenMode oMode)
- : Handle_(fName, oMode)
- , FileName_(fName)
- {
- if (!Handle_.IsOpen()) {
- ythrow TFileError() << "can't open " << fName.Quote() << " with mode " << DecodeOpenMode(oMode) << " (" << Hex(oMode.ToBaseType()) << ")";
- }
- }
- inline ~TImpl() = default;
- inline void Close() {
- if (!Handle_.Close()) {
- ythrow TFileError() << "can't close " << FileName_.Quote();
- }
- }
- const TString& GetName() const noexcept {
- return FileName_;
- }
- void SetName(const TString& newName) {
- FileName_ = newName;
- }
- const TFileHandle& GetHandle() const noexcept {
- return Handle_;
- }
- i64 Seek(i64 offset, SeekDir origin) {
- i64 pos = Handle_.Seek(offset, origin);
- if (pos == -1L) {
- ythrow TFileError() << "can't seek " << offset << " bytes in " << FileName_.Quote();
- }
- return pos;
- }
- void Resize(i64 length) {
- if (!Handle_.Resize(length)) {
- ythrow TFileError() << "can't resize " << FileName_.Quote() << " to size " << length;
- }
- }
- void Reserve(i64 length) {
- if (!Handle_.Reserve(length)) {
- ythrow TFileError() << "can't reserve " << length << " for file " << FileName_.Quote();
- }
- }
- void FallocateNoResize(i64 length) {
- if (!Handle_.FallocateNoResize(length)) {
- ythrow TFileError() << "can't allocate " << length << "bytes of space for file " << FileName_.Quote();
- }
- }
- void ShrinkToFit() {
- if (!Handle_.ShrinkToFit()) {
- ythrow TFileError() << "can't shrink " << FileName_.Quote() << " to logical size";
- }
- }
- void Flush() {
- if (!Handle_.Flush()) {
- ythrow TFileError() << "can't flush " << FileName_.Quote();
- }
- }
- void FlushData() {
- if (!Handle_.FlushData()) {
- ythrow TFileError() << "can't flush data " << FileName_.Quote();
- }
- }
- TFile Duplicate() const {
- TFileHandle dupH(Handle_.Duplicate());
- if (!dupH.IsOpen()) {
- ythrow TFileError() << "can't duplicate the handle of " << FileName_.Quote();
- }
- TFile res(dupH);
- dupH.Release();
- return res;
- }
- // Maximum amount of bytes to be read via single system call.
- // Some libraries fail when it is greater than max int.
- // Syscalls can cause contention if they operate on very large data blocks.
- static constexpr size_t MaxReadPortion = 1_GB;
- i32 RawRead(void* bufferIn, size_t numBytes) {
- const size_t toRead = Min(MaxReadPortion, numBytes);
- return Handle_.Read(bufferIn, toRead);
- }
- size_t ReadOrFail(void* buf, size_t numBytes) {
- const i32 reallyRead = RawRead(buf, numBytes);
- if (reallyRead < 0) {
- ythrow TFileError() << "can not read data from " << FileName_.Quote();
- }
- return reallyRead;
- }
- size_t Read(void* bufferIn, size_t numBytes) {
- ui8* buf = (ui8*)bufferIn;
- while (numBytes) {
- const size_t reallyRead = ReadOrFail(buf, numBytes);
- if (reallyRead == 0) {
- // file exhausted
- break;
- }
- buf += reallyRead;
- numBytes -= reallyRead;
- }
- return buf - (ui8*)bufferIn;
- }
- void Load(void* buf, size_t len) {
- if (Read(buf, len) != len) {
- ythrow TFileError() << "can't read " << len << " bytes from " << FileName_.Quote();
- }
- }
- // Maximum amount of bytes to be written via single system call.
- // Some libraries fail when it is greater than max int.
- // Syscalls can cause contention if they operate on very large data blocks.
- static constexpr size_t MaxWritePortion = 1_GB;
- void Write(const void* buffer, size_t numBytes) {
- const ui8* buf = (const ui8*)buffer;
- while (numBytes) {
- const i32 toWrite = (i32)Min(MaxWritePortion, numBytes);
- const i32 reallyWritten = Handle_.Write(buf, toWrite);
- if (reallyWritten < 0) {
- ythrow TFileError() << "can't write " << toWrite << " bytes to " << FileName_.Quote();
- }
- buf += reallyWritten;
- numBytes -= reallyWritten;
- }
- }
- size_t Pread(void* bufferIn, size_t numBytes, i64 offset) const {
- ui8* buf = (ui8*)bufferIn;
- while (numBytes) {
- const i32 toRead = (i32)Min(MaxReadPortion, numBytes);
- const i32 reallyRead = RawPread(buf, toRead, offset);
- if (reallyRead < 0) {
- ythrow TFileError() << "can not read data from " << FileName_.Quote();
- }
- if (reallyRead == 0) {
- // file exausted
- break;
- }
- buf += reallyRead;
- offset += reallyRead;
- numBytes -= reallyRead;
- }
- return buf - (ui8*)bufferIn;
- }
- i32 RawPread(void* buf, ui32 len, i64 offset) const {
- return Handle_.Pread(buf, len, offset);
- }
- void Pload(void* buf, size_t len, i64 offset) const {
- if (Pread(buf, len, offset) != len) {
- ythrow TFileError() << "can't read " << len << " bytes at offset " << offset << " from " << FileName_.Quote();
- }
- }
- void Pwrite(const void* buffer, size_t numBytes, i64 offset) const {
- const ui8* buf = (const ui8*)buffer;
- while (numBytes) {
- const i32 toWrite = (i32)Min(MaxWritePortion, numBytes);
- const i32 reallyWritten = Handle_.Pwrite(buf, toWrite, offset);
- if (reallyWritten < 0) {
- ythrow TFileError() << "can't write " << toWrite << " bytes to " << FileName_.Quote();
- }
- buf += reallyWritten;
- offset += reallyWritten;
- numBytes -= reallyWritten;
- }
- }
- void Flock(int op) {
- if (0 != Handle_.Flock(op)) {
- ythrow TFileError() << "can't flock " << FileName_.Quote();
- }
- }
- void SetDirect() {
- if (!Handle_.SetDirect()) {
- ythrow TFileError() << "can't set direct mode for " << FileName_.Quote();
- }
- }
- void ResetDirect() {
- Handle_.ResetDirect();
- }
- i64 CountCache(i64 offset, i64 length) const noexcept {
- return Handle_.CountCache(offset, length);
- }
- void PrefetchCache(i64 offset, i64 length, bool wait) const noexcept {
- Handle_.PrefetchCache(offset, length, wait);
- }
- void EvictCache(i64 offset, i64 length) const noexcept {
- Handle_.EvictCache(offset, length);
- }
- void FlushCache(i64 offset, i64 length, bool wait) {
- if (!Handle_.FlushCache(offset, length, wait)) {
- ythrow TFileError() << "can't flush data " << FileName_.Quote();
- }
- }
- private:
- TFileHandle Handle_;
- TString FileName_;
- };
- TFile::TFile()
- : Impl_(new TImpl(INVALID_FHANDLE))
- {
- }
- TFile::TFile(FHANDLE fd)
- : Impl_(new TImpl(fd))
- {
- }
- TFile::TFile(FHANDLE fd, const TString& name)
- : Impl_(new TImpl(fd, name))
- {
- }
- TFile::TFile(const TString& fName, EOpenMode oMode)
- : Impl_(new TImpl(fName, oMode))
- {
- }
- TFile::~TFile() = default;
- void TFile::Close() {
- Impl_->Close();
- }
- const TString& TFile::GetName() const noexcept {
- return Impl_->GetName();
- }
- i64 TFile::GetPosition() const noexcept {
- return Impl_->GetHandle().GetPosition();
- }
- i64 TFile::GetLength() const noexcept {
- return Impl_->GetHandle().GetLength();
- }
- bool TFile::IsOpen() const noexcept {
- return Impl_->GetHandle().IsOpen();
- }
- FHANDLE TFile::GetHandle() const noexcept {
- return Impl_->GetHandle();
- }
- i64 TFile::Seek(i64 offset, SeekDir origin) {
- return Impl_->Seek(offset, origin);
- }
- void TFile::Resize(i64 length) {
- Impl_->Resize(length);
- }
- void TFile::Reserve(i64 length) {
- Impl_->Reserve(length);
- }
- void TFile::FallocateNoResize(i64 length) {
- Impl_->FallocateNoResize(length);
- }
- void TFile::ShrinkToFit() {
- Impl_->ShrinkToFit();
- }
- void TFile::Flush() {
- Impl_->Flush();
- }
- void TFile::FlushData() {
- Impl_->FlushData();
- }
- TFile TFile::Duplicate() const {
- TFile res = Impl_->Duplicate();
- res.Impl_->SetName(Impl_->GetName());
- return res;
- }
- size_t TFile::Read(void* buf, size_t len) {
- return Impl_->Read(buf, len);
- }
- i32 TFile::RawRead(void* buf, size_t len) {
- return Impl_->RawRead(buf, len);
- }
- size_t TFile::ReadOrFail(void* buf, size_t len) {
- return Impl_->ReadOrFail(buf, len);
- }
- void TFile::Load(void* buf, size_t len) {
- Impl_->Load(buf, len);
- }
- void TFile::Write(const void* buf, size_t len) {
- Impl_->Write(buf, len);
- }
- size_t TFile::Pread(void* buf, size_t len, i64 offset) const {
- return Impl_->Pread(buf, len, offset);
- }
- i32 TFile::RawPread(void* buf, ui32 len, i64 offset) const {
- return Impl_->RawPread(buf, len, offset);
- }
- void TFile::Pload(void* buf, size_t len, i64 offset) const {
- Impl_->Pload(buf, len, offset);
- }
- void TFile::Pwrite(const void* buf, size_t len, i64 offset) const {
- Impl_->Pwrite(buf, len, offset);
- }
- void TFile::Flock(int op) {
- Impl_->Flock(op);
- }
- void TFile::SetDirect() {
- Impl_->SetDirect();
- }
- void TFile::ResetDirect() {
- Impl_->ResetDirect();
- }
- i64 TFile::CountCache(i64 offset, i64 length) const noexcept {
- return Impl_->CountCache(offset, length);
- }
- void TFile::PrefetchCache(i64 offset, i64 length, bool wait) const noexcept {
- Impl_->PrefetchCache(offset, length, wait);
- }
- void TFile::EvictCache(i64 offset, i64 length) const noexcept {
- Impl_->EvictCache(offset, length);
- }
- void TFile::FlushCache(i64 offset, i64 length, bool wait) {
- Impl_->FlushCache(offset, length, wait);
- }
- void TFile::LinkTo(const TFile& f) const {
- if (!Impl_->GetHandle().LinkTo(f.Impl_->GetHandle())) {
- ythrow TFileError() << "can not link fd(" << GetName() << " -> " << f.GetName() << ")";
- }
- }
- TFile TFile::Temporary(const TString& prefix) {
- //TODO - handle impossible case of name collision
- return TFile(prefix + ToString(MicroSeconds()) + "-" + ToString(RandomNumber<ui64>()), CreateNew | RdWr | Seq | Temp | Transient);
- }
- TFile TFile::ForAppend(const TString& path) {
- return TFile(path, OpenAlways | WrOnly | Seq | ::ForAppend);
- }
- TFile Duplicate(FILE* f) {
- return Duplicate(fileno(f));
- }
- TFile Duplicate(int fd) {
- #if defined(_win_)
- /* There are two options of how to duplicate a file descriptor on Windows:
- *
- * 1:
- * - Call dup.
- * - Call _get_osfhandle on the result.
- * - Use returned handle.
- * - Call _close on file descriptor returned by dup. This will also close
- * the handle.
- *
- * 2:
- * - Call _get_osfhandle.
- * - Call DuplicateHandle on the result.
- * - Use returned handle.
- * - Call CloseHandle.
- *
- * TFileHandle calls CloseHandle when destroyed, leaving us with option #2. */
- FHANDLE handle = reinterpret_cast<FHANDLE>(::_get_osfhandle(fd));
- FHANDLE dupHandle;
- if (!::DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), &dupHandle, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
- ythrow TFileError() << "can not duplicate file descriptor " << LastSystemError() << Endl;
- }
- return TFile(dupHandle);
- #elif defined(_unix_)
- return TFile(::dup(fd));
- #else
- #error unsupported platform
- #endif
- }
- bool PosixDisableReadAhead(FHANDLE fileHandle, void* addr) noexcept {
- int ret = -1;
- #if HAVE_POSIX_FADVISE
- #if defined(_linux_)
- Y_UNUSED(fileHandle);
- ret = madvise(addr, 0, MADV_RANDOM); // according to klamm@ posix_fadvise does not work under linux, madvise does work
- #else
- Y_UNUSED(addr);
- ret = ::posix_fadvise(fileHandle, 0, 0, POSIX_FADV_RANDOM);
- #endif
- #else
- Y_UNUSED(fileHandle);
- Y_UNUSED(addr);
- #endif
- return ret == 0;
- }
|