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