Belle II Software  release-08-01-10
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 
28 using namespace std;
29 using namespace Belle2;
30 namespace fs = std::filesystem;
31 
32 bool FileSystem::fileExists(const string& filename)
33 {
34  fs::path fullPath = fs::absolute(filename);
35  return fs::exists(fullPath);
36 }
37 
38 bool FileSystem::fileDirExists(const string& filename)
39 {
40  fs::path fullPath = fs::absolute(filename);
41  fullPath.remove_filename();
42  return fs::exists(fullPath);
43 }
44 
45 bool 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 
51 bool FileSystem::isDir(const string& filename)
52 {
53  fs::path fullPath = fs::absolute(filename);
54  return (fs::exists(fullPath)) && (fs::is_directory(fullPath));
55 }
56 
57 bool FileSystem::isSymLink(const string& filename)
58 {
59  fs::path fullPath = fs::absolute(filename);
60  return (fs::exists(fullPath)) && (fs::is_symlink(fullPath));
61 }
62 
63 bool 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 
78 std::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 
86 std::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  Bytef* buf = (Bytef*) malloc(1024 * 1024 * sizeof(Bytef));
96  if (!buf) {
97  fclose(fp);
98  return "";
99  }
100  while ((i = fread((void*) buf, 1, sizeof(buf), fp)) > 0) {
101  sum = adler32(sum, buf, i);
102  }
103  fclose(fp);
104  free(buf);
105  // Adler32 checksums hex digests ARE zero padded although
106  // HLT legacy presentation may differ.
107  sprintf(hexdigest, "%08lx", sum);
108  chksum = hexdigest;
109  } else {
110  chksum = "";
111  }
112  return chksum;
113 }
114 
115 std::string FileSystem::findFile(const string& path, const std::vector<std::string>& dirs, bool silent)
116 {
117  // check given directories
118  string fullpath;
119  for (auto dir : dirs) {
120  if (dir.empty())
121  continue;
122  if (fs::path(path).is_absolute())
123  fullpath = (fs::path(dir) += path).string();
124  else fullpath = (fs::path(dir) / path).string();
125  if (fileExists(fullpath)) {
126  if (isSymLink(fullpath) or isSymLink(dir))
127  return fullpath;
128  else
129  return fs::canonical(fullpath).string();
130  }
131  }
132 
133  // check local directory
134  fullpath = fs::absolute(path).string();
135  if (fileExists(fullpath)) {
136  if (isSymLink(fullpath))
137  return fullpath;
138  else
139  return fs::canonical(fullpath).string();
140  }
141 
142  // nothing found
143  if (!silent)
144  B2ERROR("findFile(): Could not find file." << LogVar("path", path));
145  return string("");
146 }
147 
148 std::string FileSystem::findFile(const string& path, bool silent)
149 {
150  std::vector<std::string> dirs;
151  if (getenv("BELLE2_LOCAL_DIR")) {
152  dirs.emplace_back(getenv("BELLE2_LOCAL_DIR"));
153  }
154  if (getenv("BELLE2_RELEASE_DIR")) {
155  dirs.emplace_back(getenv("BELLE2_RELEASE_DIR"));
156  }
157  return findFile(path, dirs, silent);
158 }
159 
160 std::string FileSystem::findFile(const string& path, const std::string& dataType, bool silent)
161 {
162  std::vector<std::string> dirs;
163  std::string envVar = "BELLE2_" + boost::to_upper_copy(dataType) + "_DATA_DIR";
164  if (getenv(envVar.c_str())) {
165  dirs.emplace_back(getenv(envVar.c_str()));
166  }
167  std::string result = findFile(path, dirs, true);
168  if (result.empty() && !silent)
169  B2ERROR("findFile(): Could not find data file. You may want to use the 'b2install-data' tool to get the file."
170  << LogVar("path", path) << LogVar("data type", dataType));
171  return result;
172 }
173 
174 FileSystem::Lock::Lock(const std::string& fileName, bool readonly) :
175  m_readOnly(readonly)
176 {
177  const int mode = readonly ? O_RDONLY : O_RDWR;
178  m_file = open(fileName.c_str(), mode | O_CREAT, 0640);
179 }
180 
182 {
183  if (m_file >= 0) close(m_file);
184 }
185 
186 bool FileSystem::Lock::lock(int timeout, bool ignoreErrors)
187 {
188  if (m_file < 0) return false;
189 
190  auto const maxtime = std::chrono::steady_clock::now() + std::chrono::seconds(timeout);
191  std::default_random_engine random;
192  std::uniform_int_distribution<int> uniform(1, 100);
193 
194  /* Note:
195  * Previously, this used flock(), which doesn't work with GPFS.
196  * fcntl() does, and also should be more likely to work on NFS.
197  * If you use the 'nolock' mount option to NFS, you are on your own.
198  */
199  struct flock fl;
200  memset(&fl, '\0', sizeof(fl));
201  fl.l_type = m_readOnly ? F_RDLCK : F_WRLCK;
202  //lock entire file
203  fl.l_whence = SEEK_SET;
204  fl.l_start = 0;
205  fl.l_len = 0;
206 
207  while (true) {
208  int lock = fcntl(m_file, F_SETLK, &fl);
209  if (lock == 0)
210  return true;
211  else if (std::chrono::steady_clock::now() > maxtime)
212  break;
213  if (errno != EAGAIN && errno != EACCES && errno != EINTR) break;
214  usleep(uniform(random) * 1000);
215  }
216  if (!ignoreErrors) B2ERROR("Locking failed: " << strerror(errno));
217  return false;
218 }
219 
220 FileSystem::TemporaryFile::TemporaryFile(std::ios_base::openmode mode): std::fstream()
221 {
222  char* temporaryFileName = strdup((std::filesystem::temp_directory_path() / "basf2_XXXXXX").c_str());
223  int fileDescriptor = mkstemp(temporaryFileName);
224  if (fileDescriptor == -1) {
225  B2ERROR("Cannot create temporary file: " << strerror(errno));
226  free(temporaryFileName);
227  return;
228  }
229  m_filename = std::string(temporaryFileName);
230  open(temporaryFileName, mode);
231  if (!is_open()) {
232  B2ERROR("Cannot open temporary file: " << strerror(errno));
233  }
234  free(temporaryFileName);
235  ::close(fileDescriptor);
236 }
237 
239 {
240  close();
241  fs::remove(m_filename);
242 }
int m_file
File descriptor of file to be locked.
Definition: FileSystem.h:123
bool lock(int timeout=300, bool ignoreErrors=false)
Try to lock the file.
Definition: FileSystem.cc:186
~TemporaryFile()
close file and delete on destruction
Definition: FileSystem.cc:238
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
Definition: FileSystem.cc:220
Class to store variables with their name which were sent to the logging service.
Abstract base class for different kinds of events.