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  struct mntent *m;
234  FILE *mtab = setmntent(_PATH_MOUNTED, "r");
235  if (! mtab)
236  {
237  int nerr = errno;
238  edm::LogWarning("LocalFileSystem::initFSList()")
239  << "Cannot read '" << _PATH_MOUNTED << "': "
240  << strerror(nerr) << " (error " << nerr << ")";
241  return -1;
242  }
243 
244  fs_.reserve(20);
245  while ((m = getmntent(mtab)))
246  fs_.push_back(initFSInfo(m));
247 
248  endmntent(mtab);
249 #endif
250 
251  return 0;
252 }
253 
262 int
264 {
265  int ret = 0;
266  struct stat s;
267  struct statfs sfs;
268 
269  if (! i->checked)
270  {
271  i->checked = 1;
272  if (lstat(i->dir, &s) < 0)
273  {
274  int nerr = errno;
275  if (nerr != ENOENT && nerr != EACCES)
276  edm::LogWarning("LocalFileSystem::statFSInfo()")
277  << "Cannot lstat('" << i->dir << "'): "
278  << strerror(nerr) << " (error " << nerr << ")";
279  return -1;
280  }
281 
282  if (statfs(i->dir, &sfs) < 0)
283  {
284  int nerr = errno;
285  edm::LogWarning("LocalFileSystem::statFSInfo()")
286  << "Cannot statfs('" << i->dir << "'): "
287  << strerror(nerr) << " (error " << nerr << ")";
288  return -1;
289  }
290 
291  i->dev = s.st_dev;
292  i->fstype = sfs.f_type;
293  if (sfs.f_bsize > 0)
294  {
295  i->freespc = sfs.f_bavail;
296  i->freespc *= sfs.f_bsize;
297  i->freespc /= 1024. * 1024. * 1024.;
298  }
299  }
300  else if (i->fstype == -1)
301  {
302  errno = ENOENT;
303  ret = -1;
304  }
305 
306  return ret;
307 }
308 
323 LocalFileSystem::findMount(const char *path, struct statfs *sfs, struct stat *s, std::vector<std::string> &prev_paths)
324 {
325  for (const auto & old_path : prev_paths)
326  {
327  if (!strcmp(old_path.c_str(), path))
328  {
329  edm::LogWarning("LocalFileSystem::findMount()")
330  << "Found a loop in bind mounts; stopping evaluation.";
331  return nullptr;
332  }
333  }
334 
335  FSInfo *best = 0;
336  size_t bestlen = 0;
337  size_t len = strlen(path);
338  for (size_t i = 0; i < fs_.size(); ++i)
339  {
340  // First match simply against the file system path. We don't
341  // touch the file system until the path prefix matches.
342  size_t fslen = strlen(fs_[i]->dir);
343  if (! strncmp(fs_[i]->dir, path, fslen)
344  && ((fslen == 1 && fs_[i]->dir[0] == '/')
345  || len == fslen || path[fslen] == '/')
346  && (! best || fslen > bestlen))
347  {
348  // Get the file system device and file system ids.
349  if (statFSInfo(fs_[i]) < 0)
350  return 0;
351 
352  // Check the path is on the same device / file system. If this
353  // fails, we found a better prefix match on path, but it's the
354  // wrong device, so reset our idea of the best match: it can't
355  // be the outer mount any more. Not sure this is the right
356  // thing to do with e.g. loop-back or union mounts.
357  if (fs_[i]->dev != s->st_dev || fs_[i]->fstype != sfs->f_type)
358  {
359  best = 0;
360  continue;
361  }
362 
363  // OK this is better than anything else we found so far.
364  best = fs_[i];
365  bestlen = fslen;
366  }
367  }
368  // In the case of a bind mount, try looking again at the source directory.
369  if (best->bind && best->origin)
370  {
371  struct stat s2;
372  struct statfs sfs2;
373  char *fullpath = realpath(best->origin, 0);
374 
375  if (! fullpath)
376  fullpath = strdup(best->origin);
377 
378  if (lstat(fullpath, &s2) < 0)
379  {
380  int nerr = errno;
381  edm::LogWarning("LocalFileSystem::findMount()")
382  << "Cannot lstat('" << fullpath << "' alias '"
383  << path << "'): " << strerror(nerr) << " (error "
384  << nerr << ")";
385  free(fullpath);
386  return best;
387  }
388 
389  if (statfs(fullpath, &sfs2) < 0)
390  {
391  int nerr = errno;
392  edm::LogWarning("LocalFileSystem::findMount()")
393  << "Cannot statfs('" << fullpath << "' alias '"
394  << path << "'): " << strerror(nerr) << " (error "
395  << nerr << ")";
396  free(fullpath);
397  return best;
398  }
399 
400  prev_paths.push_back(path);
401  LocalFileSystem::FSInfo *new_best = findMount(fullpath, &sfs2, &s2, prev_paths);
402  return new_best ? new_best : best;
403  }
404 
405  return best;
406 }
407 
417 bool
419 {
420  struct stat s;
421  struct statfs sfs;
422  char *fullpath = realpath(path.c_str(), 0);
423 
424  if (! fullpath)
425  fullpath = strdup(path.c_str());
426 
427  if (lstat(fullpath, &s) < 0)
428  {
429  int nerr = errno;
430  edm::LogWarning("LocalFileSystem::isLocalPath()")
431  << "Cannot lstat('" << fullpath << "' alias '"
432  << path << "'): " << strerror(nerr) << " (error "
433  << nerr << ")";
434  free(fullpath);
435  return false;
436  }
437 
438  if (statfs(fullpath, &sfs) < 0)
439  {
440  int nerr = errno;
441  edm::LogWarning("LocalFileSystem::isLocalPath()")
442  << "Cannot statfs('" << fullpath << "' alias '"
443  << path << "'): " << strerror(nerr) << " (error "
444  << nerr << ")";
445  free(fullpath);
446  return false;
447  }
448 
449  std::vector<std::string> prev_paths;
450  FSInfo *m = findMount(fullpath, &sfs, &s, prev_paths);
451  free(fullpath);
452 
453  return m ? m->local : false;
454 }
455 
475 LocalFileSystem::findCachePath(const std::vector<std::string> &paths,
476  double minFreeSpace)
477 {
478  struct stat s;
479  struct statfs sfs;
480  std::ostringstream warningst;
481  warningst << "Cannot use lazy-download because:\n";
482 
483  for (size_t i = 0, e = paths.size(); i < e; ++i)
484  {
485  char *fullpath;
486  const char *inpath = paths[i].c_str();
487  const char *path = inpath;
488 
489  if (*path == '$')
490  {
491  char *p = getenv(path+1);
492  if (p && *p)
493  path = p;
494  else if (! strcmp(path, "$TMPDIR"))
495  path = "/tmp";
496  }
497 
498  if (! (fullpath = realpath(path, 0)))
499  fullpath = strdup(path);
500 
501 #if 0
502  std::cerr /* edm::LogInfo("LocalFileSystem") */
503  << "Checking if '" << fullpath << "', from '"
504  << inpath << "' is valid cache path with "
505  << minFreeSpace << " free space" << std::endl;
506 #endif
507 
508  if (lstat(fullpath, &s) < 0)
509  {
510  int nerr = errno;
511  if (nerr != ENOENT && nerr != EACCES)
512  edm::LogWarning("LocalFileSystem::findCachePath()")
513  << "Cannot lstat('" << fullpath << "', from '"
514  << inpath << "'): " << strerror(nerr) << " (error "
515  << nerr << ")";
516  free(fullpath);
517  continue;
518  }
519 
520  if (statfs(fullpath, &sfs) < 0)
521  {
522  int nerr = errno;
523  edm::LogWarning("LocalFileSystem::findCachePath()")
524  << "Cannot statfs('" << fullpath << "', from '"
525  << inpath << "'): " << strerror(nerr) << " (error "
526  << nerr << ")";
527  free(fullpath);
528  continue;
529  }
530 
531  std::vector<std::string> prev_paths;
532  FSInfo *m = findMount(fullpath, &sfs, &s, prev_paths);
533 #if 0
534  std::cerr /* edm::LogInfo("LocalFileSystem") */
535  << "Candidate '" << fullpath << "': "
536  << "found=" << (m ? 1 : 0)
537  << " local=" << (m && m->local)
538  << " free=" << (m ? m->freespc : 0)
539  << " access=" << access(fullpath, W_OK)
540  << std::endl;
541 #endif
542 
543  if (m
544  && m->local
545  && m->freespc >= minFreeSpace
546  && access(fullpath, W_OK) == 0)
547  {
548  std::string result(fullpath);
549  free(fullpath);
550  return result;
551  }
552  else if (m)
553  {
554  if (!m->local)
555  {
556  warningst << "- The mount " << fullpath << " is not local.\n";
557  }
558  else if (m->freespc < minFreeSpace)
559  {
560  warningst << " - The mount at " << fullpath << " has only " << m->freespc << " GB free; a minumum of " << minFreeSpace << " GB is required.\n";
561  }
562  else if (access(fullpath, W_OK))
563  {
564  warningst << " - The process has no permission to write into " << fullpath << "\n";
565  }
566  }
567 
568  free(fullpath);
569  }
570 
571  std::string warning_str = warningst.str();
572  if (warning_str.size())
573  {
574  warning_str = warning_str.substr(0, warning_str.size()-2);
575  }
576  unusable_dir_warnings_ = std::move(warning_str);
577 
578  return std::string();
579 }
580 
581 void
583 {
584  if (unusable_dir_warnings_.size())
585  {
586  edm::LogWarning("LocalFileSystem::findCachePath()") << unusable_dir_warnings_;
587  }
588 }
589 
592 {
593  if (readFSTypes() < 0)
594  return;
595 
596  if (initFSList() < 0)
597  return;
598 }
599 
602 {
603  for (size_t i = 0, e = fs_.size(); i < e; ++i)
604  free(fs_[i]);
605 }
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
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 > &)