File.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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.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 
81 FileOpeningConfig::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
110 int
111 fcntlLock(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 
122 File *
123 File::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 
134 File::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 
156 File::File(File &&other)
157 {
158  *this = std::move(other);
159 }
160 
161 File &
163 {
164  std::swap(fd_, other.fd_);
165  return *this;
166 }
167 
169 void
171 {
172 #if _SQUID_WINDOWS_
173  fd_ = CreateFile(TEXT(name_.c_str()), desiredAccess, shareMode, nullptr, creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
174  if (fd_ == InvalidHandle) {
175  const auto savedError = GetLastError();
176  throw TexcHere(sysCallFailure("CreateFile", WindowsErrorMessage(savedError).c_str()));
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 
194 void
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 
213 void
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).c_str()));
220  }
221 
222  if (!SetEndOfFile(fd_)) {
223  const auto savedError = GetLastError();
224  throw TexcHere(sysCallFailure("SetEndOfFile", WindowsErrorMessage(savedError).c_str()));
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 
239 SBuf
240 File::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).c_str()));
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  const auto failure = buf.length() ? "premature eof" : "empty file";
265  throw TexcHere(sysCallFailure("read", failure));
266  }
267 
268  if (buf.length() > maxBytes) {
269  const auto failure = "unreasonably large file";
270  throw TexcHere(sysCallFailure("read", failure));
271  }
272 
273  Must(minBytes <= buf.length() && buf.length() <= maxBytes);
274  return buf;
275 }
276 
277 void
279 {
280 #if _SQUID_WINDOWS_
281  DWORD nBytesWritten = 0;
282  if (!WriteFile(fd_, data.rawContent(), data.length(), &nBytesWritten, nullptr)) {
283  const auto savedError = GetLastError();
284  throw TexcHere(sysCallFailure("WriteFile", WindowsErrorMessage(savedError).c_str()));
285  }
286  const auto bytesWritten = static_cast<size_t>(nBytesWritten);
287 #else
288  const auto result = ::write(fd_, data.rawContent(), data.length());
289  if (result < 0) {
290  const auto savedErrno = errno;
291  throw TexcHere(sysCallError("write", savedErrno));
292  }
293  const auto bytesWritten = static_cast<size_t>(result);
294 #endif
295  if (bytesWritten != data.length())
296  throw TexcHere(sysCallFailure("write", "partial write"));
297 }
298 
299 void
301 {
302 #if _SQUID_WINDOWS_
303  if (!FlushFileBuffers(fd_)) {
304  const auto savedError = GetLastError();
305  throw TexcHere(sysCallFailure("FlushFileBuffers", WindowsErrorMessage(savedError).c_str()));
306  }
307 #else
308  if (::fsync(fd_) != 0) {
309  const auto savedErrno = errno;
310  throw TexcHere(sysCallError("fsync", savedErrno));
311  }
312 #endif
313 }
314 
316 void
318 {
319  unsigned int attemptsLeft = cfg.lockAttempts;
320  while (attemptsLeft) {
321  try {
322  --attemptsLeft;
323  return lockOnce(cfg);
324  } catch (const std::exception &ex) {
325  if (!attemptsLeft)
326  throw;
327  debugs(54, 4, "sleeping and then trying up to " << attemptsLeft <<
328  " more time(s) after a failure: " << ex.what());
329  }
330  Must(attemptsLeft); // the catch statement handles the last attempt
331  xusleep(cfg.RetryGapUsec);
332  }
333  debugs(54, 9, "disabled");
334 }
335 
337 void
339 {
340 #if _SQUID_WINDOWS_
341  if (!LockFileEx(fd_, cfg.lockFlags, 0, 0, 1, 0)) {
342  const auto savedError = GetLastError();
343  throw TexcHere(sysCallFailure("LockFileEx", WindowsErrorMessage(savedError).c_str()));
344  }
345 #elif _SQUID_SOLARIS_
346  if (fcntlLock(fd_, cfg.lockType) != 0) {
347  const auto savedErrno = errno;
348  throw TexcHere(sysCallError("fcntl(flock)", savedErrno));
349  }
350 #else
351  if (::flock(fd_, cfg.flockMode) != 0) {
352  const auto savedErrno = errno;
353  throw TexcHere(sysCallError("flock", savedErrno));
354  }
355 #endif
356  debugs(54, 3, "succeeded for " << name_);
357 }
358 
360 SBuf
361 File::sysCallFailure(const char *callName, const char *error) const
362 {
363  return ToSBuf("failed to ", callName, ' ', name_, ": ", error);
364 }
365 
367 SBuf
368 File::sysCallError(const char *callName, const int savedErrno) const
369 {
370  return sysCallFailure(callName, xstrerr(savedErrno));
371 }
372 
static const Handle InvalidHandle
Definition: File.h:121
#define assert(EX)
Definition: assert.h:17
void truncate()
makes the file size (and the current I/O offset) zero
Definition: File.cc:214
static File * Optional(const SBuf &aName, const FileOpeningConfig &cfg)
Definition: File.cc:123
Definition: SBuf.h:87
void error(char *format,...)
How should a file be opened/created? Should it be locked?
Definition: File.h:19
File & operator=(const File &)=delete
void rawAppendFinish(const char *start, size_type actualSize)
Definition: SBuf.cc:155
void lockOnce(const FileOpeningConfig &cfg)
locks, blocking or returning immediately depending on the lock waiting mode
Definition: File.cc:338
const char * xstrerr(int error)
Definition: xstrerror.cc:83
void const char HLPCB void * data
Definition: stub_helper.cc:16
void open(const FileOpeningConfig &cfg)
opens (or creates) the file
Definition: File.cc:170
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:405
a portable locking-aware exception-friendly file (with RAII API)
Definition: File.h:66
~File()
closes
Definition: File.cc:150
char * rawAppendStart(size_type anticipatedSize)
Definition: SBuf.cc:147
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
#define DBG_IMPORTANT
Definition: Debug.h:45
SBuf name_
location on disk
Definition: File.h:113
File(const SBuf &aFilename, const FileOpeningConfig &cfg)
opens
Definition: File.cc:134
void leave_suid(void)
Definition: tools.cc:504
SBuf sysCallFailure(const char *callName, const char *error) const
Definition: File.cc:361
const char * c_str()
Definition: SBuf.cc:546
SBuf sysCallError(const char *callName, const int savedErrno) const
Definition: File.cc:368
void const char * buf
Definition: stub_helper.cc:16
unsigned short mode_t
Definition: types.h:150
SBuf readSmall(SBuf::size_type minBytes, SBuf::size_type maxBytes)
read(2) for small files
Definition: File.cc:240
#define Must(cond)
Definition: TextException.h:89
typedef DWORD
Definition: WinSvc.cc:73
void enter_suid(void)
Definition: tools.cc:575
mode_t creationMask
umask() parameter; the default is S_IWGRP|S_IWOTH
Definition: File.h:47
SBuf ToSBuf(Args &&...args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:124
#define TexcHere(msg)
Definition: TextException.h:81
void writeAll(const SBuf &data)
write(2) with a "wrote everything" check
Definition: File.cc:278
void synchronize()
fsync(2)
Definition: File.cc:300
int flockMode
2nd flock(2) parameter
Definition: File.h:58
unsigned int lockAttempts
pause before each lock retry
Definition: File.h:61
int openFlags
opening flags; 2nd open(2) parameter
Definition: File.h:48
void close()
Definition: File.cc:195
static const unsigned int RetryGapUsec
Definition: File.h:60
const char * rawContent() const
Definition: SBuf.cc:539
MemBlob::size_type size_type
Definition: SBuf.h:90
mode_t openMode
access mode; 3rd open(2) parameter
Definition: File.h:49
static FileOpeningConfig ReadWrite()
Definition: File.cc:56
bool isOpen() const
Definition: File.h:94
FileOpeningConfig & createdIfMissing()
when opening a file for writing, create it if it does not exist
Definition: File.cc:89
FileOpeningConfig & locked(unsigned int attempts=5)
protect concurrent accesses by attempting to obtain an appropriate lock
Definition: File.cc:81
static FileOpeningConfig ReadOnly()
Definition: File.cc:31
SQUIDCEXTERN int xusleep(unsigned int)
Definition: xusleep.c:20
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:317

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors