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;
00029 char *type;
00030 char *dir;
00031 dev_t dev;
00032 long fstype;
00033 double freespc;
00034 unsigned local : 1;
00035 unsigned checked : 1;
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
00164
00165
00166
00167
00168
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
00324
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
00332 if (statFSInfo(fs_[i]) < 0)
00333 return 0;
00334
00335
00336
00337
00338
00339
00340 if (fs_[i]->dev != s->st_dev || fs_[i]->fstype != sfs->f_type)
00341 {
00342 best = 0;
00343 continue;
00344 }
00345
00346
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
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
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 }