//------------------------------------------------------------------------------ // File: FsIo.cc // Author: Elvin-Alin Sindrilaru - 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 .* ************************************************************************/ #include "fst/XrdFstOfsFile.hh" #include "fst/io/local/FsIo.hh" #include "common/XattrCompat.hh" #ifndef __APPLE__ #include #endif #undef __USE_FILE_OFFSET64 #include EOSFSTNAMESPACE_BEGIN //------------------------------------------------------------------------------ // Constructor //------------------------------------------------------------------------------ FsIo::FsIo(std::string path) : FileIo(path, "FsIo"), mFd(-1) { } //------------------------------------------------------------------------------ // Constructor //------------------------------------------------------------------------------ FsIo::FsIo(std::string path, std::string iotype) : FileIo(path, iotype), mFd(-1) { } //------------------------------------------------------------------------------ // Destructor //------------------------------------------------------------------------------ FsIo::~FsIo() { if (mFd != -1) { fileClose(mFd); } } //------------------------------------------------------------------------------ // Open file //------------------------------------------------------------------------------ int FsIo::fileOpen(XrdSfsFileOpenMode flags, mode_t mode, const std::string& opaque, uint16_t timeout) { mFd = ::open(mFilePath.c_str(), flags, mode); if (mFd > 0) { return 0; } else { mFd = -1; return -1; } } //------------------------------------------------------------------------------ // Open file asynchronously //------------------------------------------------------------------------------ std::future FsIo::fileOpenAsync(XrdSfsFileOpenMode flags, mode_t mode, const std::string& opaque, uint16_t timeout) { std::promise open_promise; std::future open_future = open_promise.get_future(); if (fileOpen(flags, mode, opaque, timeout) != SFS_OK) { open_promise.set_value(XrdCl::XRootDStatus(XrdCl::stError, XrdCl::errUnknown, EIO, "failed open")); } else { open_promise.set_value(XrdCl::XRootDStatus(XrdCl::stOK, "")); } return open_future; } //------------------------------------------------------------------------------ // Read from file - sync //------------------------------------------------------------------------------ int64_t FsIo::fileRead(XrdSfsFileOffset offset, char* buffer, XrdSfsXferSize length, uint16_t timeout) { return ::pread(mFd, buffer, length, offset); } //------------------------------------------------------------------------------ // Read from file async - falls back on synchronous mode //------------------------------------------------------------------------------ int64_t FsIo::fileReadPrefetch(XrdSfsFileOffset offset, char* buffer, XrdSfsXferSize length, uint16_t timeout) { return fileRead(offset, buffer, length, timeout); } //------------------------------------------------------------------------------ // Read from file asynchronously - falls back to synchronous mode //------------------------------------------------------------------------------ int64_t FsIo::fileReadAsync(XrdSfsFileOffset offset, char* buffer, XrdSfsXferSize length, uint16_t timeout) { return fileRead(offset, buffer, length, timeout); } //------------------------------------------------------------------------------ // Write to file - sync //------------------------------------------------------------------------------ int64_t FsIo::fileWrite(XrdSfsFileOffset offset, const char* buffer, XrdSfsXferSize length, uint16_t timeout) { return ::pwrite(mFd, buffer, length, offset); } //------------------------------------------------------------------------------ // Write to file async - falls back on synchronous mode //------------------------------------------------------------------------------ int64_t FsIo::fileWriteAsync(XrdSfsFileOffset offset, const char* buffer, XrdSfsXferSize length, uint16_t timeout) { return fileWrite(offset, buffer, length, timeout); } //---------------------------------------------------------------------------- // Write to file - async //-------------------------------------------------------------------------- std::future FsIo::fileWriteAsync(const char* buffer, XrdSfsFileOffset offset, XrdSfsXferSize length) { std::promise wr_promise; std::future wr_future = wr_promise.get_future(); int64_t nwrite = fileWrite(offset, buffer, length); if (nwrite != length) { wr_promise.set_value(XrdCl::XRootDStatus(XrdCl::stError, XrdCl::errUnknown, EIO, "failed write")); } else { wr_promise.set_value(XrdCl::XRootDStatus(XrdCl::stOK, "")); } return wr_future; } //------------------------------------------------------------------------------ // Truncate file //------------------------------------------------------------------------------ int FsIo::fileTruncate(XrdSfsFileOffset offset, uint16_t timeout) { return ::ftruncate(mFd, offset); } //------------------------------------------------------------------------------ // Truncate asynchronous //------------------------------------------------------------------------------ std::future FsIo::fileTruncateAsync(XrdSfsFileOffset offset, uint16_t timeout) { std::promise tr_promise; std::future tr_future = tr_promise.get_future(); int retc = fileTruncate(offset, timeout); if (retc) { tr_promise.set_value(XrdCl::XRootDStatus(XrdCl::stError, XrdCl::errUnknown, EIO, "failed truncate")); } else { tr_promise.set_value(XrdCl::XRootDStatus(XrdCl::stOK, "")); } return tr_future; } //------------------------------------------------------------------------------ // Allocate space for file //------------------------------------------------------------------------------ int FsIo::fileFallocate(XrdSfsFileOffset length) { eos_debug("fallocate with length = %lli", length); #ifdef __APPLE__ // no pre-allocation return 0; #else if (platform_test_xfs_fd(mFd)) { // Select the fast XFS allocation function if available xfs_flock64_t fl; fl.l_whence = 0; fl.l_start = 0; fl.l_len = (off64_t) length; return xfsctl(NULL, mFd, XFS_IOC_RESVSP64, &fl); } else { if (getenv("EOS_FST_POSIX_FALLOCATE")) { // only fallocate if defined return posix_fallocate(mFd, 0, length); } else { // don't fallocate return 0; } } #endif return SFS_ERROR; } //------------------------------------------------------------------------------ // Deallocate space reserved for file //------------------------------------------------------------------------------ int FsIo::fileFdeallocate(XrdSfsFileOffset fromOffset, XrdSfsFileOffset toOffset) { eos_debug("fdeallocate from = %lli to = %lli", fromOffset, toOffset); #ifdef __APPLE__ // no de-allocation return 0; #else if (mFd > 0) { if (platform_test_xfs_fd(mFd)) { // Select the fast XFS deallocation function if available xfs_flock64_t fl; fl.l_whence = 0; fl.l_start = fromOffset; fl.l_len = (off64_t) toOffset - fromOffset; return xfsctl(NULL, mFd, XFS_IOC_UNRESVSP64, &fl); } else { return 0; } } return SFS_ERROR; #endif } //------------------------------------------------------------------------------ // Sync file to disk //------------------------------------------------------------------------------ int FsIo::fileSync(uint16_t timeout) { return ::fsync(mFd); } //------------------------------------------------------------------------------ // Get stats about the file //------------------------------------------------------------------------------ int FsIo::fileStat(struct stat* buf, uint16_t timeout) { if (mFd > 0) { return ::fstat(mFd, buf); } else { return ::stat(mFilePath.c_str(), buf); } } //------------------------------------------------------------------------------ // Close file //------------------------------------------------------------------------------ int FsIo::fileClose(uint16_t timeout) { int rc = ::close(mFd); mFd = -1; return rc; } //------------------------------------------------------------------------------ // Remove file //------------------------------------------------------------------------------ int FsIo::fileRemove(uint16_t timeout) { struct stat buf; if (!fileStat(&buf)) { return ::unlink(mFilePath.c_str()); } return SFS_OK; } //------------------------------------------------------------------------------ // Check for existence by path //------------------------------------------------------------------------------ int FsIo::fileExists() { struct stat buf; return ::stat(mFilePath.c_str(), &buf); } //------------------------------------------------------------------------------ // Get pointer to async meta handler object //------------------------------------------------------------------------------ void* FsIo::fileGetAsyncHandler() { return NULL; } //------------------------------------------------------------------------------ // Open a cursor to traverse a storage system to find files //------------------------------------------------------------------------------ FileIo::FtsHandle* FsIo::ftsOpen() { FtsHandle* handle = (new FtsHandle(mFilePath.c_str())); handle->paths[0] = (char*) mFilePath.c_str(); handle->paths[1] = 0; handle->tree = (void*) fts_open(handle->paths, FTS_NOCHDIR, 0); if (!handle->tree) { delete handle; return NULL; } return dynamic_cast(handle); } //------------------------------------------------------------------------------ // Return the next path related to a traversal cursor obtained with ftsOpen //------------------------------------------------------------------------------ std::string FsIo::ftsRead(FileIo::FtsHandle* fts_handle) { FTSENT* node; FtsHandle* handle = dynamic_cast(fts_handle); if (handle) { while ((node = fts_read((FTS*) handle->tree))) { if (node->fts_level > 0 && node->fts_name[0] == '.') { fts_set((FTS*) handle->tree, node, FTS_SKIP); } else { if (node->fts_info == FTS_F) { XrdOucString filePath = node->fts_accpath; if (!filePath.matches("*.xsmap")) { return filePath.c_str(); } } } } } // no file anymore return ""; } //------------------------------------------------------------------------------ // Close a traversal cursor //------------------------------------------------------------------------------ int FsIo::ftsClose(FileIo::FtsHandle* fts_handle) { FtsHandle* handle = dynamic_cast(fts_handle); if (handle) { int rc = fts_close((FTS*) handle->tree); return rc; } return -1; } //------------------------------------------------------------------------------ // Get statfs information //------------------------------------------------------------------------------ int FsIo::Statfs(struct statfs* statFs) { return ::statfs(mFilePath.c_str(), statFs); } //------------------------------------------------------------------------------ // Set attr //------------------------------------------------------------------------------ int FsIo::attrSet(const char* name, const char* value, size_t len) { if ((!name) || (!value) || mFilePath.empty()) { errno = EINVAL; return SFS_ERROR; } #ifdef __APPLE__ return setxattr(mFilePath.c_str(), name, value, len, 0, 0); #else return lsetxattr(mFilePath.c_str(), name, value, len, 0); #endif } //------------------------------------------------------------------------------ // Set attr //------------------------------------------------------------------------------ int FsIo::attrSet(string name, std::string value) { return attrSet(name.c_str(), value.c_str(), value.length()); } //------------------------------------------------------------------------------ // Get attr //------------------------------------------------------------------------------ int FsIo::attrGet(const char* name, char* value, size_t& size) { if ((!name) || (!value) || mFilePath.empty()) { errno = EINVAL; return SFS_ERROR; } #ifdef __APPLE__ int retc = getxattr(mFilePath.c_str(), name, value, size, 0, 0); #else int retc = lgetxattr(mFilePath.c_str(), name, value, size); #endif if (retc != -1) { size = retc; return SFS_OK; } return SFS_ERROR; } //------------------------------------------------------------------------------ // Get attr //------------------------------------------------------------------------------ int FsIo::attrGet(string name, std::string& value) { char buffer[1024]; size_t size = sizeof(buffer); if (!attrGet(name.c_str(), buffer, size)) { value.assign(buffer, size); return SFS_OK; } return SFS_ERROR; } //------------------------------------------------------------------------------ // Delete attr //------------------------------------------------------------------------------ int FsIo::attrDelete(const char* name) { if ((!name) || mFilePath.empty()) { errno = EINVAL; return SFS_ERROR; } #ifdef __APPLE__ return removexattr(mFilePath.c_str(), name, 0); #else return lremovexattr(mFilePath.c_str(), name); #endif } //------------------------------------------------------------------------------ // List attr //------------------------------------------------------------------------------ int FsIo::attrList(std::vector& list) { if (mFilePath.empty()) { errno = EINVAL; return SFS_ERROR; } char* pointer = NULL; #ifdef __APPLE__ auto size = listxattr(mFilePath.c_str(), pointer, 0, XATTR_NOFOLLOW); #else auto size = llistxattr(mFilePath.c_str(), pointer, 0); #endif if (size <= 0) { return size; } std::vector buffer(size); #ifdef __APPLE__ size = listxattr(mFilePath.c_str(), buffer.data(), buffer.size(), XATTR_NOFOLLOW); #else size = llistxattr(mFilePath.c_str(), buffer.data(), buffer.size()); #endif if (size <= 0) { return size; } pointer = buffer.data(); while (pointer - buffer.data() < size) { list.push_back(std::string(pointer)); pointer += list.back().length() + 2; } return SFS_OK; } EOSFSTNAMESPACE_END