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  const char * const _PATH_MOUNTED_LINUX = "/proc/self/mounts";
227  struct mntent *m;
228  FILE *mtab = setmntent(_PATH_MOUNTED_LINUX, "r");
229  if (! mtab)
230  {
231  int nerr = errno;
232  edm::LogWarning("LocalFileSystem::initFSList()")
233  << "Cannot read '" << _PATH_MOUNTED_LINUX << "': "
234  << strerror(nerr) << " (error " << nerr << ")";
235  return -1;
236  }
237 
238  fs_.reserve(20);
239  while ((m = getmntent(mtab)))
240  fs_.push_back(initFSInfo(m));
241 
242  endmntent(mtab);
243 #endif
244 
245  return 0;
246 }
247 
256 int
258 {
259  int ret = 0;
260  struct stat s;
261  struct statfs sfs;
262 
263  if (! i->checked)
264  {
265  i->checked = 1;
266  if (lstat(i->dir, &s) < 0)
267  {
268  int nerr = errno;
269  if (nerr != ENOENT && nerr != EACCES)
270  edm::LogWarning("LocalFileSystem::statFSInfo()")
271  << "Cannot lstat('" << i->dir << "'): "
272  << strerror(nerr) << " (error " << nerr << ")";
273  return -1;
274  }
275 
276  if (statfs(i->dir, &sfs) < 0)
277  {
278  int nerr = errno;
279  edm::LogWarning("LocalFileSystem::statFSInfo()")
280  << "Cannot statfs('" << i->dir << "'): "
281  << strerror(nerr) << " (error " << nerr << ")";
282  return -1;
283  }
284 
285  i->dev = s.st_dev;
286  i->fstype = sfs.f_type;
287  if (sfs.f_bsize > 0)
288  {
289  i->freespc = sfs.f_bavail;
290  i->freespc *= sfs.f_bsize;
291  i->freespc /= 1024. * 1024. * 1024.;
292  }
293  }
294  else if (i->fstype == -1)
295  {
296  errno = ENOENT;
297  ret = -1;
298  }
299 
300  return ret;
301 }
302 
317 LocalFileSystem::findMount(const char *path, struct statfs *sfs, struct stat *s)
318 {
319  FSInfo *best = 0;
320  size_t bestlen = 0;
321  size_t len = strlen(path);
322  for (size_t i = 0; i < fs_.size(); ++i)
323  {
324  // First match simply against the file system path. We don't
325  // touch the file system until the path prefix matches.
326  size_t fslen = strlen(fs_[i]->dir);
327  if (! strncmp(fs_[i]->dir, path, fslen)
328  && ((fslen == 1 && fs_[i]->dir[0] == '/')
329  || len == fslen || path[fslen] == '/')
330  && (! best || fslen > bestlen))
331  {
332  // Get the file system device and file system ids.
333  if (statFSInfo(fs_[i]) < 0)
334  return 0;
335 
336  // Check the path is on the same device / file system. If this
337  // fails, we found a better prefix match on path, but it's the
338  // wrong device, so reset our idea of the best match: it can't
339  // be the outer mount any more. Not sure this is the right
340  // thing to do with e.g. loop-back or union mounts.
341  if (fs_[i]->dev != s->st_dev || fs_[i]->fstype != sfs->f_type)
342  {
343  best = 0;
344  continue;
345  }
346 
347  // OK this is better than anything else we found so far.
348  best = fs_[i];
349  bestlen = fslen;
350  }
351  }
352 
353  return best;
354 }
355 
365 bool
367 {
368  struct stat s;
369  struct statfs sfs;
370  char *fullpath = realpath(path.c_str(), 0);
371 
372  if (! fullpath)
373  fullpath = strdup(path.c_str());
374 
375  if (lstat(fullpath, &s) < 0)
376  {
377  int nerr = errno;
378  edm::LogWarning("LocalFileSystem::isLocalPath()")
379  << "Cannot lstat('" << fullpath << "' alias '"
380  << path << "'): " << strerror(nerr) << " (error "
381  << nerr << ")";
382  free(fullpath);
383  return false;
384  }
385 
386  if (statfs(fullpath, &sfs) < 0)
387  {
388  int nerr = errno;
389  edm::LogWarning("LocalFileSystem::isLocalPath()")
390  << "Cannot statfs('" << fullpath << "' alias '"
391  << path << "'): " << strerror(nerr) << " (error "
392  << nerr << ")";
393  free(fullpath);
394  return false;
395  }
396 
397  FSInfo *m = findMount(fullpath, &sfs, &s);
398  free(fullpath);
399 
400  return m ? m->local : false;
401 }
402 
422 LocalFileSystem::findCachePath(const std::vector<std::string> &paths,
423  double minFreeSpace)
424 {
425  struct stat s;
426  struct statfs sfs;
427  for (size_t i = 0, e = paths.size(); i < e; ++i)
428  {
429  char *fullpath;
430  const char *inpath = paths[i].c_str();
431  const char *path = inpath;
432 
433  if (*path == '$')
434  {
435  char *p = getenv(path+1);
436  if (p && *p)
437  path = p;
438  else if (! strcmp(path, "$TMPDIR"))
439  path = "/tmp";
440  }
441 
442  if (! (fullpath = realpath(path, 0)))
443  fullpath = strdup(path);
444 
445 #if 0
446  std::cerr /* edm::LogInfo("LocalFileSystem") */
447  << "Checking if '" << fullpath << "', from '"
448  << inpath << "' is valid cache path with "
449  << minFreeSpace << " free space" << std::endl;
450 #endif
451 
452  if (lstat(fullpath, &s) < 0)
453  {
454  int nerr = errno;
455  if (nerr != ENOENT && nerr != EACCES)
456  edm::LogWarning("LocalFileSystem::findCachePath()")
457  << "Cannot lstat('" << fullpath << "', from '"
458  << inpath << "'): " << strerror(nerr) << " (error "
459  << nerr << ")";
460  free(fullpath);
461  continue;
462  }
463 
464  if (statfs(fullpath, &sfs) < 0)
465  {
466  int nerr = errno;
467  edm::LogWarning("LocalFileSystem::findCachePath()")
468  << "Cannot statfs('" << fullpath << "', from '"
469  << inpath << "'): " << strerror(nerr) << " (error "
470  << nerr << ")";
471  free(fullpath);
472  continue;
473  }
474 
475  FSInfo *m = findMount(fullpath, &sfs, &s);
476 #if 0
477  std::cerr /* edm::LogInfo("LocalFileSystem") */
478  << "Candidate '" << fullpath << "': "
479  << "found=" << (m ? 1 : 0)
480  << " local=" << (m && m->local)
481  << " free=" << (m ? m->freespc : 0)
482  << " access=" << access(fullpath, W_OK)
483  << std::endl;
484 #endif
485 
486  if (m
487  && m->local
488  && m->freespc >= minFreeSpace
489  && access(fullpath, W_OK) == 0)
490  {
491  std::string result(fullpath);
492  free(fullpath);
493  return result;
494  }
495 
496  free(fullpath);
497  }
498 
499  return std::string();
500 }
501 
504 {
505  if (readFSTypes() < 0)
506  return;
507 
508  if (initFSList() < 0)
509  return;
510 }
511 
514 {
515  for (size_t i = 0, e = fs_.size(); i < e; ++i)
516  free(fs_[i]);
517 }
type
Definition: HCALResponse.h:21
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)
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