// ---------------------------------------------------------------------- // File: Stat.cc // Author: Andreas-Joachim Peters - CERN // ---------------------------------------------------------------------- /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2011 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ // ----------------------------------------------------------------------- // This file is included source code in XrdMgmOfs.cc to make the code more // transparent without slowing down the compilation time. // ----------------------------------------------------------------------- /*----------------------------------------------------------------------------*/ #include "namespace/Resolver.hh" int XrdMgmOfs::stat(const char* inpath, struct stat* buf, XrdOucErrInfo& error, const XrdSecEntity* client, const char* ininfo ) { return stat(inpath, buf, error, 0, client, ininfo, false); } /*----------------------------------------------------------------------------*/ int XrdMgmOfs::stat(const char* inpath, struct stat* buf, XrdOucErrInfo& error, std::string* etag, const XrdSecEntity* client, const char* ininfo, bool follow, std::string* uri, std::string* cks) /*----------------------------------------------------------------------------*/ /* * @brief return stat information for a given path * * @param inpath path to stat * @param buf stat buffer where to store the stat information * @param error error object * @param client XRootD authentication object * @param ininfo CGI * @param etag string to return the ETag for that object * @param follow to indicate to follow symbolic links on leave nodes * @return SFS_OK on success otherwise SFS_ERROR * * See the internal implemtation _stat for details. */ /*----------------------------------------------------------------------------*/ { static const char* epname = "stat"; const char* tident = error.getErrUser(); // use a thread private vid eos::common::VirtualIdentity vid = eos::common::VirtualIdentity::Nobody(); NAMESPACEMAP; BOUNCE_ILLEGAL_NAMES; XrdOucEnv Open_Env(ininfo); AUTHORIZE(client, &Open_Env, AOP_Stat, "stat", inpath, error); EXEC_TIMING_BEGIN("IdMap"); eos::common::Mapping::IdMap(client, ininfo, tident, vid, gOFS->mTokenAuthz, AOP_Stat, path, true); EXEC_TIMING_END("IdMap"); gOFS->MgmStats.Add("IdMap", vid.uid, vid.gid, 1); BOUNCE_NOT_ALLOWED; ACCESSMODE_R; MAYSTALL; eos::common::Path cPath(path); // Never redirect stat's for the master mode if (cPath.GetFullPath() != gOFS->MgmProcMasterPath) { MAYREDIRECT; } errno = 0; int rc = _stat(path, buf, error, vid, ininfo, etag, follow, uri, cks); if (rc) { if (errno == ENOENT) { MAYREDIRECT_ENOENT; MAYSTALL_ENOENT; } } else { _stat_set_flags(buf); } return rc; } /*----------------------------------------------------------------------------*/ void XrdMgmOfs::_stat_set_flags(struct stat* buf) /*----------------------------------------------------------------------------*/ /* * @brief set XRDSFS_OFFLINE and XRDSFS_HASBKUP flags * * @param[in,out] buf Stat structure * * XRDSFS_HASBKUP is set iff there is a tape copy for the file * XRDSFS_OFFLINE is set iff there is no disk copy for the file * (i.e. only a tape copy exists) */ /*----------------------------------------------------------------------------*/ { // If EOS_TAPE_MODE_T is set, there is a copy on tape if (buf->st_mode & EOS_TAPE_MODE_T) { buf->st_rdev |= XRDSFS_HASBKUP; } else { buf->st_rdev &= ~XRDSFS_HASBKUP; } // Number of disk copies = total number of copies - 1 if there is a tape copy auto numDiskCopies = buf->st_nlink - (buf->st_mode & EOS_TAPE_MODE_T ? 1 : 0); if (numDiskCopies > 0) { buf->st_rdev &= ~XRDSFS_OFFLINE; } else { buf->st_rdev |= XRDSFS_OFFLINE; } } /*----------------------------------------------------------------------------*/ int XrdMgmOfs::_stat(const char* path, struct stat* buf, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, const char* ininfo, std::string* etag, bool follow, std::string* uri, std::string* cks) /*----------------------------------------------------------------------------*/ /* * @brief return stat information for a given path * * @param inpath path to stat * @param buf stat buffer where to store the stat information * @param error error object * @param vid virtual identity of the client * @param ininfo CGI * @param follow to indicate to follow symbolic links on leave nodes * @return SFS_OK on success otherwise SFS_ERROR * * We don't apply any access control on stat calls for performance reasons. * Modification times of directories are only emulated and returned from an * in-memory map. */ /*----------------------------------------------------------------------------*/ { static const char* epname = "_stat"; EXEC_TIMING_BEGIN("Stat"); gOFS->MgmStats.Add("Stat", vid.uid, vid.gid, 1); // --------------------------------------------------------------------------- // try if that is a file errno = 0; std::shared_ptr fmd; eos::common::Path cPath(path); // Stat on the master proc entry succeeds only if this MGM is in RW master mode if (cPath.GetFullPath() == gOFS->MgmProcMasterPath) { if (!gOFS->mMaster->IsMaster()) { return Emsg(epname, error, ENODEV, "stat", cPath.GetPath()); } } // public access level restriction if (!gOFS->allow_public_access(path, vid)) { eos_static_err("vid.uid=%d\n", vid.uid); errno = EACCES; return Emsg(epname, error, EACCES, "access - public access level restriction", path); } // Prefetch path eos::Prefetcher::prefetchItemAndWait(gOFS->eosView, cPath.GetPath(), follow); eos::common::RWMutexReadLock lock(gOFS->eosViewRWMutex); try { if (strncmp(cPath.GetPath(), "/.fxid:", 7) == 0) { XrdOucString spath = cPath.GetPath(); unsigned long long byfid = eos::Resolver::retrieveFileIdentifier( spath).getUnderlyingUInt64(); fmd = gOFS->eosFileService->getFileMD(byfid); eos_info("msg=\"access by inode\" ino=%s", cPath.GetPath()); } else { fmd = gOFS->eosView->getFile(cPath.GetPath(), follow); // if a stat comes with file/ return an error if (std::string(path).back() == '/') { errno = EISDIR; return Emsg(epname, error, errno, "stat", cPath.GetPath()); } } if (uri) { *uri = gOFS->eosView->getUri(fmd.get()); } if (cks) { eos::appendChecksumOnStringAsHex(fmd.get(), *cks); } } catch (eos::MDException& e) { errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(), e.getMessage().str().c_str()); if (errno == ELOOP) { return Emsg(epname, error, errno, "stat", cPath.GetPath()); } } if (fmd) { memset(buf, 0, sizeof(struct stat)); buf->st_dev = 0xcaff; buf->st_ino = eos::common::FileId::FidToInode(fmd->getId()); buf->st_mode = eos::modeFromMetadataEntry(fmd); if (fmd->isLink()) { buf->st_nlink = 1; } else { // we have to pass only disk locations to GetRedundancy and correct unsigned long disk_locations = fmd->getNumLocation(); if (buf->st_mode & EOS_TAPE_MODE_T) { if (disk_locations > 0) { disk_locations--; } } buf->st_nlink = eos::common::LayoutId::GetRedundancy(fmd->getLayoutId(), disk_locations); if ((buf->st_mode & EOS_TAPE_MODE_T)) { // file is unavailable on disk, but available on tape e.g. redundancy from disk layout = 0 buf->st_nlink++; } } buf->st_size = fmd->getSize(); buf->st_uid = fmd->getCUid(); buf->st_gid = fmd->getCGid(); buf->st_rdev = 0; /* device type (if inode device) */ buf->st_blksize = 512; buf->st_blocks = (Quota::MapSizeCB(fmd.get()) + 512) / 512; // including layout factor eos::IFileMD::ctime_t atime; // adding also nanosecond to stat struct fmd->getCTime(atime); #ifdef __APPLE__ buf->st_ctimespec.tv_sec = atime.tv_sec; buf->st_ctimespec.tv_nsec = atime.tv_nsec; #else buf->st_ctime = atime.tv_sec; buf->st_ctim.tv_sec = atime.tv_sec; buf->st_ctim.tv_nsec = atime.tv_nsec; #endif fmd->getMTime(atime); #ifdef __APPLE__ buf->st_mtimespec.tv_sec = atime.tv_sec; buf->st_mtimespec.tv_nsec = atime.tv_nsec; fmd->getATime(atime); buf->st_atimespec.tv_sec = atime.tv_sec; buf->st_atimespec.tv_nsec = atime.tv_nsec; #else buf->st_mtime = atime.tv_sec; buf->st_mtim.tv_sec = atime.tv_sec; buf->st_mtim.tv_nsec = atime.tv_nsec; fmd->getATime(atime); buf->st_atime = atime.tv_sec; buf->st_atim.tv_sec = atime.tv_sec; buf->st_atim.tv_nsec = atime.tv_nsec; #endif if (etag) { eos::calculateEtag(fmd.get(), *etag); if (fmd->hasAttribute("sys.eos.mdino")) { *etag = "hardlink"; } } EXEC_TIMING_END("Stat"); return SFS_OK; } // Check if it's a directory std::shared_ptr cmd; errno = 0; // --------------------------------------------------------------------------- try { cmd = gOFS->eosView->getContainer(cPath.GetPath(), follow); if (uri) { *uri = gOFS->eosView->getUri(cmd.get()); } memset(buf, 0, sizeof(struct stat)); buf->st_dev = 0xcaff; buf->st_ino = cmd->getId(); buf->st_mode = eos::modeFromMetadataEntry(cmd); buf->st_nlink = 1; buf->st_uid = cmd->getCUid(); buf->st_gid = cmd->getCGid(); buf->st_rdev = 0; /* device type (if inode device) */ buf->st_size = cmd->getTreeSize(); buf->st_blksize = cmd->getNumContainers() + cmd->getNumFiles(); buf->st_blocks = 0; eos::IContainerMD::ctime_t ctime; eos::IContainerMD::ctime_t mtime; eos::IContainerMD::ctime_t tmtime; cmd->getCTime(ctime); cmd->getMTime(mtime); if (gOFS->eosSyncTimeAccounting) { cmd->getTMTime(tmtime); } else // if there is no sync time accounting we just use the normal modification time { tmtime = mtime; } #ifdef __APPLE__ buf->st_atimespec.tv_sec = tmtime.tv_sec; buf->st_mtimespec.tv_sec = mtime.tv_sec; buf->st_ctimespec.tv_sec = ctime.tv_sec; buf->st_atimespec.tv_nsec = tmtime.tv_nsec; buf->st_mtimespec.tv_nsec = mtime.tv_nsec; buf->st_ctimespec.tv_nsec = ctime.tv_nsec; #else buf->st_atime = tmtime.tv_sec; buf->st_mtime = mtime.tv_sec; buf->st_ctime = ctime.tv_sec; buf->st_atim.tv_sec = tmtime.tv_sec; buf->st_mtim.tv_sec = mtime.tv_sec; buf->st_ctim.tv_sec = ctime.tv_sec; buf->st_atim.tv_nsec = tmtime.tv_nsec; buf->st_mtim.tv_nsec = mtime.tv_nsec; buf->st_ctim.tv_nsec = ctime.tv_nsec; #endif if (etag) { eos::calculateEtag(cmd.get(), *etag); } if (cks) { *cks = ""; } return SFS_OK; } catch (eos::MDException& e) { errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(), e.getMessage().str().c_str()); return Emsg(epname, error, errno, "stat", cPath.GetPath()); } } // --------------------------------------------------------------------------- // get the checksum info of a file // --------------------------------------------------------------------------- int XrdMgmOfs::_getchecksum(const char* Name, XrdOucErrInfo& error, std::string* xstype, std::string* xs, const XrdSecEntity* client, const char* opaque, bool follow) { // --------------------------------------------------------------------------- errno = 0; std::shared_ptr fmd; eos::common::Path cPath(Name); eos::Prefetcher::prefetchFileMDAndWait(gOFS->eosView, cPath.GetPath(), follow); eos::common::RWMutexReadLock lock(gOFS->eosViewRWMutex); try { fmd = gOFS->eosView->getFile(cPath.GetPath(), follow); } catch (eos::MDException& e) { errno = e.getErrno(); eos_debug("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(), e.getMessage().str().c_str()); return errno; } if (fmd) { size_t cxlen = eos::common::LayoutId::GetChecksumLen(fmd->getLayoutId()); if (cxlen) { *xstype = eos::common::LayoutId::GetChecksumStringReal(fmd->getLayoutId()); eos::appendChecksumOnStringAsHex(fmd.get(), *xs); } } return 0; } //------------------------------------------------------------------------------ // Stat following links (not existing in EOS - behaves like stat) //------------------------------------------------------------------------------ int XrdMgmOfs::lstat(const char* path, struct stat* buf, XrdOucErrInfo& error, const XrdSecEntity* client, const char* info) { return stat(path, buf, error, client, info); }