38 cfg.desiredAccess = GENERIC_READ;
39 cfg.shareMode = FILE_SHARE_READ;
48 cfg.lockType = F_RDLCK;
63 cfg.desiredAccess = GENERIC_READ | GENERIC_WRITE;
64 cfg.shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
71 cfg.lockFlags = LOCKFILE_EXCLUSIVE_LOCK;
73 cfg.lockType = F_WRLCK;
93 Must((desiredAccess & GENERIC_WRITE) == GENERIC_WRITE);
94 creationDisposition = OPEN_ALWAYS;
98 creationMask = (S_IXUSR | S_IXGRP|S_IWGRP | S_IXOTH|S_IWOTH);
112fcntlLock(
const int fd,
const short lockType)
116 memset(&fl, 0,
sizeof(fl));
117 fl.l_type = lockType;
118 fl.l_whence = SEEK_SET;
119 return ::fcntl(fd, F_SETLK, &fl);
127 return new File(filename, cfg);
129 catch (
const std::exception &ex) {
130 debugs(54, 5,
"will not lock: " << ex.what());
138 debugs(54, 7,
"constructing, this=" <<
this <<
' ' <<
name_);
153 debugs(54, 7,
"destructing, this=" <<
this <<
' ' <<
name_);
159 *
this = std::move(other);
165 std::swap(
fd_, other.fd_);
174 fd_ = CreateFile(TEXT(
name_.
c_str()), cfg.desiredAccess, cfg.shareMode,
nullptr, cfg.creationDisposition, FILE_ATTRIBUTE_NORMAL,
nullptr);
176 const auto savedError = GetLastError();
180 mode_t oldCreationMask = 0;
186 const auto savedErrno = errno;
188 umask(oldCreationMask);
201 if (!CloseHandle(
fd_)) {
202 const auto savedError = GetLastError();
207 const auto savedErrno = errno;
218 if (!SetFilePointer(
fd_, 0,
nullptr, FILE_BEGIN)) {
219 const auto savedError = GetLastError();
223 if (!SetEndOfFile(
fd_)) {
224 const auto savedError = GetLastError();
228 if (::lseek(
fd_, SEEK_SET, 0) < 0) {
229 const auto savedErrno = errno;
233 if (::ftruncate(
fd_, 0) != 0) {
234 const auto savedErrno = errno;
244 const auto readLimit = maxBytes + 1;
248 if (!ReadFile(
fd_, rawBuf, readLimit, &result,
nullptr)) {
249 const auto savedError = GetLastError();
253 const auto result = ::read(
fd_, rawBuf, readLimit);
255 const auto savedErrno = errno;
259 const auto bytesRead =
static_cast<size_t>(result);
260 assert(bytesRead <= readLimit);
264 if (buf.
length() < minBytes) {
265 static const SBuf errPrematureEof(
"premature eof");
266 static const SBuf errEmptyFile(
"empty file");
270 if (buf.
length() > maxBytes) {
271 static const SBuf failure(
"unreasonably large file");
283 DWORD nBytesWritten = 0;
285 const auto savedError = GetLastError();
288 const auto bytesWritten =
static_cast<size_t>(nBytesWritten);
292 const auto savedErrno = errno;
295 const auto bytesWritten =
static_cast<size_t>(result);
297 if (bytesWritten != data.
length()) {
298 static const SBuf failure(
"partial write");
307 if (!FlushFileBuffers(
fd_)) {
308 const auto savedError = GetLastError();
312 if (::fsync(
fd_) != 0) {
313 const auto savedErrno = errno;
324 while (attemptsLeft) {
328 }
catch (
const std::exception &ex) {
331 debugs(54, 4,
"sleeping and then trying up to " << attemptsLeft <<
332 " more time(s) after a failure: " << ex.what());
335 std::this_thread::sleep_for(std::chrono::microseconds(cfg.
retryGapUsec));
337 debugs(54, 9,
"disabled");
345 if (!LockFileEx(
fd_, cfg.lockFlags, 0, 0, 1, 0)) {
346 const auto savedError = GetLastError();
350 if (fcntlLock(
fd_, cfg.lockType) != 0) {
351 const auto savedErrno = errno;
356 const auto savedErrno = errno;
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
void error(char *format,...)
How should a file be opened/created? Should it be locked?
const unsigned int retryGapUsec
pause before each lock retry
static FileOpeningConfig ReadWrite()
unsigned int lockAttempts
how many times to try locking
mode_t openMode
access mode; 3rd open(2) parameter
int openFlags
opening flags; 2nd open(2) parameter
static FileOpeningConfig ReadOnly()
int flockMode
2nd flock(2) parameter
mode_t creationMask
umask() parameter; the default is S_IWGRP|S_IWOTH
FileOpeningConfig & locked(unsigned int attempts=5)
protect concurrent accesses by attempting to obtain an appropriate lock
FileOpeningConfig & createdIfMissing()
when opening a file for writing, create it if it does not exist
a portable locking-aware exception-friendly file (with RAII API)
File(const SBuf &aFilename, const FileOpeningConfig &cfg)
opens
Handle fd_
OS-specific file handle.
void lock(const FileOpeningConfig &cfg)
calls lockOnce() as many times as necessary (including zero)
void writeAll(const SBuf &data)
write(2) with a "wrote everything" check
void open(const FileOpeningConfig &cfg)
opens (or creates) the file
static File * Optional(const SBuf &aName, const FileOpeningConfig &cfg)
File & operator=(const File &)=delete
void lockOnce(const FileOpeningConfig &cfg)
locks, blocking or returning immediately depending on the lock waiting mode
SBuf sysCallError(const char *callName, const int savedErrno) const
SBuf sysCallFailure(const char *callName, const SBuf &error) const
static const Handle InvalidHandle
void truncate()
makes the file size (and the current I/O offset) zero
void synchronize()
fsync(2)
SBuf readSmall(SBuf::size_type minBytes, SBuf::size_type maxBytes)
read(2) for small files
SBuf name_
location on disk
char * rawAppendStart(size_type anticipatedSize)
const char * rawContent() const
size_type length() const
Returns the number of bytes stored in SBuf.
MemBlob::size_type size_type
void rawAppendFinish(const char *start, size_type actualSize)
#define debugs(SECTION, LEVEL, CONTENT)
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
const char * xstrerr(int error)