37 cfg.desiredAccess = GENERIC_READ;
38 cfg.shareMode = FILE_SHARE_READ;
47 cfg.lockType = F_RDLCK;
62 cfg.desiredAccess = GENERIC_READ | GENERIC_WRITE;
63 cfg.shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
70 cfg.lockFlags = LOCKFILE_EXCLUSIVE_LOCK;
72 cfg.lockType = F_WRLCK;
92 Must((desiredAccess & GENERIC_WRITE) == GENERIC_WRITE);
93 creationDisposition = OPEN_ALWAYS;
97 creationMask = (S_IXUSR | S_IXGRP|S_IWGRP | S_IXOTH|S_IWOTH);
111 fcntlLock(
const int fd,
const short lockType)
115 memset(&fl, 0,
sizeof(fl));
116 fl.l_type = lockType;
117 fl.l_whence = SEEK_SET;
118 return ::fcntl(fd, F_SETLK, &fl);
120 #endif // _SQUID_SOLARIS_ 126 return new File(filename, cfg);
128 catch (
const std::exception &ex) {
129 debugs(54, 5,
"will not lock: " << ex.what());
137 debugs(54, 7,
"constructing, this=" <<
this <<
' ' <<
name_);
152 debugs(54, 7,
"destructing, this=" <<
this <<
' ' <<
name_);
158 *
this = std::move(other);
164 std::swap(
fd_, other.fd_);
173 fd_ = CreateFile(TEXT(
name_.
c_str()), cfg.desiredAccess, cfg.shareMode,
nullptr, cfg.creationDisposition, FILE_ATTRIBUTE_NORMAL,
nullptr);
175 const auto savedError = GetLastError();
179 mode_t oldCreationMask = 0;
185 const auto savedErrno = errno;
187 umask(oldCreationMask);
200 if (!CloseHandle(
fd_)) {
201 const auto savedError = GetLastError();
206 const auto savedErrno = errno;
217 if (!SetFilePointer(
fd_, 0,
nullptr, FILE_BEGIN)) {
218 const auto savedError = GetLastError();
222 if (!SetEndOfFile(
fd_)) {
223 const auto savedError = GetLastError();
227 if (::lseek(
fd_, SEEK_SET, 0) < 0) {
228 const auto savedErrno = errno;
232 if (::ftruncate(
fd_, 0) != 0) {
233 const auto savedErrno = errno;
243 const auto readLimit = maxBytes + 1;
247 if (!ReadFile(
fd_, rawBuf, readLimit, &result,
nullptr)) {
248 const auto savedError = GetLastError();
252 const auto result = ::read(
fd_, rawBuf, readLimit);
254 const auto savedErrno = errno;
258 const auto bytesRead =
static_cast<size_t>(result);
259 assert(bytesRead <= readLimit);
263 if (buf.
length() < minBytes) {
264 const auto failure = buf.
length() ?
"premature eof" :
"empty file";
268 if (buf.
length() > maxBytes) {
269 const auto failure =
"unreasonably large file";
281 DWORD nBytesWritten = 0;
283 const auto savedError = GetLastError();
286 const auto bytesWritten =
static_cast<size_t>(nBytesWritten);
290 const auto savedErrno = errno;
293 const auto bytesWritten =
static_cast<size_t>(result);
295 if (bytesWritten != data.
length())
303 if (!FlushFileBuffers(
fd_)) {
304 const auto savedError = GetLastError();
308 if (::fsync(
fd_) != 0) {
309 const auto savedErrno = errno;
320 while (attemptsLeft) {
324 }
catch (
const std::exception &ex) {
327 debugs(54, 4,
"sleeping and then trying up to " << attemptsLeft <<
328 " more time(s) after a failure: " << ex.what());
333 debugs(54, 9,
"disabled");
341 if (!LockFileEx(
fd_, cfg.lockFlags, 0, 0, 1, 0)) {
342 const auto savedError = GetLastError();
345 #elif _SQUID_SOLARIS_ 346 if (fcntlLock(
fd_, cfg.lockType) != 0) {
347 const auto savedErrno = errno;
352 const auto savedErrno = errno;
363 return ToSBuf(
"failed to ", callName,
' ',
name_,
": ", error);
size_type length() const
Returns the number of bytes stored in SBuf.
static const Handle InvalidHandle
void truncate()
makes the file size (and the current I/O offset) zero
static File * Optional(const SBuf &aName, const FileOpeningConfig &cfg)
void error(char *format,...)
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
How should a file be opened/created? Should it be locked?
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
File & operator=(const File &)=delete
void rawAppendFinish(const char *start, size_type actualSize)
void lockOnce(const FileOpeningConfig &cfg)
locks, blocking or returning immediately depending on the lock waiting mode
const char * xstrerr(int error)
void const char HLPCB void * data
void open(const FileOpeningConfig &cfg)
opens (or creates) the file
a portable locking-aware exception-friendly file (with RAII API)
char * rawAppendStart(size_type anticipatedSize)
#define debugs(SECTION, LEVEL, CONTENT)
SBuf name_
location on disk
File(const SBuf &aFilename, const FileOpeningConfig &cfg)
opens
SBuf sysCallFailure(const char *callName, const char *error) const
SBuf readSmall(SBuf::size_type minBytes, SBuf::size_type maxBytes)
read(2) for small files
mode_t creationMask
umask() parameter; the default is S_IWGRP|S_IWOTH
SBuf sysCallError(const char *callName, const int savedErrno) const
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
void writeAll(const SBuf &data)
write(2) with a "wrote everything" check
void synchronize()
fsync(2)
int flockMode
2nd flock(2) parameter
unsigned int lockAttempts
pause before each lock retry
int openFlags
opening flags; 2nd open(2) parameter
static const unsigned int RetryGapUsec
MemBlob::size_type size_type
mode_t openMode
access mode; 3rd open(2) parameter
static FileOpeningConfig ReadWrite()
FileOpeningConfig & createdIfMissing()
when opening a file for writing, create it if it does not exist
FileOpeningConfig & locked(unsigned int attempts=5)
protect concurrent accesses by attempting to obtain an appropriate lock
const char * rawContent() const
static FileOpeningConfig ReadOnly()
SQUIDCEXTERN int xusleep(unsigned int)
Handle fd_
OS-specific file handle.
void lock(const FileOpeningConfig &cfg)
calls lockOnce() as many times as necessary (including zero)