CMS 3D CMS Logo

/afs/cern.ch/work/a/aaltunda/public/www/CMSSW_5_3_14/src/Utilities/StorageFactory/src/LocalFileSystem.cc

Go to the documentation of this file.
00001 #define _GNU_SOURCE 1
00002 #define _FILE_OFFSET_BITS 64
00003 #include "Utilities/StorageFactory/interface/LocalFileSystem.h"
00004 #include "FWCore/MessageLogger/interface/MessageLogger.h"
00005 #include <errno.h>
00006 #include <stdio.h>
00007 #include <stdlib.h>
00008 #include <string.h>
00009 #include <assert.h>
00010 #include <sys/param.h>
00011 #if BSD
00012 # include <sys/statvfs.h>
00013 # include <sys/ucred.h>
00014 # include <sys/mount.h>
00015 #else
00016 # include <mntent.h>
00017 # include <sys/vfs.h>
00018 #endif
00019 #include <sys/stat.h>
00020 #include <unistd.h>
00021 #include <iostream>
00022 
00023 #pragma GCC diagnostic ignored "-Wformat" // shut warning on '%z'
00024 
00026 struct LocalFileSystem::FSInfo
00027 {
00028   char          *fsname;        //< file system name
00029   char          *type;          //< file system type
00030   char          *dir;           //< mount point directory
00031   dev_t         dev;            //< device id
00032   long          fstype;         //< file system id
00033   double        freespc;        //< free space in megabytes
00034   unsigned      local : 1;      //< flag for local device
00035   unsigned      checked : 1;    //< flag for valid dev, fstype
00036 };
00037 
00057 int
00058 LocalFileSystem::readFSTypes(void)
00059 {
00060   int ret = 0;
00061 
00062 #if __linux__
00063   static const char procfs[] = "/proc/filesystems";
00064   FILE *fs = fopen(procfs, "r");
00065   if (! fs)
00066   {
00067     int nerr = errno;
00068     edm::LogWarning("LocalFileSystem::readFSTypes()")
00069       << "Cannot read '" << procfs << "': "
00070       << strerror(nerr) << " (error " << nerr << ")";
00071     return -1;
00072   }
00073 
00074   ssize_t nread;
00075   int line = 0;
00076   while (! feof(fs))
00077   {
00078     char *type = 0;
00079     char *fstype = 0;
00080     size_t len = 0;
00081     ++line;
00082 
00083     if ((nread = getdelim(&type, &len, '\t', fs)) == -1 && ! feof(fs))
00084     {
00085       fprintf(stderr, "%s:%d: %s (%zd; 1)\n",
00086               procfs, line, strerror(errno), nread);
00087       free(type);
00088       ret = -1;
00089       break;
00090     }
00091 
00092     if ((nread = getdelim(&fstype, &len, '\n', fs)) == -1 && ! feof(fs))
00093     {
00094       fprintf(stderr, "%s:%d: %s (%zd; 2)\n",
00095               procfs, line, strerror(errno), nread);
00096       free(type);
00097       free(fstype);
00098       ret = -1;
00099       break;
00100     }
00101 
00102     if (feof (fs))
00103     {
00104       free(type);
00105       free(fstype);
00106       break;
00107     }
00108     
00109     if (! strcmp(type, "nodev\t")
00110         || ! strcmp(fstype, "lustre\n")
00111         || ! strncmp(fstype, "fuse", 4))
00112     {
00113       free(type);
00114       free(fstype);
00115       continue;
00116     }
00117 
00118     assert(nread >= 1);
00119     fstype[nread-1] = 0;
00120     fstypes_.push_back(fstype);
00121     free(fstype);
00122     free(type);
00123   }
00124 
00125   fclose(fs);
00126 #endif // __linux__
00127 
00128   return ret;
00129 }
00130 
00139 LocalFileSystem::FSInfo *
00140 LocalFileSystem::initFSInfo(void *arg)
00141 {
00142 #if BSD
00143   struct statfs *m = static_cast<struct statfs *>(arg);
00144   size_t infolen = sizeof(struct FSInfo);
00145   size_t fslen = strlen(m->f_mntfromname) + 1;
00146   size_t dirlen = strlen(m->f_mntonname) + 1;
00147   size_t typelen = strlen(m->f_fstypename) + 1;
00148   size_t totlen = infolen + fslen + dirlen + typelen;
00149   FSInfo *i = (FSInfo *) malloc(totlen);
00150   char *p = (char *) i;
00151   i->fsname = strncpy(p += infolen, m->f_mntfromname, fslen);
00152   i->type = strncpy(p += fslen, m->f_fstypename, typelen);
00153   i->dir = strncpy(p += typelen, m->f_mntonname, dirlen);
00154   i->dev = m->f_fsid.val[0];
00155   i->fstype = m->f_type;
00156   i->freespc = 0;
00157   if (m->f_bsize > 0)
00158   {
00159     i->freespc = m->f_bavail;
00160     i->freespc *= m->f_bsize;
00161     i->freespc /= 1024. * 1024. * 1024.;
00162   } 
00163   /* FIXME: This incorrectly says that mounted disk images are local,
00164      even if it was mounted from a network server. The alternative is
00165      to walk up the device tree using either a) process IORegistry to
00166      get the device tree, which lists devices for disk images, and from
00167      there translate volume uuid to a mount point; b) parse output from
00168      'hdiutil info -plist' to determine image-path / dev-entry map. */
00169   i->local = ((m->f_flags & MNT_LOCAL) ? 1 : 0);
00170   i->checked = 1;
00171   return i;
00172 
00173 #else // ! BSD
00174   mntent *m = static_cast<mntent *>(arg);
00175   size_t infolen = sizeof(struct FSInfo);
00176   size_t fslen = strlen(m->mnt_fsname) + 1;
00177   size_t dirlen = strlen(m->mnt_dir) + 1;
00178   size_t typelen = strlen(m->mnt_type) + 1;
00179   size_t totlen = infolen + fslen + dirlen + typelen;
00180   FSInfo *i = (FSInfo *) malloc(totlen);
00181   char *p = (char *) i;
00182   i->fsname = strncpy(p += infolen, m->mnt_fsname, fslen);
00183   i->type = strncpy(p += fslen, m->mnt_type, typelen);
00184   i->dir = strncpy(p += typelen, m->mnt_dir, dirlen);
00185   i->dev = -1;
00186   i->fstype = -1;
00187   i->freespc = 0;
00188   i->local = 0;
00189   i->checked = 0;
00190 
00191   for (size_t j = 0; j < fstypes_.size() && ! i->local; ++j)
00192     if (fstypes_[j] == i->type)
00193       i->local = 1;
00194 #endif // BSD
00195 
00196   return i;
00197 }
00198 
00205 int
00206 LocalFileSystem::initFSList(void)
00207 {
00208 #if BSD
00209   int rc;
00210   struct statfs *mtab = 0;
00211   if ((rc = getmntinfo(&mtab, MNT_NOWAIT)) < 0)
00212   {
00213     int nerr = errno;
00214     edm::LogWarning("LocalFileSystem::initFSList()")
00215       << "getmntinfo() failed: " << strerror(nerr)
00216       << " (error " << nerr << ")";
00217     return -1;
00218   }
00219 
00220   fs_.reserve(rc);
00221   for (int ix = 0; ix < rc; ++ix)
00222     fs_.push_back(initFSInfo(&mtab[ix]));
00223 
00224   free(mtab);
00225 #else
00226   struct mntent *m;
00227   FILE *mtab = setmntent(_PATH_MOUNTED, "r");
00228   if (! mtab)
00229   {
00230     int nerr = errno;
00231     edm::LogWarning("LocalFileSystem::initFSList()")
00232       << "Cannot read '" << _PATH_MOUNTED << "': "
00233       << strerror(nerr) << " (error " << nerr << ")";
00234     return -1;
00235   }
00236 
00237   fs_.reserve(20);
00238   while ((m = getmntent(mtab)))
00239     fs_.push_back(initFSInfo(m));
00240 
00241   endmntent(mtab);
00242 #endif
00243 
00244   return 0;
00245 }
00246 
00255 int
00256 LocalFileSystem::statFSInfo(FSInfo *i)
00257 {
00258   int ret = 0;
00259   struct stat s;
00260   struct statfs sfs;
00261 
00262   if (! i->checked)
00263   {
00264     i->checked = 1;
00265     if (lstat(i->dir, &s) < 0)
00266     {
00267       int nerr = errno;
00268       if (nerr != ENOENT && nerr != EACCES)
00269         edm::LogWarning("LocalFileSystem::statFSInfo()")
00270           << "Cannot lstat('" << i->dir << "'): "
00271           << strerror(nerr) << " (error " << nerr << ")";
00272       return -1;
00273     }
00274 
00275     if (statfs(i->dir, &sfs) < 0)
00276     {
00277       int nerr = errno;
00278       edm::LogWarning("LocalFileSystem::statFSInfo()")
00279         << "Cannot statfs('" << i->dir << "'): "
00280         << strerror(nerr) << " (error " << nerr << ")";
00281       return -1;
00282     }
00283 
00284     i->dev = s.st_dev;
00285     i->fstype = sfs.f_type;
00286     if (sfs.f_bsize > 0)
00287     {
00288       i->freespc = sfs.f_bavail;
00289       i->freespc *= sfs.f_bsize;
00290       i->freespc /= 1024. * 1024. * 1024.;
00291     }
00292   }
00293   else if (i->fstype == -1)
00294   {
00295     errno = ENOENT;
00296     ret = -1;
00297   }
00298 
00299   return ret;
00300 }
00301 
00315 LocalFileSystem::FSInfo *
00316 LocalFileSystem::findMount(const char *path, struct statfs *sfs, struct stat *s)
00317 {
00318   FSInfo *best = 0;
00319   size_t bestlen = 0;
00320   size_t len = strlen(path);
00321   for (size_t i = 0; i < fs_.size(); ++i)
00322   {
00323     // First match simply against the file system path.  We don't
00324     // touch the file system until the path prefix matches.
00325     size_t fslen = strlen(fs_[i]->dir);
00326     if (! strncmp(fs_[i]->dir, path, fslen)
00327         && ((fslen == 1 && fs_[i]->dir[0] == '/')
00328             || len == fslen || path[fslen] == '/')
00329         && (! best || fslen > bestlen))
00330     {
00331       // Get the file system device and file system ids.
00332       if (statFSInfo(fs_[i]) < 0)
00333         return 0;
00334 
00335       // Check the path is on the same device / file system.  If this
00336       // fails, we found a better prefix match on path, but it's the
00337       // wrong device, so reset our idea of the best match: it can't
00338       // be the outer mount any more.  Not sure this is the right
00339       // thing to do with e.g. loop-back or union mounts.
00340       if (fs_[i]->dev != s->st_dev || fs_[i]->fstype != sfs->f_type)
00341       {
00342         best = 0;
00343         continue;
00344       }
00345 
00346       // OK this is better than anything else we found so far.
00347       best = fs_[i];
00348       bestlen = fslen;
00349     }
00350   }
00351 
00352   return best;
00353 }
00354 
00364 bool
00365 LocalFileSystem::isLocalPath(const std::string &path)
00366 {
00367   struct stat s;
00368   struct statfs sfs;
00369   char *fullpath = realpath(path.c_str(), 0);
00370 
00371   if (! fullpath)
00372     fullpath = strdup(path.c_str());
00373 
00374   if (lstat(fullpath, &s) < 0)
00375   {
00376     int nerr = errno;
00377     edm::LogWarning("LocalFileSystem::isLocalPath()")
00378       << "Cannot lstat('" << fullpath << "' alias '"
00379       << path << "'): " << strerror(nerr) << " (error "
00380       << nerr << ")";
00381     free(fullpath);
00382     return false;
00383   }
00384     
00385   if (statfs(fullpath, &sfs) < 0)
00386   {
00387     int nerr = errno;
00388     edm::LogWarning("LocalFileSystem::isLocalPath()")
00389       << "Cannot statfs('" << fullpath << "' alias '"
00390       << path << "'): " << strerror(nerr) << " (error "
00391       << nerr << ")";
00392     free(fullpath);
00393     return false;
00394   }
00395 
00396   FSInfo *m = findMount(fullpath, &sfs, &s);
00397   free(fullpath);
00398 
00399   return m ? m->local : false;
00400 }
00401 
00420 std::string
00421 LocalFileSystem::findCachePath(const std::vector<std::string> &paths,
00422                                double minFreeSpace)
00423 {
00424   struct stat s;
00425   struct statfs sfs;
00426   for (size_t i = 0, e = paths.size(); i < e; ++i)
00427   {
00428     char *fullpath;
00429     const char *inpath = paths[i].c_str();
00430     const char *path = inpath;
00431 
00432     if (*path == '$')
00433     {
00434       char *p = getenv(path+1);
00435       if (p && *p)
00436         path = p;
00437       else if (! strcmp(path, "$TMPDIR"))
00438         path = "/tmp";
00439     }
00440 
00441     if (! (fullpath = realpath(path, 0)))
00442       fullpath = strdup(path);
00443 
00444 #if 0
00445     std::cerr /* edm::LogInfo("LocalFileSystem") */
00446       << "Checking if '" << fullpath << "', from '"
00447       << inpath << "' is valid cache path with "
00448       << minFreeSpace << " free space" << std::endl;
00449 #endif
00450 
00451     if (lstat(fullpath, &s) < 0)
00452     {
00453       int nerr = errno;
00454       if (nerr != ENOENT && nerr != EACCES)
00455         edm::LogWarning("LocalFileSystem::findCachePath()")
00456           << "Cannot lstat('" << fullpath << "', from '"
00457           << inpath << "'): " << strerror(nerr) << " (error "
00458           << nerr << ")";
00459       free(fullpath);
00460       continue;
00461     }
00462     
00463     if (statfs(fullpath, &sfs) < 0)
00464     {
00465       int nerr = errno;
00466       edm::LogWarning("LocalFileSystem::findCachePath()")
00467         << "Cannot statfs('" << fullpath << "', from '"
00468         << inpath << "'): " << strerror(nerr) << " (error "
00469         << nerr << ")";
00470       free(fullpath);
00471       continue;
00472     }
00473 
00474     FSInfo *m = findMount(fullpath, &sfs, &s);
00475 #if 0
00476     std::cerr /* edm::LogInfo("LocalFileSystem") */
00477       << "Candidate '" << fullpath << "': "
00478       << "found=" << (m ? 1 : 0)
00479       << " local=" << (m && m->local)
00480       << " free=" << (m ? m->freespc : 0)
00481       << " access=" << access(fullpath, W_OK)
00482       << std::endl;
00483 #endif
00484 
00485     if (m
00486         && m->local
00487         && m->freespc >= minFreeSpace
00488         && access(fullpath, W_OK) == 0)
00489     {
00490       std::string result(fullpath);
00491       free(fullpath);
00492       return result;
00493     }
00494 
00495     free(fullpath);
00496   }
00497 
00498   return std::string();
00499 }
00500 
00502 LocalFileSystem::LocalFileSystem(void)
00503 {
00504   if (readFSTypes() < 0)
00505     return;
00506 
00507   if (initFSList() < 0)
00508     return;
00509 }
00510 
00512 LocalFileSystem::~LocalFileSystem(void)
00513 {
00514   for (size_t i = 0, e = fs_.size(); i < e; ++i)
00515     free(fs_[i]);
00516 }