Coverage Report

Created: 2024-11-15 12:18

/root/bitcoin/src/util/fs_helpers.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2009-2010 Satoshi Nakamoto
2
// Copyright (c) 2009-2023 The Bitcoin Core developers
3
// Distributed under the MIT software license, see the accompanying
4
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6
#include <util/fs_helpers.h>
7
8
#include <bitcoin-build-config.h> // IWYU pragma: keep
9
10
#include <logging.h>
11
#include <sync.h>
12
#include <util/fs.h>
13
#include <util/syserror.h>
14
15
#include <cerrno>
16
#include <fstream>
17
#include <map>
18
#include <memory>
19
#include <optional>
20
#include <string>
21
#include <system_error>
22
#include <utility>
23
24
#ifndef WIN32
25
// for posix_fallocate, in cmake/introspection.cmake we check if it is present after this
26
#ifdef __linux__
27
28
#ifdef _POSIX_C_SOURCE
29
#undef _POSIX_C_SOURCE
30
#endif
31
32
#define _POSIX_C_SOURCE 200112L
33
34
#endif // __linux__
35
36
#include <fcntl.h>
37
#include <sys/resource.h>
38
#include <unistd.h>
39
#else
40
#include <io.h> /* For _get_osfhandle, _chsize */
41
#include <shlobj.h> /* For SHGetSpecialFolderPathW */
42
#endif // WIN32
43
44
/** Mutex to protect dir_locks. */
45
static GlobalMutex cs_dir_locks;
46
/** A map that contains all the currently held directory locks. After
47
 * successful locking, these will be held here until the global destructor
48
 * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
49
 * is called.
50
 */
51
static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks GUARDED_BY(cs_dir_locks);
52
namespace util {
53
LockResult LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only)
54
0
{
55
0
    LOCK(cs_dir_locks);
56
0
    fs::path pathLockFile = directory / lockfile_name;
57
58
    // If a lock for this directory already exists in the map, don't try to re-lock it
59
0
    if (dir_locks.count(fs::PathToString(pathLockFile))) {
60
0
        return LockResult::Success;
61
0
    }
62
63
    // Create empty lock file if it doesn't exist.
64
0
    if (auto created{fsbridge::fopen(pathLockFile, "a")}) {
65
0
        std::fclose(created);
66
0
    } else {
67
0
        return LockResult::ErrorWrite;
68
0
    }
69
0
    auto lock = std::make_unique<fsbridge::FileLock>(pathLockFile);
70
0
    if (!lock->TryLock()) {
71
0
        LogError("Error while attempting to lock directory %s: %s\n", fs::PathToString(directory), lock->GetReason());
72
0
        return LockResult::ErrorLock;
73
0
    }
74
0
    if (!probe_only) {
75
        // Lock successful and we're not just probing, put it into the map
76
0
        dir_locks.emplace(fs::PathToString(pathLockFile), std::move(lock));
77
0
    }
78
0
    return LockResult::Success;
79
0
}
80
} // namespace util
81
void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name)
82
0
{
83
0
    LOCK(cs_dir_locks);
84
0
    dir_locks.erase(fs::PathToString(directory / lockfile_name));
85
0
}
86
87
void ReleaseDirectoryLocks()
88
0
{
89
0
    LOCK(cs_dir_locks);
90
0
    dir_locks.clear();
91
0
}
92
93
bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
94
0
{
95
0
    constexpr uint64_t min_disk_space = 52428800; // 50 MiB
96
97
0
    uint64_t free_bytes_available = fs::space(dir).available;
98
0
    return free_bytes_available >= min_disk_space + additional_bytes;
99
0
}
100
101
std::streampos GetFileSize(const char* path, std::streamsize max)
102
0
{
103
0
    std::ifstream file{path, std::ios::binary};
104
0
    file.ignore(max);
105
0
    return file.gcount();
106
0
}
107
108
bool FileCommit(FILE* file)
109
0
{
110
0
    if (fflush(file) != 0) { // harmless if redundantly called
111
0
        LogPrintf("fflush failed: %s\n", SysErrorString(errno));
112
0
        return false;
113
0
    }
114
#ifdef WIN32
115
    HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
116
    if (FlushFileBuffers(hFile) == 0) {
117
        LogPrintf("FlushFileBuffers failed: %s\n", Win32ErrorString(GetLastError()));
118
        return false;
119
    }
120
#elif defined(__APPLE__) && defined(F_FULLFSYNC)
121
    if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
122
        LogPrintf("fcntl F_FULLFSYNC failed: %s\n", SysErrorString(errno));
123
        return false;
124
    }
125
#elif HAVE_FDATASYNC
126
0
    if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
127
0
        LogPrintf("fdatasync failed: %s\n", SysErrorString(errno));
128
0
        return false;
129
0
    }
130
#else
131
    if (fsync(fileno(file)) != 0 && errno != EINVAL) {
132
        LogPrintf("fsync failed: %s\n", SysErrorString(errno));
133
        return false;
134
    }
135
#endif
136
0
    return true;
137
0
}
138
139
void DirectoryCommit(const fs::path& dirname)
140
0
{
141
0
#ifndef WIN32
142
0
    FILE* file = fsbridge::fopen(dirname, "r");
143
0
    if (file) {
144
0
        fsync(fileno(file));
145
0
        fclose(file);
146
0
    }
147
0
#endif
148
0
}
149
150
bool TruncateFile(FILE* file, unsigned int length)
151
0
{
152
#if defined(WIN32)
153
    return _chsize(_fileno(file), length) == 0;
154
#else
155
0
    return ftruncate(fileno(file), length) == 0;
156
0
#endif
157
0
}
158
159
/**
160
 * this function tries to raise the file descriptor limit to the requested number.
161
 * It returns the actual file descriptor limit (which may be more or less than nMinFD)
162
 */
163
int RaiseFileDescriptorLimit(int nMinFD)
164
0
{
165
#if defined(WIN32)
166
    return 2048;
167
#else
168
0
    struct rlimit limitFD;
169
0
    if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
170
0
        if (limitFD.rlim_cur < (rlim_t)nMinFD) {
171
0
            limitFD.rlim_cur = nMinFD;
172
0
            if (limitFD.rlim_cur > limitFD.rlim_max)
173
0
                limitFD.rlim_cur = limitFD.rlim_max;
174
0
            setrlimit(RLIMIT_NOFILE, &limitFD);
175
0
            getrlimit(RLIMIT_NOFILE, &limitFD);
176
0
        }
177
0
        return limitFD.rlim_cur;
178
0
    }
179
0
    return nMinFD; // getrlimit failed, assume it's fine
180
0
#endif
181
0
}
182
183
/**
184
 * this function tries to make a particular range of a file allocated (corresponding to disk space)
185
 * it is advisory, and the range specified in the arguments will never contain live data
186
 */
187
void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
188
0
{
189
#if defined(WIN32)
190
    // Windows-specific version
191
    HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
192
    LARGE_INTEGER nFileSize;
193
    int64_t nEndPos = (int64_t)offset + length;
194
    nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
195
    nFileSize.u.HighPart = nEndPos >> 32;
196
    SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
197
    SetEndOfFile(hFile);
198
#elif defined(__APPLE__)
199
    // OSX specific version
200
    // NOTE: Contrary to other OS versions, the OSX version assumes that
201
    // NOTE: offset is the size of the file.
202
    fstore_t fst;
203
    fst.fst_flags = F_ALLOCATECONTIG;
204
    fst.fst_posmode = F_PEOFPOSMODE;
205
    fst.fst_offset = 0;
206
    fst.fst_length = length; // mac os fst_length takes the # of free bytes to allocate, not desired file size
207
    fst.fst_bytesalloc = 0;
208
    if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) {
209
        fst.fst_flags = F_ALLOCATEALL;
210
        fcntl(fileno(file), F_PREALLOCATE, &fst);
211
    }
212
    ftruncate(fileno(file), static_cast<off_t>(offset) + length);
213
#else
214
0
#if defined(HAVE_POSIX_FALLOCATE)
215
    // Version using posix_fallocate
216
0
    off_t nEndPos = (off_t)offset + length;
217
0
    if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
218
0
#endif
219
    // Fallback version
220
    // TODO: just write one byte per block
221
0
    static const char buf[65536] = {};
222
0
    if (fseek(file, offset, SEEK_SET)) {
223
0
        return;
224
0
    }
225
0
    while (length > 0) {
226
0
        unsigned int now = 65536;
227
0
        if (length < now)
228
0
            now = length;
229
0
        fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
230
0
        length -= now;
231
0
    }
232
0
#endif
233
0
}
234
235
#ifdef WIN32
236
fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
237
{
238
    WCHAR pszPath[MAX_PATH] = L"";
239
240
    if (SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate)) {
241
        return fs::path(pszPath);
242
    }
243
244
    LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
245
    return fs::path("");
246
}
247
#endif
248
249
bool RenameOver(fs::path src, fs::path dest)
250
0
{
251
0
    std::error_code error;
252
0
    fs::rename(src, dest, error);
253
0
    return !error;
254
0
}
255
256
/**
257
 * Ignores exceptions thrown by create_directories if the requested directory exists.
258
 * Specifically handles case where path p exists, but it wasn't possible for the user to
259
 * write to the parent directory.
260
 */
261
bool TryCreateDirectories(const fs::path& p)
262
0
{
263
0
    try {
264
0
        return fs::create_directories(p);
265
0
    } catch (const fs::filesystem_error&) {
266
0
        if (!fs::exists(p) || !fs::is_directory(p))
267
0
            throw;
268
0
    }
269
270
    // create_directories didn't create the directory, it had to have existed already
271
0
    return false;
272
0
}
273
274
std::string PermsToSymbolicString(fs::perms p)
275
0
{
276
0
    std::string perm_str(9, '-');
277
278
0
    auto set_perm = [&](size_t pos, fs::perms required_perm, char letter) {
279
0
        if ((p & required_perm) != fs::perms::none) {
280
0
            perm_str[pos] = letter;
281
0
        }
282
0
    };
283
284
0
    set_perm(0, fs::perms::owner_read,   'r');
285
0
    set_perm(1, fs::perms::owner_write,  'w');
286
0
    set_perm(2, fs::perms::owner_exec,   'x');
287
0
    set_perm(3, fs::perms::group_read,   'r');
288
0
    set_perm(4, fs::perms::group_write,  'w');
289
0
    set_perm(5, fs::perms::group_exec,   'x');
290
0
    set_perm(6, fs::perms::others_read,  'r');
291
0
    set_perm(7, fs::perms::others_write, 'w');
292
0
    set_perm(8, fs::perms::others_exec,  'x');
293
294
0
    return perm_str;
295
0
}
296
297
std::optional<fs::perms> InterpretPermString(const std::string& s)
298
0
{
299
0
    if (s == "owner") {
300
0
        return fs::perms::owner_read | fs::perms::owner_write;
301
0
    } else if (s == "group") {
302
0
        return fs::perms::owner_read | fs::perms::owner_write |
303
0
               fs::perms::group_read;
304
0
    } else if (s == "all") {
305
0
        return fs::perms::owner_read | fs::perms::owner_write |
306
0
               fs::perms::group_read |
307
0
               fs::perms::others_read;
308
0
    } else {
309
0
        return std::nullopt;
310
0
    }
311
0
}