Belle II Software development
FileSystem.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9#include <framework/utilities/FileSystem.h>
10
11#include <framework/logging/Logger.h>
12
13#include <boost/algorithm/string.hpp>
14
15#include <chrono>
16#include <random>
17#include <cstring>
18#include <filesystem>
19
20//dlopen etc.
21#include <dlfcn.h>
22#include <fcntl.h>
23#include <unistd.h>
24
25#include <TMD5.h>
26#include <zlib.h>
27
28using namespace std;
29using namespace Belle2;
30namespace fs = std::filesystem;
31
32bool FileSystem::fileExists(const string& filename)
33{
34 fs::path fullPath = fs::absolute(filename);
35 return fs::exists(fullPath);
36}
37
38bool FileSystem::fileDirExists(const string& filename)
39{
40 fs::path fullPath = fs::absolute(filename);
41 fullPath.remove_filename();
42 return fs::exists(fullPath);
43}
44
45bool FileSystem::isFile(const string& filename)
46{
47 fs::path fullPath = fs::absolute(filename);
48 return (fs::exists(fullPath)) && (fs::is_regular_file(fullPath));
49}
50
51bool FileSystem::isDir(const string& filename)
52{
53 fs::path fullPath = fs::absolute(filename);
54 return (fs::exists(fullPath)) && (fs::is_directory(fullPath));
55}
56
57bool FileSystem::isSymLink(const string& filename)
58{
59 fs::path fullPath = fs::absolute(filename);
60 return (fs::exists(fullPath)) && (fs::is_symlink(fullPath));
61}
62
63bool FileSystem::loadLibrary(std::string library, bool fullname)
64{
65 if (!fullname) library = "lib" + library + ".so";
66
67 B2DEBUG(100, "Loading shared library " << library);
68 void* libPointer = dlopen(library.c_str(), RTLD_LAZY | RTLD_GLOBAL);
69
70 if (libPointer == nullptr) {
71 B2ERROR("Could not open shared library file (error in dlopen) : " << dlerror());
72 return false;
73 }
74
75 return true;
76}
77
78std::string FileSystem::calculateMD5(const std::string& filename)
79{
80 if (not isFile(filename)) return "";
81 fs::path fullPath = fs::absolute(filename);
82 std::unique_ptr<TMD5> md5(TMD5::FileChecksum(fullPath.c_str()));
83 return md5->AsString();
84}
85
86std::string FileSystem::calculateAdler32(const std::string& filename)
87{
88 string chksum;
89 if (not isFile(filename)) return "";
90 fs::path fullPath = fs::absolute(filename);
91 FILE* fp = fopen(fullPath.c_str(), "rb");
92 if (fp) {
93 uLong i, sum = adler32(0, 0, 0);
94 char hexdigest[9];
95 const size_t bufsize = 1024 * 1024 * sizeof(Bytef);
96 Bytef* buf = (Bytef*) malloc(bufsize);
97 if (!buf) {
98 fclose(fp);
99 return "";
100 }
101 while (true) {
102 i = fread((void*) buf, 1, bufsize, fp);
103 if (ferror(fp)) {
104 free(buf);
105 fclose(fp);
106 return "";
107 }
108 if (i > 0) sum = adler32(sum, buf, i);
109 if (feof(fp)) break;
110 }
111 fclose(fp);
112 free(buf);
113 // Adler32 checksums hex digests ARE zero padded although
114 // HLT legacy presentation may differ.
115 sprintf(hexdigest, "%08lx", sum);
116 chksum = hexdigest;
117 } else {
118 chksum = "";
119 }
120 return chksum;
121}
122
123std::string FileSystem::findFile(const string& path, const std::vector<std::string>& dirs, bool silent)
124{
125 // check given directories
126 string fullpath;
127 for (auto dir : dirs) {
128 if (dir.empty())
129 continue;
130 fs::path dir_path = dir;
131 if (fs::path(path).is_absolute())
132 dir_path += path;
133 else
134 dir_path /= path;
135 fullpath = dir_path.string();
136 if (fileExists(fullpath)) {
137 if (isSymLink(fullpath) or isSymLink(dir))
138 return fullpath;
139 else
140 return fs::canonical(fullpath).string();
141 }
142 }
143
144 // check local directory
145 fullpath = fs::absolute(path).string();
146 if (fileExists(fullpath)) {
147 if (isSymLink(fullpath))
148 return fullpath;
149 else
150 return fs::canonical(fullpath).string();
151 }
152
153 // nothing found
154 if (!silent)
155 B2ERROR("findFile(): Could not find file." << LogVar("path", path));
156 return string("");
157}
158
159std::string FileSystem::findFile(const string& path, bool silent)
160{
161 std::vector<std::string> dirs;
162 if (getenv("BELLE2_LOCAL_DIR")) {
163 dirs.emplace_back(getenv("BELLE2_LOCAL_DIR"));
164 }
165 if (getenv("BELLE2_RELEASE_DIR")) {
166 dirs.emplace_back(getenv("BELLE2_RELEASE_DIR"));
167 }
168 return findFile(path, dirs, silent);
169}
170
171std::string FileSystem::findFile(const string& path, const std::string& dataType, bool silent)
172{
173 std::vector<std::string> dirs;
174 std::string envVar = "BELLE2_" + boost::to_upper_copy(dataType) + "_DATA_DIR";
175 if (getenv(envVar.c_str())) {
176 dirs.emplace_back(getenv(envVar.c_str()));
177 }
178 std::string result = findFile(path, dirs, true);
179 if (result.empty() && !silent)
180 B2ERROR("findFile(): Could not find data file. You may want to use the 'b2install-data' tool to get the file."
181 << LogVar("path", path) << LogVar("data type", dataType));
182 return result;
183}
184
185FileSystem::Lock::Lock(const std::string& fileName, bool readonly) :
186 m_readOnly(readonly)
187{
188 const int mode = readonly ? O_RDONLY : O_RDWR;
189 m_file = open(fileName.c_str(), mode | O_CREAT, 0640);
190}
191
193{
194 if (m_file >= 0) close(m_file);
195}
196
197bool FileSystem::Lock::lock(int timeout, bool ignoreErrors)
198{
199 if (m_file < 0) return false;
200
201 auto const maxtime = std::chrono::steady_clock::now() + std::chrono::seconds(timeout);
202 std::default_random_engine random;
203 std::uniform_int_distribution<int> uniform(1, 100);
204
205 /* Note:
206 * Previously, this used flock(), which doesn't work with GPFS.
207 * fcntl() does, and also should be more likely to work on NFS.
208 * If you use the 'nolock' mount option to NFS, you are on your own.
209 */
210 struct flock fl;
211 memset(&fl, '\0', sizeof(fl));
212 fl.l_type = m_readOnly ? F_RDLCK : F_WRLCK;
213 //lock entire file
214 fl.l_whence = SEEK_SET;
215 fl.l_start = 0;
216 fl.l_len = 0;
217
218 while (true) {
219 int lock = fcntl(m_file, F_SETLK, &fl);
220 if (lock == 0)
221 return true;
222 else if (std::chrono::steady_clock::now() > maxtime)
223 break;
224 if (errno != EAGAIN && errno != EACCES && errno != EINTR) break;
225 usleep(uniform(random) * 1000);
226 }
227 if (!ignoreErrors) B2ERROR("Locking failed: " << strerror(errno));
228 return false;
229}
230
231FileSystem::TemporaryFile::TemporaryFile(std::ios_base::openmode mode): std::fstream()
232{
233 char* temporaryFileName = strdup((std::filesystem::temp_directory_path() / "basf2_XXXXXX").c_str());
234 int fileDescriptor = mkstemp(temporaryFileName);
235 if (fileDescriptor == -1) {
236 B2ERROR("Cannot create temporary file: " << strerror(errno));
237 free(temporaryFileName);
238 return;
239 }
240 m_filename = std::string(temporaryFileName);
241 open(temporaryFileName, mode);
242 if (!is_open()) {
243 B2ERROR("Cannot open temporary file: " << strerror(errno));
244 }
245 free(temporaryFileName);
246 ::close(fileDescriptor);
247}
248
250{
251 close();
252 fs::remove(m_filename);
253}
bool m_readOnly
if this is a read-only lock (multiple processes can hold one).
Definition FileSystem.h:124
int m_file
File descriptor of file to be locked.
Definition FileSystem.h:123
Lock(const std::string &fileName, bool readonly=false)
Construct a Lock object for the given file.
bool lock(int timeout=300, bool ignoreErrors=false)
Try to lock the file.
~TemporaryFile()
close file and delete on destruction
std::string m_filename
filename of the temporary file
Definition FileSystem.h:144
TemporaryFile(std::ios_base::openmode mode=std::ios_base::trunc|std::ios_base::out)
construct a new temporary file
static bool loadLibrary(std::string library, bool fullname=true)
Load a shared library.
Definition FileSystem.cc:63
static bool isSymLink(const std::string &filename)
Check if filename points to an existing symbolic link.
Definition FileSystem.cc:57
static bool fileDirExists(const std::string &filename)
Check if the dir containing the filename exists.
Definition FileSystem.cc:38
static bool isFile(const std::string &filename)
Check if filename points to an existing file.
Definition FileSystem.cc:45
static std::string findFile(const std::string &path, bool silent=false)
Search for given file or directory in local or central release directory, and return absolute path if...
static std::string calculateMD5(const std::string &filename)
Calculate the MD5 checksum of a given file.
Definition FileSystem.cc:78
static std::string calculateAdler32(const std::string &filename)
Calculate the Adler-32 checksum of a given file.
Definition FileSystem.cc:86
static bool isDir(const std::string &filename)
Check if filename points to an existing directory.
Definition FileSystem.cc:51
static bool fileExists(const std::string &filename)
Check if the file with given filename exists.
Definition FileSystem.cc:32
Class to store variables with their name which were sent to the logging service.
Abstract base class for different kinds of events.
STL namespace.