CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
LocalFileSystem.cc
Go to the documentation of this file.
1 #define _GNU_SOURCE 1
2 #define _FILE_OFFSET_BITS 64
5 #include <errno.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <sys/param.h>
11 #if BSD
12 # include <sys/statvfs.h>
13 # include <sys/ucred.h>
14 # include <sys/mount.h>
15 #else
16 # include <mntent.h>
17 # include <sys/vfs.h>
18 #endif
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <iostream>
22 #include <atomic>
23 
24 #pragma GCC diagnostic ignored "-Wformat" // shut warning on '%z'
25 
28 {
29  char *fsname; //< file system name
30  char *type; //< file system type
31  char *dir; //< mount point directory
32  char *origin = nullptr; //< mount origin
33  dev_t dev; //< device id
34  long fstype; //< file system id
35  double freespc; //< free space in megabytes
36  unsigned local : 1; //< flag for local device
37  unsigned bind : 1; //< flag for bind mounts
38  std::atomic<bool> checked ; //< flag for valid dev, fstype
39 };
40 
60 int
62 {
63  int ret = 0;
64 
65 #if __linux__
66  static const char procfs[] = "/proc/filesystems";
67  FILE *fs = fopen(procfs, "r");
68  if (! fs)
69  {
70  int nerr = errno;
71  edm::LogWarning("LocalFileSystem::readFSTypes()")
72  << "Cannot read '" << procfs << "': "
73  << strerror(nerr) << " (error " << nerr << ")";
74  return -1;
75  }
76 
77  ssize_t nread;
78  int line = 0;
79  while (! feof(fs))
80  {
81  char *type = 0;
82  char *fstype = 0;
83  size_t len = 0;
84  ++line;
85 
86  if ((nread = getdelim(&type, &len, '\t', fs)) == -1 && ! feof(fs))
87  {
88  fprintf(stderr, "%s:%d: %s (%zd; 1)\n",
89  procfs, line, strerror(errno), nread);
90  free(type);
91  ret = -1;
92  break;
93  }
94 
95  if ((nread = getdelim(&fstype, &len, '\n', fs)) == -1 && ! feof(fs))
96  {
97  fprintf(stderr, "%s:%d: %s (%zd; 2)\n",
98  procfs, line, strerror(errno), nread);
99  free(type);
100  free(fstype);
101  ret = -1;
102  break;
103  }
104 
105  if (feof (fs))
106  {
107  free(type);
108  free(fstype);
109  break;
110  }
111 
112  if (! strcmp(type, "nodev\t")
113  || ! strcmp(fstype, "lustre\n")
114  || ! strncmp(fstype, "fuse", 4))
115  {
116  free(type);
117  free(fstype);
118  continue;
119  }
120 
121  assert(nread >= 1);
122  fstype[nread-1] = 0;
123  fstypes_.push_back(fstype);
124  free(fstype);
125  free(type);
126  }
127 
128  fclose(fs);
129 #endif // __linux__
130 
131  return ret;
132 }
133 
144 {
145 #if BSD
146  struct statfs *m = static_cast<struct statfs *>(arg);
147  size_t infolen = sizeof(struct FSInfo);
148  size_t fslen = strlen(m->f_mntfromname) + 1;
149  size_t dirlen = strlen(m->f_mntonname) + 1;
150  size_t typelen = strlen(m->f_fstypename) + 1;
151  size_t totlen = infolen + fslen + dirlen + typelen;
152  FSInfo *i = (FSInfo *) malloc(totlen);
153  char *p = (char *) i;
154  i->fsname = strncpy(p += infolen, m->f_mntfromname, fslen);
155  i->type = strncpy(p += fslen, m->f_fstypename, typelen);
156  i->dir = strncpy(p += typelen, m->f_mntonname, dirlen);
157  i->dev = m->f_fsid.val[0];
158  i->fstype = m->f_type;
159  i->freespc = 0;
160  i->bind = 0;
161  i->origin = nullptr;
162  if (m->f_bsize > 0)
163  {
164  i->freespc = m->f_bavail;
165  i->freespc *= m->f_bsize;
166  i->freespc /= 1024. * 1024. * 1024.;
167  }
168  /* FIXME: This incorrectly says that mounted disk images are local,
169  even if it was mounted from a network server. The alternative is
170  to walk up the device tree using either a) process IORegistry to
171  get the device tree, which lists devices for disk images, and from
172  there translate volume uuid to a mount point; b) parse output from
173  'hdiutil info -plist' to determine image-path / dev-entry map. */
174  i->local = ((m->f_flags & MNT_LOCAL) ? 1 : 0);
175  i->checked = 1;
176  return i;
177 
178 #else // ! BSD
179  mntent *m = static_cast<mntent *>(arg);
180  size_t infolen = sizeof(struct FSInfo);
181  size_t fslen = strlen(m->mnt_fsname) + 1;
182  size_t dirlen = strlen(m->mnt_dir) + 1;
183  size_t typelen = strlen(m->mnt_type) + 1;
184  size_t originlen = strlen(m->mnt_fsname) + 1;
185  size_t totlen = infolen + fslen + dirlen + typelen + originlen;
186  FSInfo *i = (FSInfo *) malloc(totlen);
187  char *p = (char *) i;
188  i->fsname = strncpy(p += infolen, m->mnt_fsname, fslen);
189  i->type = strncpy(p += fslen, m->mnt_type, typelen);
190  i->dir = strncpy(p += typelen, m->mnt_dir, dirlen);
191  i->origin = strncpy(p += dirlen, m->mnt_fsname, originlen);
192  i->dev = -1;
193  i->fstype = -1;
194  i->freespc = 0;
195  i->local = 0;
196  i->checked = 0;
197  i->bind = strstr(m->mnt_opts, "bind") != nullptr;
198 
199  for (size_t j = 0; j < fstypes_.size() && ! i->local; ++j)
200  if (fstypes_[j] == i->type)
201  i->local = 1;
202 #endif // BSD
203 
204  return i;
205 }
206 
213 int
215 {
216 #if BSD
217  int rc;
218  struct statfs *mtab = 0;
219  if ((rc = getmntinfo(&mtab, MNT_NOWAIT)) < 0)
220  {
221  int nerr = errno;
222  edm::LogWarning("LocalFileSystem::initFSList()")
223  << "getmntinfo() failed: " << strerror(nerr)
224  << " (error " << nerr << ")";
225  return -1;
226  }
227 
228  fs_.reserve(rc);
229  for (int ix = 0; ix < rc; ++ix)
230  fs_.push_back(initFSInfo(&mtab[ix]));
231 
232  free(mtab);
233 #else
234  const char * const _PATH_MOUNTED_LINUX = "/proc/self/mounts";
235  struct mntent *m;
236  FILE *mtab = setmntent(_PATH_MOUNTED_LINUX, "r");
237  if (! mtab)
238  {
239  int nerr = errno;
240  edm::LogWarning("LocalFileSystem::initFSList()")
241  << "Cannot read '" << _PATH_MOUNTED_LINUX << "': "
242  << strerror(nerr) << " (error " << nerr << ")";
243  return -1;
244  }
245 
246  fs_.reserve(20);
247  while ((m = getmntent(mtab)))
248  fs_.push_back(initFSInfo(m));
249 
250  endmntent(mtab);
251 #endif
252 
253  return 0;
254 }
255 
264 int
266 {
267  int ret = 0;
268  struct stat s;
269  struct statfs sfs;
270 
271  if (! i->checked)
272  {
273  if (lstat(i->dir, &s) < 0)
274  {
275  i->checked = 1;
276 
277  int nerr = errno;
278  if (nerr != ENOENT && nerr != EACCES)
279  edm::LogWarning("LocalFileSystem::statFSInfo()")
280  << "Cannot lstat('" << i->dir << "'): "
281  << strerror(nerr) << " (error " << nerr << ")";
282  return -1;
283  }
284 
285  if (statfs(i->dir, &sfs) < 0)
286  {
287  i->checked = 1;
288  int nerr = errno;
289  edm::LogWarning("LocalFileSystem::statFSInfo()")
290  << "Cannot statfs('" << i->dir << "'): "
291  << strerror(nerr) << " (error " << nerr << ")";
292  return -1;
293  }
294 
295  i->dev = s.st_dev;
296  i->fstype = sfs.f_type;
297  if (sfs.f_bsize > 0)
298  {
299  i->freespc = sfs.f_bavail;
300  i->freespc *= sfs.f_bsize;
301  i->freespc /= 1024. * 1024. * 1024.;
302  }
303  i->checked = 1;
304  }
305  else if (i->fstype == -1)
306  {
307  errno = ENOENT;
308  ret = -1;
309  }
310 
311  return ret;
312 }
313 
328 LocalFileSystem::findMount(const char *path, struct statfs *sfs, struct stat *s, std::vector<std::string> &prev_paths) const
329 {
330  for (const auto & old_path : prev_paths)
331  {
332  if (!strcmp(old_path.c_str(), path))
333  {
334  edm::LogWarning("LocalFileSystem::findMount()")
335  << "Found a loop in bind mounts; stopping evaluation.";
336  return nullptr;
337  }
338  }
339 
340  FSInfo *best = 0;
341  size_t bestlen = 0;
342  size_t len = strlen(path);
343  for (size_t i = 0; i < fs_.size(); ++i)
344  {
345  // First match simply against the file system path. We don't
346  // touch the file system until the path prefix matches.
347  size_t fslen = strlen(fs_[i]->dir);
348  if (! strncmp(fs_[i]->dir, path, fslen)
349  && ((fslen == 1 && fs_[i]->dir[0] == '/')
350  || len == fslen || path[fslen] == '/')
351  && (! best || fslen > bestlen))
352  {
353  // Get the file system device and file system ids.
354  if (statFSInfo(fs_[i]) < 0)
355  return 0;
356 
357  // Check the path is on the same device / file system. If this
358  // fails, we found a better prefix match on path, but it's the
359  // wrong device, so reset our idea of the best match: it can't
360  // be the outer mount any more. Not sure this is the right
361  // thing to do with e.g. loop-back or union mounts.
362  if (fs_[i]->dev != s->st_dev || fs_[i]->fstype != sfs->f_type)
363  {
364  best = 0;
365  continue;
366  }
367 
368  // OK this is better than anything else we found so far.
369  best = fs_[i];
370  bestlen = fslen;
371  }
372  }
373  // In the case of a bind mount, try looking again at the source directory.
374  if (best && best->bind && best->origin)
375  {
376  struct stat s2;
377  struct statfs sfs2;
378  char *fullpath = realpath(best->origin, 0);
379 
380  if (! fullpath)
381  fullpath = strdup(best->origin);
382 
383  if (lstat(fullpath, &s2) < 0)
384  {
385  int nerr = errno;
386  edm::LogWarning("LocalFileSystem::findMount()")
387  << "Cannot lstat('" << fullpath << "' alias '"
388  << path << "'): " << strerror(nerr) << " (error "
389  << nerr << ")";
390  free(fullpath);
391  return best;
392  }
393 
394  if (statfs(fullpath, &sfs2) < 0)
395  {
396  int nerr = errno;
397  edm::LogWarning("LocalFileSystem::findMount()")
398  << "Cannot statfs('" << fullpath << "' alias '"
399  << path << "'): " << strerror(nerr) << " (error "
400  << nerr << ")";
401  free(fullpath);
402  return best;
403  }
404 
405  prev_paths.push_back(path);
406  LocalFileSystem::FSInfo *new_best = findMount(fullpath, &sfs2, &s2, prev_paths);
407  return new_best ? new_best : best;
408  }
409 
410  return best;
411 }
412 
422 bool
424 {
425  struct stat s;
426  struct statfs sfs;
427  char *fullpath = realpath(path.c_str(), 0);
428 
429  if (! fullpath)
430  fullpath = strdup(path.c_str());
431 
432  if (lstat(fullpath, &s) < 0)
433  {
434  int nerr = errno;
435  edm::LogWarning("LocalFileSystem::isLocalPath()")
436  << "Cannot lstat('" << fullpath << "' alias '"
437  << path << "'): " << strerror(nerr) << " (error "
438  << nerr << ")";
439  free(fullpath);
440  return false;
441  }
442 
443  if (statfs(fullpath, &sfs) < 0)
444  {
445  int nerr = errno;
446  edm::LogWarning("LocalFileSystem::isLocalPath()")
447  << "Cannot statfs('" << fullpath << "' alias '"
448  << path << "'): " << strerror(nerr) << " (error "
449  << nerr << ")";
450  free(fullpath);
451  return false;
452  }
453 
454  std::vector<std::string> prev_paths;
455  FSInfo *m = findMount(fullpath, &sfs, &s, prev_paths);
456  free(fullpath);
457 
458  return m ? m->local : false;
459 }
460 
479 std::pair<std::string, std::string>
480 LocalFileSystem::findCachePath(const std::vector<std::string> &paths,
481  double minFreeSpace) const
482 {
483  struct stat s;
484  struct statfs sfs;
485  std::ostringstream warningst;
486  warningst << "Cannot use lazy-download because:\n";
487 
488  for (size_t i = 0, e = paths.size(); i < e; ++i)
489  {
490  char *fullpath;
491  const char *inpath = paths[i].c_str();
492  const char *path = inpath;
493 
494  if (*path == '$')
495  {
496  char *p = getenv(path+1);
497  if (p && *p)
498  path = p;
499  else if (! strcmp(path, "$TMPDIR"))
500  path = "/tmp";
501  }
502 
503  if (! (fullpath = realpath(path, 0)))
504  fullpath = strdup(path);
505 
506 #if 0
507  std::cerr /* edm::LogInfo("LocalFileSystem") */
508  << "Checking if '" << fullpath << "', from '"
509  << inpath << "' is valid cache path with "
510  << minFreeSpace << " free space" << std::endl;
511 #endif
512 
513  if (lstat(fullpath, &s) < 0)
514  {
515  int nerr = errno;
516  if (nerr != ENOENT && nerr != EACCES)
517  edm::LogWarning("LocalFileSystem::findCachePath()")
518  << "Cannot lstat('" << fullpath << "', from '"
519  << inpath << "'): " << strerror(nerr) << " (error "
520  << nerr << ")";
521  free(fullpath);
522  continue;
523  }
524 
525  if (statfs(fullpath, &sfs) < 0)
526  {
527  int nerr = errno;
528  edm::LogWarning("LocalFileSystem::findCachePath()")
529  << "Cannot statfs('" << fullpath << "', from '"
530  << inpath << "'): " << strerror(nerr) << " (error "
531  << nerr << ")";
532  free(fullpath);
533  continue;
534  }
535 
536  std::vector<std::string> prev_paths;
537  FSInfo *m = findMount(fullpath, &sfs, &s, prev_paths);
538 #if 0
539  std::cerr /* edm::LogInfo("LocalFileSystem") */
540  << "Candidate '" << fullpath << "': "
541  << "found=" << (m ? 1 : 0)
542  << " local=" << (m && m->local)
543  << " free=" << (m ? m->freespc : 0)
544  << " access=" << access(fullpath, W_OK)
545  << std::endl;
546 #endif
547 
548  if (m
549  && m->local
550  && m->freespc >= minFreeSpace
551  && access(fullpath, W_OK) == 0)
552  {
553  std::string result(fullpath);
554  free(fullpath);
555  return std::make_pair(result,std::string());
556  }
557  else if (m)
558  {
559  if (!m->local)
560  {
561  warningst << "- The mount " << fullpath << " is not local.\n";
562  }
563  else if (m->freespc < minFreeSpace)
564  {
565  warningst << " - The mount at " << fullpath << " has only " << m->freespc << " GB free; a minumum of " << minFreeSpace << " GB is required.\n";
566  }
567  else if (access(fullpath, W_OK))
568  {
569  warningst << " - The process has no permission to write into " << fullpath << "\n";
570  }
571  }
572 
573  free(fullpath);
574  }
575 
576  std::string warning_str = warningst.str();
577  if (warning_str.size())
578  {
579  warning_str = warning_str.substr(0, warning_str.size()-2);
580  }
581 
582  return std::make_pair(std::string(), std::move(warning_str));
583 }
584 
587 {
588  if (readFSTypes() < 0)
589  return;
590 
591  if (initFSList() < 0)
592  return;
593 }
594 
597 {
598  for (size_t i = 0, e = fs_.size(); i < e; ++i)
599  free(fs_[i]);
600 }
int statFSInfo(FSInfo *i) const
type
Definition: HCALResponse.h:21
int i
Definition: DBlmapReader.cc:9
Information about file systems on this node.
assert(m_qm.get())
A arg
Definition: Factorize.h:36
int readFSTypes(void)
std::vector< std::string > fstypes_
tuple result
Definition: query.py:137
FSInfo * initFSInfo(void *p)
int j
Definition: DBlmapReader.cc:9
bool isLocalPath(const std::string &path) const
std::atomic< bool > checked
std::pair< std::string, std::string > findCachePath(const std::vector< std::string > &paths, double minFreeSpace) const
std::vector< FSInfo * > fs_
dbl *** dir
Definition: mlp_gen.cc:35
FSInfo * findMount(const char *path, struct statfs *sfs, struct stat *s, std::vector< std::string > &) const