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