]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/base/File.cc
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
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.
10 #include "base/File.h"
12 #include "sbuf/Stream.h"
28 /* FileOpeningConfig */
31 FileOpeningConfig::ReadOnly()
33 FileOpeningConfig cfg
;
37 cfg
.desiredAccess
= GENERIC_READ
;
38 cfg
.shareMode
= FILE_SHARE_READ
;
40 cfg
.openFlags
= O_RDONLY
;
43 /* locking (if enabled later) */
45 cfg
.lockFlags
= 0; // no named constant for a shared lock
47 cfg
.lockType
= F_RDLCK
;
49 cfg
.flockMode
= LOCK_SH
| LOCK_NB
;
56 FileOpeningConfig::ReadWrite()
58 FileOpeningConfig cfg
;
62 cfg
.desiredAccess
= GENERIC_READ
| GENERIC_WRITE
;
63 cfg
.shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
65 cfg
.openFlags
= O_RDWR
;
68 /* locking (if enabled later) */
70 cfg
.lockFlags
= LOCKFILE_EXCLUSIVE_LOCK
;
72 cfg
.lockType
= F_WRLCK
;
74 cfg
.flockMode
= LOCK_EX
| LOCK_NB
;
81 FileOpeningConfig::locked(unsigned int attempts
)
83 lockAttempts
= attempts
;
84 // for simplicity, correct locking flags are preset in constructing methods
89 FileOpeningConfig::createdIfMissing()
92 Must((desiredAccess
& GENERIC_WRITE
) == GENERIC_WRITE
);
93 creationDisposition
= OPEN_ALWAYS
;
95 Must((openFlags
& O_RDWR
) == O_RDWR
);
97 creationMask
= (S_IXUSR
| S_IXGRP
|S_IWGRP
| S_IXOTH
|S_IWOTH
); // unwanted bits
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
109 /// fcntl(... struct flock) convenience wrapper
111 fcntlLock(const int fd
, const short lockType
)
113 // the exact composition and order of flock data members is unknown!
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
);
120 #endif // _SQUID_SOLARIS_
123 File::Optional(const SBuf
&filename
, const FileOpeningConfig
&cfg
)
126 return new File(filename
, cfg
);
128 catch (const std::exception
&ex
) {
129 debugs(54, 5, "will not lock: " << ex
.what());
134 File::File(const SBuf
&aName
, const FileOpeningConfig
&cfg
):
137 debugs(54, 7, "constructing, this=" << this << ' ' << name_
);
138 // close the file during post-open constructor exceptions
152 debugs(54, 7, "destructing, this=" << this << ' ' << name_
);
156 File::File(File
&&other
)
158 *this = std::move(other
);
162 File::operator = (File
&&other
)
164 std::swap(fd_
, other
.fd_
);
168 /// opens (or creates) the file
170 File::open(const FileOpeningConfig
&cfg
)
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
)));
179 mode_t oldCreationMask
= 0;
180 const auto filename
= name_
.c_str(); // avoid complex operations inside 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
);
190 throw TexcHere(sysCallError("open", savedErrno
));
200 if (!CloseHandle(fd_
)) {
201 const auto savedError
= GetLastError();
202 debugs(54, DBG_IMPORTANT
, sysCallFailure("CloseHandle", WindowsErrorMessage(savedError
)));
205 if (::close(fd_
) != 0) {
206 const auto savedErrno
= errno
;
207 debugs(54, DBG_IMPORTANT
, sysCallError("close", savedErrno
));
210 // closing the file handler implicitly removes all associated locks
217 if (!SetFilePointer(fd_
, 0, nullptr, FILE_BEGIN
)) {
218 const auto savedError
= GetLastError();
219 throw TexcHere(sysCallFailure("SetFilePointer", WindowsErrorMessage(savedError
)));
222 if (!SetEndOfFile(fd_
)) {
223 const auto savedError
= GetLastError();
224 throw TexcHere(sysCallFailure("SetEndOfFile", WindowsErrorMessage(savedError
)));
227 if (::lseek(fd_
, SEEK_SET
, 0) < 0) {
228 const auto savedErrno
= errno
;
229 throw TexcHere(sysCallError("lseek", savedErrno
));
232 if (::ftruncate(fd_
, 0) != 0) {
233 const auto savedErrno
= errno
;
234 throw TexcHere(sysCallError("ftruncate", savedErrno
));
240 File::readSmall(const SBuf::size_type minBytes
, const SBuf::size_type maxBytes
)
243 const auto readLimit
= maxBytes
+ 1; // to detect excessively large files that we do not handle
244 char *rawBuf
= buf
.rawAppendStart(readLimit
);
247 if (!ReadFile(fd_
, rawBuf
, readLimit
, &result
, nullptr)) {
248 const auto savedError
= GetLastError();
249 throw TexcHere(sysCallFailure("ReadFile", WindowsErrorMessage(savedError
)));
252 const auto result
= ::read(fd_
, rawBuf
, readLimit
);
254 const auto savedErrno
= errno
;
255 throw TexcHere(sysCallError("read", savedErrno
));
258 const auto bytesRead
= static_cast<size_t>(result
);
259 assert(bytesRead
<= readLimit
);
261 buf
.rawAppendFinish(rawBuf
, bytesRead
);
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
));
269 if (buf
.length() > maxBytes
) {
270 static const SBuf
failure("unreasonably large file");
271 throw TexcHere(sysCallFailure("read", failure
));
274 Must(minBytes
<= buf
.length() && buf
.length() <= maxBytes
);
279 File::writeAll(const SBuf
&data
)
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
)));
287 const auto bytesWritten
= static_cast<size_t>(nBytesWritten
);
289 const auto result
= ::write(fd_
, data
.rawContent(), data
.length());
291 const auto savedErrno
= errno
;
292 throw TexcHere(sysCallError("write", savedErrno
));
294 const auto bytesWritten
= static_cast<size_t>(result
);
296 if (bytesWritten
!= data
.length()) {
297 static const SBuf
failure("partial write");
298 throw TexcHere(sysCallFailure("write", failure
));
306 if (!FlushFileBuffers(fd_
)) {
307 const auto savedError
= GetLastError();
308 throw TexcHere(sysCallFailure("FlushFileBuffers", WindowsErrorMessage(savedError
)));
311 if (::fsync(fd_
) != 0) {
312 const auto savedErrno
= errno
;
313 throw TexcHere(sysCallError("fsync", savedErrno
));
318 /// calls lockOnce() as many times as necessary (including zero)
320 File::lock(const FileOpeningConfig
&cfg
)
322 unsigned int attemptsLeft
= cfg
.lockAttempts
;
323 while (attemptsLeft
) {
326 return lockOnce(cfg
);
327 } catch (const std::exception
&ex
) {
330 debugs(54, 4, "sleeping and then trying up to " << attemptsLeft
<<
331 " more time(s) after a failure: " << ex
.what());
333 Must(attemptsLeft
); // the catch statement handles the last attempt
334 xusleep(cfg
.RetryGapUsec
);
336 debugs(54, 9, "disabled");
339 /// locks, blocking or returning immediately depending on the lock waiting mode
341 File::lockOnce(const FileOpeningConfig
&cfg
)
344 if (!LockFileEx(fd_
, cfg
.lockFlags
, 0, 0, 1, 0)) {
345 const auto savedError
= GetLastError();
346 throw TexcHere(sysCallFailure("LockFileEx", WindowsErrorMessage(savedError
)));
348 #elif _SQUID_SOLARIS_
349 if (fcntlLock(fd_
, cfg
.lockType
) != 0) {
350 const auto savedErrno
= errno
;
351 throw TexcHere(sysCallError("fcntl(flock)", savedErrno
));
354 if (::flock(fd_
, cfg
.flockMode
) != 0) {
355 const auto savedErrno
= errno
;
356 throw TexcHere(sysCallError("flock", savedErrno
));
359 debugs(54, 3, "succeeded for " << name_
);
362 /// \returns a description a system call-related failure
364 File::sysCallFailure(const char *callName
, const SBuf
&error
) const
366 return ToSBuf("failed to ", callName
, ' ', name_
, ": ", error
);
369 /// \returns a description of an errno-based system call failure
371 File::sysCallError(const char *callName
, const int savedErrno
) const
373 return sysCallFailure(callName
, SBuf(xstrerr(savedErrno
)));
377 const HANDLE
File::InvalidHandle
= INVALID_HANDLE_VALUE
;
378 #endif /* _SQUID_WINDOWS_ */