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  dev_t dev; //< device id
32  long fstype; //< file system id
33  double freespc; //< free space in megabytes
34  unsigned local : 1; //< flag for local device
35  unsigned checked : 1; //< flag for valid dev, fstype
36 };
37 
57 int
59 {
60  int ret = 0;
61 
62 #if __linux__
63  static const char procfs[] = "/proc/filesystems";
64  FILE *fs = fopen(procfs, "r");
65  if (! fs)
66  {
67  int nerr = errno;
68  edm::LogWarning("LocalFileSystem::readFSTypes()")
69  << "Cannot read '" << procfs << "': "
70  << strerror(nerr) << " (error " << nerr << ")";
71  return -1;
72  }
73 
74  ssize_t nread;
75  int line = 0;
76  while (! feof(fs))
77  {
78  char *type = 0;
79  char *fstype = 0;
80  size_t len = 0;
81  ++line;
82 
83  if ((nread = getdelim(&type, &len, '\t', fs)) == -1 && ! feof(fs))
84  {
85  fprintf(stderr, "%s:%d: %s (%zd; 1)\n",
86  procfs, line, strerror(errno), nread);
87  free(type);
88  ret = -1;
89  break;
90  }
91 
92  if ((nread = getdelim(&fstype, &len, '\n', fs)) == -1 && ! feof(fs))
93  {
94  fprintf(stderr, "%s:%d: %s (%zd; 2)\n",
95  procfs, line, strerror(errno), nread);
96  free(type);
97  free(fstype);
98  ret = -1;
99  break;
100  }
101 
102  if (feof (fs))
103  {
104  free(type);
105  free(fstype);
106  break;
107  }
108 
109  if (! strcmp(type, "nodev\t")
110  || ! strcmp(fstype, "lustre\n")
111  || ! strncmp(fstype, "fuse", 4))
112  {
113  free(type);
114  free(fstype);
115  continue;
116  }
117 
118  assert(nread >= 1);
119  fstype[nread-1] = 0;
120  fstypes_.push_back(fstype);
121  free(fstype);
122  free(type);
123  }
124 
125  fclose(fs);
126 #endif // __linux__
127 
128  return ret;
129 }
130 
141 {
142 #if BSD
143  struct statfs *m = static_cast<struct statfs *>(arg);
144  size_t infolen = sizeof(struct FSInfo);
145  size_t fslen = strlen(m->f_mntfromname) + 1;
146  size_t dirlen = strlen(m->f_mntonname) + 1;
147  size_t typelen = strlen(m->f_fstypename) + 1;
148  size_t totlen = infolen + fslen + dirlen + typelen;
149  FSInfo *i = (FSInfo *) malloc(totlen);
150  char *p = (char *) i;
151  i->fsname = strncpy(p += infolen, m->f_mntfromname, fslen);
152  i->type = strncpy(p += fslen, m->f_fstypename, typelen);
153  i->dir = strncpy(p += typelen, m->f_mntonname, dirlen);
154  i->dev = m->f_fsid.val[0];
155  i->fstype = m->f_type;
156  i->freespc = 0;
157  if (m->f_bsize > 0)
158  {
159  i->freespc = m->f_bavail;
160  i->freespc *= m->f_bsize;
161  i->freespc /= 1024. * 1024. * 1024.;
162  }
163  /* FIXME: This incorrectly says that mounted disk images are local,
164  even if it was mounted from a network server. The alternative is
165  to walk up the device tree using either a) process IORegistry to
166  get the device tree, which lists devices for disk images, and from
167  there translate volume uuid to a mount point; b) parse output from
168  'hdiutil info -plist' to determine image-path / dev-entry map. */
169  i->local = ((m->f_flags & MNT_LOCAL) ? 1 : 0);
170  i->checked = 1;
171  return i;
172 
173 #else // ! BSD
174  mntent *m = static_cast<mntent *>(arg);
175  size_t infolen = sizeof(struct FSInfo);
176  size_t fslen = strlen(m->mnt_fsname) + 1;
177  size_t dirlen = strlen(m->mnt_dir) + 1;
178  size_t typelen = strlen(m->mnt_type) + 1;
179  size_t totlen = infolen + fslen + dirlen + typelen;
180  FSInfo *i = (FSInfo *) malloc(totlen);
181  char *p = (char *) i;
182  i->fsname = strncpy(p += infolen, m->mnt_fsname, fslen);
183  i->type = strncpy(p += fslen, m->mnt_type, typelen);
184  i->dir = strncpy(p += typelen, m->mnt_dir, dirlen);
185  i->dev = -1;
186  i->fstype = -1;
187  i->freespc = 0;
188  i->local = 0;
189  i->checked = 0;
190 
191  for (size_t j = 0; j < fstypes_.size() && ! i->local; ++j)
192  if (fstypes_[j] == i->type)
193  i->local = 1;
194 #endif // BSD
195 
196  return i;
197 }
198 
205 int
207 {
208 #if BSD
209  int rc;
210  struct statfs *mtab = 0;
211  if ((rc = getmntinfo(&mtab, MNT_NOWAIT)) < 0)
212  {
213  int nerr = errno;
214  edm::LogWarning("LocalFileSystem::initFSList()")
215  << "getmntinfo() failed: " << strerror(nerr)
216  << " (error " << nerr << ")";
217  return -1;
218  }
219 
220  fs_.reserve(rc);
221  for (int ix = 0; ix < rc; ++ix)
222  fs_.push_back(initFSInfo(&mtab[ix]));
223 
224  free(mtab);
225 #else
226  struct mntent *m;
227  FILE *mtab = setmntent(_PATH_MOUNTED, "r");
228  if (! mtab)
229  {
230  int nerr = errno;
231  edm::LogWarning("LocalFileSystem::initFSList()")
232  << "Cannot read '" << _PATH_MOUNTED << "': "
233  << strerror(nerr) << " (error " << nerr << ")";
234  return -1;
235  }
236 
237  fs_.reserve(20);
238  while ((m = getmntent(mtab)))
239  fs_.push_back(initFSInfo(m));
240 
241  endmntent(mtab);
242 #endif
243 
244  return 0;
245 }
246 
255 int
257 {
258  int ret = 0;
259  struct stat s;
260  struct statfs sfs;
261 
262  if (! i->checked)
263  {
264  i->checked = 1;
265  if (lstat(i->dir, &s) < 0)
266  {
267  int nerr = errno;
268  if (nerr != ENOENT && nerr != EACCES)
269  edm::LogWarning("LocalFileSystem::statFSInfo()")
270  << "Cannot lstat('" << i->dir << "'): "
271  << strerror(nerr) << " (error " << nerr << ")";
272  return -1;
273  }
274 
275  if (statfs(i->dir, &sfs) < 0)
276  {
277  int nerr = errno;
278  edm::LogWarning("LocalFileSystem::statFSInfo()")
279  << "Cannot statfs('" << i->dir << "'): "
280  << strerror(nerr) << " (error " << nerr << ")";
281  return -1;
282  }
283 
284  i->dev = s.st_dev;
285  i->fstype = sfs.f_type;
286  if (sfs.f_bsize > 0)
287  {
288  i->freespc = sfs.f_bavail;
289  i->freespc *= sfs.f_bsize;
290  i->freespc /= 1024. * 1024. * 1024.;
291  }
292  }
293  else if (i->fstype == -1)
294  {
295  errno = ENOENT;
296  ret = -1;
297  }
298 
299  return ret;
300 }
301 
316 LocalFileSystem::findMount(const char *path, struct statfs *sfs, struct stat *s)
317 {
318  FSInfo *best = 0;
319  size_t bestlen = 0;
320  size_t len = strlen(path);
321  for (size_t i = 0; i < fs_.size(); ++i)
322  {
323  // First match simply against the file system path. We don't
324  // touch the file system until the path prefix matches.
325  size_t fslen = strlen(fs_[i]->dir);
326  if (! strncmp(fs_[i]->dir, path, fslen)
327  && ((fslen == 1 && fs_[i]->dir[0] == '/')
328  || len == fslen || path[fslen] == '/')
329  && (! best || fslen > bestlen))
330  {
331  // Get the file system device and file system ids.
332  if (statFSInfo(fs_[i]) < 0)
333  return 0;
334 
335  // Check the path is on the same device / file system. If this
336  // fails, we found a better prefix match on path, but it's the
337  // wrong device, so reset our idea of the best match: it can't
338  // be the outer mount any more. Not sure this is the right
339  // thing to do with e.g. loop-back or union mounts.
340  if (fs_[i]->dev != s->st_dev || fs_[i]->fstype != sfs->f_type)
341  {
342  best = 0;
343  continue;
344  }
345 
346  // OK this is better than anything else we found so far.
347  best = fs_[i];
348  bestlen = fslen;
349  }
350  }
351 
352  return best;
353 }
354 
364 bool
366 {
367  struct stat s;
368  struct statfs sfs;
369  char *fullpath = realpath(path.c_str(), 0);
370 
371  if (! fullpath)
372  fullpath = strdup(path.c_str());
373 
374  if (lstat(fullpath, &s) < 0)
375  {
376  int nerr = errno;
377  edm::LogWarning("LocalFileSystem::isLocalPath()")
378  << "Cannot lstat('" << fullpath << "' alias '"
379  << path << "'): " << strerror(nerr) << " (error "
380  << nerr << ")";
381  free(fullpath);
382  return false;
383  }
384 
385  if (statfs(fullpath, &sfs) < 0)
386  {
387  int nerr = errno;
388  edm::LogWarning("LocalFileSystem::isLocalPath()")
389  << "Cannot statfs('" << fullpath << "' alias '"
390  << path << "'): " << strerror(nerr) << " (error "
391  << nerr << ")";
392  free(fullpath);
393  return false;
394  }
395 
396  FSInfo *m = findMount(fullpath, &sfs, &s);
397  free(fullpath);
398 
399  return m ? m->local : false;
400 }
401 
420 std::string
421 LocalFileSystem::findCachePath(const std::vector<std::string> &paths,
422  double minFreeSpace)
423 {
424  struct stat s;
425  struct statfs sfs;
426  for (size_t i = 0, e = paths.size(); i < e; ++i)
427  {
428  char *fullpath;
429  const char *inpath = paths[i].c_str();
430  const char *path = inpath;
431 
432  if (*path == '$')
433  {
434  char *p = getenv(path+1);
435  if (p && *p)
436  path = p;
437  else if (! strcmp(path, "$TMPDIR"))
438  path = "/tmp";
439  }
440 
441  if (! (fullpath = realpath(path, 0)))
442  fullpath = strdup(path);
443 
444 #if 0
445  std::cerr /* edm::LogInfo("LocalFileSystem") */
446  << "Checking if '" << fullpath << "', from '"
447  << inpath << "' is valid cache path with "
448  << minFreeSpace << " free space" << std::endl;
449 #endif
450 
451  if (lstat(fullpath, &s) < 0)
452  {
453  int nerr = errno;
454  if (nerr != ENOENT && nerr != EACCES)
455  edm::LogWarning("LocalFileSystem::findCachePath()")
456  << "Cannot lstat('" << fullpath << "', from '"
457  << inpath << "'): " << strerror(nerr) << " (error "
458  << nerr << ")";
459  free(fullpath);
460  continue;
461  }
462 
463  if (statfs(fullpath, &sfs) < 0)
464  {
465  int nerr = errno;
466  edm::LogWarning("LocalFileSystem::findCachePath()")
467  << "Cannot statfs('" << fullpath << "', from '"
468  << inpath << "'): " << strerror(nerr) << " (error "
469  << nerr << ")";
470  free(fullpath);
471  continue;
472  }
473 
474  FSInfo *m = findMount(fullpath, &sfs, &s);
475 #if 0
476  std::cerr /* edm::LogInfo("LocalFileSystem") */
477  << "Candidate '" << fullpath << "': "
478  << "found=" << (m ? 1 : 0)
479  << " local=" << (m && m->local)
480  << " free=" << (m ? m->freespc : 0)
481  << " access=" << access(fullpath, W_OK)
482  << std::endl;
483 #endif
484 
485  if (m
486  && m->local
487  && m->freespc >= minFreeSpace
488  && access(fullpath, W_OK) == 0)
489  {
490  std::string result(fullpath);
491  free(fullpath);
492  return result;
493  }
494 
495  free(fullpath);
496  }
497 
498  return std::string();
499 }
500 
503 {
504  if (readFSTypes() < 0)
505  return;
506 
507  if (initFSList() < 0)
508  return;
509 }
510 
513 {
514  for (size_t i = 0, e = fs_.size(); i < e; ++i)
515  free(fs_[i]);
516 }
type
Definition: HCALResponse.h:22
int i
Definition: DBlmapReader.cc:9
int statFSInfo(FSInfo *i)
Information about file systems on this node.
FSInfo * findMount(const char *path, struct statfs *sfs, struct stat *s)
std::string findCachePath(const std::vector< std::string > &paths, double minFreeSpace)
A arg
Definition: Factorize.h:36
bool isLocalPath(const std::string &path)
list path
Definition: scaleCards.py:51
int readFSTypes(void)
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