File.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9#include "squid.h"
10#include "base/File.h"
11#include "debug/Stream.h"
12#include "sbuf/Stream.h"
13#include "tools.h"
14#include "xusleep.h"
15
16#include <utility>
17
18#if HAVE_FCNTL_H
19#include <fcntl.h>
20#endif
21#if HAVE_SYS_STAT_H
22#include <sys/stat.h>
23#endif
24#if HAVE_UNISTD_H
25#include <unistd.h>
26#endif
27
28/* FileOpeningConfig */
29
32{
34
35 /* I/O */
36#if _SQUID_WINDOWS_
37 cfg.desiredAccess = GENERIC_READ;
38 cfg.shareMode = FILE_SHARE_READ;
39#else
40 cfg.openFlags = O_RDONLY;
41#endif
42
43 /* locking (if enabled later) */
44#if _SQUID_WINDOWS_
45 cfg.lockFlags = 0; // no named constant for a shared lock
46#elif _SQUID_SOLARIS_
47 cfg.lockType = F_RDLCK;
48#else
49 cfg.flockMode = LOCK_SH | LOCK_NB;
50#endif
51
52 return cfg;
53}
54
57{
59
60 /* I/O */
61#if _SQUID_WINDOWS_
62 cfg.desiredAccess = GENERIC_READ | GENERIC_WRITE;
63 cfg.shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
64#else
65 cfg.openFlags = O_RDWR;
66#endif
67
68 /* locking (if enabled later) */
69#if _SQUID_WINDOWS_
70 cfg.lockFlags = LOCKFILE_EXCLUSIVE_LOCK;
71#elif _SQUID_SOLARIS_
72 cfg.lockType = F_WRLCK;
73#else
74 cfg.flockMode = LOCK_EX | LOCK_NB;
75#endif
76
77 return cfg;
78}
79
81FileOpeningConfig::locked(unsigned int attempts)
82{
83 lockAttempts = attempts;
84 // for simplicity, correct locking flags are preset in constructing methods
85 return *this;
86}
87
90{
91#if _SQUID_WINDOWS_
92 Must((desiredAccess & GENERIC_WRITE) == GENERIC_WRITE);
93 creationDisposition = OPEN_ALWAYS;
94#else
95 Must((openFlags & O_RDWR) == O_RDWR);
96 openFlags |= O_CREAT;
97 creationMask = (S_IXUSR | S_IXGRP|S_IWGRP | S_IXOTH|S_IWOTH); // unwanted bits
98#endif
99 return *this;
100}
101
102/* File */
103
104#if _SQUID_SOLARIS_
105// XXX: fcntl() locks are incompatible with complex applications that may lock
106// multiple open descriptors corresponding to the same underlying file. There is
107// nothing better on Solaris, but do not be tempted to use this elsewhere. For
108// more info, see http://bugs.squid-cache.org/show_bug.cgi?id=4212#c14
110static int
111fcntlLock(const int fd, const short lockType)
112{
113 // the exact composition and order of flock data members is unknown!
114 struct flock fl;
115 memset(&fl, 0, sizeof(fl));
116 fl.l_type = lockType;
117 fl.l_whence = SEEK_SET; // with zero l_len and l_start, means "whole file"
118 return ::fcntl(fd, F_SETLK, &fl);
119}
120#endif // _SQUID_SOLARIS_
121
122File *
123File::Optional(const SBuf &filename, const FileOpeningConfig &cfg)
124{
125 try {
126 return new File(filename, cfg);
127 }
128 catch (const std::exception &ex) {
129 debugs(54, 5, "will not lock: " << ex.what());
130 }
131 return nullptr;
132}
133
134File::File(const SBuf &aName, const FileOpeningConfig &cfg):
135 name_(aName)
136{
137 debugs(54, 7, "constructing, this=" << this << ' ' << name_);
138 // close the file during post-open constructor exceptions
139 try {
140 open(cfg);
141 lock(cfg);
142 }
143 catch (...)
144 {
145 close();
146 throw;
147 }
148}
149
151{
152 debugs(54, 7, "destructing, this=" << this << ' ' << name_);
153 close();
154}
155
157{
158 *this = std::move(other);
159}
160
161File &
163{
164 std::swap(fd_, other.fd_);
165 return *this;
166}
167
169void
171{
172#if _SQUID_WINDOWS_
173 fd_ = CreateFile(TEXT(name_.c_str()), cfg.desiredAccess, cfg.shareMode, nullptr, cfg.creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
174 if (fd_ == InvalidHandle) {
175 const auto savedError = GetLastError();
176 throw TexcHere(sysCallFailure("CreateFile", WindowsErrorMessage(savedError)));
177 }
178#else
179 mode_t oldCreationMask = 0;
180 const auto filename = name_.c_str(); // avoid complex operations inside enter_suid()
181 enter_suid();
182 if (cfg.creationMask)
183 oldCreationMask = umask(cfg.creationMask); // XXX: Why here? Should not this be set for the whole Squid?
184 fd_ = ::open(filename, cfg.openFlags, cfg.openMode);
185 const auto savedErrno = errno;
186 if (cfg.creationMask)
187 umask(oldCreationMask);
188 leave_suid();
189 if (fd_ < 0)
190 throw TexcHere(sysCallError("open", savedErrno));
191#endif
192}
193
194void
196{
197 if (!isOpen())
198 return;
199#if _SQUID_WINDOWS_
200 if (!CloseHandle(fd_)) {
201 const auto savedError = GetLastError();
202 debugs(54, DBG_IMPORTANT, sysCallFailure("CloseHandle", WindowsErrorMessage(savedError)));
203 }
204#else
205 if (::close(fd_) != 0) {
206 const auto savedErrno = errno;
207 debugs(54, DBG_IMPORTANT, sysCallError("close", savedErrno));
208 }
209#endif
210 // closing the file handler implicitly removes all associated locks
211}
212
213void
215{
216#if _SQUID_WINDOWS_
217 if (!SetFilePointer(fd_, 0, nullptr, FILE_BEGIN)) {
218 const auto savedError = GetLastError();
219 throw TexcHere(sysCallFailure("SetFilePointer", WindowsErrorMessage(savedError)));
220 }
221
222 if (!SetEndOfFile(fd_)) {
223 const auto savedError = GetLastError();
224 throw TexcHere(sysCallFailure("SetEndOfFile", WindowsErrorMessage(savedError)));
225 }
226#else
227 if (::lseek(fd_, SEEK_SET, 0) < 0) {
228 const auto savedErrno = errno;
229 throw TexcHere(sysCallError("lseek", savedErrno));
230 }
231
232 if (::ftruncate(fd_, 0) != 0) {
233 const auto savedErrno = errno;
234 throw TexcHere(sysCallError("ftruncate", savedErrno));
235 }
236#endif
237}
238
239SBuf
240File::readSmall(const SBuf::size_type minBytes, const SBuf::size_type maxBytes)
241{
242 SBuf buf;
243 const auto readLimit = maxBytes + 1; // to detect excessively large files that we do not handle
244 char *rawBuf = buf.rawAppendStart(readLimit);
245#if _SQUID_WINDOWS_
246 DWORD result = 0;
247 if (!ReadFile(fd_, rawBuf, readLimit, &result, nullptr)) {
248 const auto savedError = GetLastError();
249 throw TexcHere(sysCallFailure("ReadFile", WindowsErrorMessage(savedError)));
250 }
251#else
252 const auto result = ::read(fd_, rawBuf, readLimit);
253 if (result < 0) {
254 const auto savedErrno = errno;
255 throw TexcHere(sysCallError("read", savedErrno));
256 }
257#endif
258 const auto bytesRead = static_cast<size_t>(result);
259 assert(bytesRead <= readLimit);
260 Must(!buf.length());
261 buf.rawAppendFinish(rawBuf, bytesRead);
262
263 if (buf.length() < minBytes) {
264 static const SBuf errPrematureEof("premature eof");
265 static const SBuf errEmptyFile("empty file");
266 throw TexcHere(sysCallFailure("read", buf.length() ? errPrematureEof : errEmptyFile));
267 }
268
269 if (buf.length() > maxBytes) {
270 static const SBuf failure("unreasonably large file");
271 throw TexcHere(sysCallFailure("read", failure));
272 }
273
274 Must(minBytes <= buf.length() && buf.length() <= maxBytes);
275 return buf;
276}
277
278void
280{
281#if _SQUID_WINDOWS_
282 DWORD nBytesWritten = 0;
283 if (!WriteFile(fd_, data.rawContent(), data.length(), &nBytesWritten, nullptr)) {
284 const auto savedError = GetLastError();
285 throw TexcHere(sysCallFailure("WriteFile", WindowsErrorMessage(savedError)));
286 }
287 const auto bytesWritten = static_cast<size_t>(nBytesWritten);
288#else
289 const auto result = ::write(fd_, data.rawContent(), data.length());
290 if (result < 0) {
291 const auto savedErrno = errno;
292 throw TexcHere(sysCallError("write", savedErrno));
293 }
294 const auto bytesWritten = static_cast<size_t>(result);
295#endif
296 if (bytesWritten != data.length()) {
297 static const SBuf failure("partial write");
298 throw TexcHere(sysCallFailure("write", failure));
299 }
300}
301
302void
304{
305#if _SQUID_WINDOWS_
306 if (!FlushFileBuffers(fd_)) {
307 const auto savedError = GetLastError();
308 throw TexcHere(sysCallFailure("FlushFileBuffers", WindowsErrorMessage(savedError)));
309 }
310#else
311 if (::fsync(fd_) != 0) {
312 const auto savedErrno = errno;
313 throw TexcHere(sysCallError("fsync", savedErrno));
314 }
315#endif
316}
317
319void
321{
322 unsigned int attemptsLeft = cfg.lockAttempts;
323 while (attemptsLeft) {
324 try {
325 --attemptsLeft;
326 return lockOnce(cfg);
327 } catch (const std::exception &ex) {
328 if (!attemptsLeft)
329 throw;
330 debugs(54, 4, "sleeping and then trying up to " << attemptsLeft <<
331 " more time(s) after a failure: " << ex.what());
332 }
333 Must(attemptsLeft); // the catch statement handles the last attempt
335 }
336 debugs(54, 9, "disabled");
337}
338
340void
342{
343#if _SQUID_WINDOWS_
344 if (!LockFileEx(fd_, cfg.lockFlags, 0, 0, 1, 0)) {
345 const auto savedError = GetLastError();
346 throw TexcHere(sysCallFailure("LockFileEx", WindowsErrorMessage(savedError)));
347 }
348#elif _SQUID_SOLARIS_
349 if (fcntlLock(fd_, cfg.lockType) != 0) {
350 const auto savedErrno = errno;
351 throw TexcHere(sysCallError("fcntl(flock)", savedErrno));
352 }
353#else
354 if (::flock(fd_, cfg.flockMode) != 0) {
355 const auto savedErrno = errno;
356 throw TexcHere(sysCallError("flock", savedErrno));
357 }
358#endif
359 debugs(54, 3, "succeeded for " << name_);
360}
361
363SBuf
364File::sysCallFailure(const char *callName, const SBuf &error) const
365{
366 return ToSBuf("failed to ", callName, ' ', name_, ": ", error);
367}
368
370SBuf
371File::sysCallError(const char *callName, const int savedErrno) const
372{
373 return sysCallFailure(callName, SBuf(xstrerr(savedErrno)));
374}
375
376#if _SQUID_WINDOWS_
377const HANDLE File::InvalidHandle = INVALID_HANDLE_VALUE;
378#endif /* _SQUID_WINDOWS_ */
379
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:59
#define Must(condition)
Definition: TextException.h:71
void error(char *format,...)
#define assert(EX)
Definition: assert.h:19
How should a file be opened/created? Should it be locked?
Definition: File.h:20
static FileOpeningConfig ReadWrite()
Definition: File.cc:56
unsigned int lockAttempts
pause before each lock retry
Definition: File.h:61
mode_t openMode
access mode; 3rd open(2) parameter
Definition: File.h:49
static const unsigned int RetryGapUsec
Definition: File.h:60
int openFlags
opening flags; 2nd open(2) parameter
Definition: File.h:48
static FileOpeningConfig ReadOnly()
Definition: File.cc:31
int flockMode
2nd flock(2) parameter
Definition: File.h:58
mode_t creationMask
umask() parameter; the default is S_IWGRP|S_IWOTH
Definition: File.h:47
FileOpeningConfig & locked(unsigned int attempts=5)
protect concurrent accesses by attempting to obtain an appropriate lock
Definition: File.cc:81
FileOpeningConfig & createdIfMissing()
when opening a file for writing, create it if it does not exist
Definition: File.cc:89
a portable locking-aware exception-friendly file (with RAII API)
Definition: File.h:67
File(const SBuf &aFilename, const FileOpeningConfig &cfg)
opens
Definition: File.cc:134
Handle fd_
OS-specific file handle.
Definition: File.h:123
void lock(const FileOpeningConfig &cfg)
calls lockOnce() as many times as necessary (including zero)
Definition: File.cc:320
void writeAll(const SBuf &data)
write(2) with a "wrote everything" check
Definition: File.cc:279
void open(const FileOpeningConfig &cfg)
opens (or creates) the file
Definition: File.cc:170
static File * Optional(const SBuf &aName, const FileOpeningConfig &cfg)
Definition: File.cc:123
void close()
Definition: File.cc:195
File & operator=(const File &)=delete
void lockOnce(const FileOpeningConfig &cfg)
locks, blocking or returning immediately depending on the lock waiting mode
Definition: File.cc:341
SBuf sysCallError(const char *callName, const int savedErrno) const
Definition: File.cc:371
SBuf sysCallFailure(const char *callName, const SBuf &error) const
Definition: File.cc:364
static const Handle InvalidHandle
Definition: File.h:121
bool isOpen() const
Definition: File.h:94
void truncate()
makes the file size (and the current I/O offset) zero
Definition: File.cc:214
~File()
closes
Definition: File.cc:150
void synchronize()
fsync(2)
Definition: File.cc:303
SBuf readSmall(SBuf::size_type minBytes, SBuf::size_type maxBytes)
read(2) for small files
Definition: File.cc:240
SBuf name_
location on disk
Definition: File.h:113
Definition: SBuf.h:94
char * rawAppendStart(size_type anticipatedSize)
Definition: SBuf.cc:136
const char * rawContent() const
Definition: SBuf.cc:509
const char * c_str()
Definition: SBuf.cc:516
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:415
MemBlob::size_type size_type
Definition: SBuf.h:96
void rawAppendFinish(const char *start, size_type actualSize)
Definition: SBuf.cc:144
#define DBG_IMPORTANT
Definition: Stream.h:41
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
void leave_suid(void)
Definition: tools.cc:556
void enter_suid(void)
Definition: tools.cc:620
unsigned short mode_t
Definition: types.h:150
const char * xstrerr(int error)
Definition: xstrerror.cc:83
SQUIDCEXTERN int xusleep(unsigned int)
Definition: xusleep.c:20

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors