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