/******************************************************************************/ /* */ /* X r d F r m A d m i n R e l o c . c c */ /* */ /* (c) 2008 by the Board of Trustees of the Leland Stanford, Jr., University */ /* All Rights Reserved */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Department of Energy */ /* */ /* This file is part of the XRootD software suite. */ /* */ /* XRootD is free software: you can redistribute it and/or modify it under */ /* the terms of the GNU Lesser General Public License as published by the */ /* Free Software Foundation, either version 3 of the License, or (at your */ /* option) any later version. */ /* */ /* XRootD 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 Lesser General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ /* COPYING (GPL license). If not, see . */ /* */ /* The copyright holder's institutional names and contributor's names may not */ /* be used to endorse or promote products derived from this software without */ /* specific prior written permission of the institution or contributor. */ /******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "XrdFrc/XrdFrcTrace.hh" #include "XrdFrm/XrdFrmAdmin.hh" #include "XrdFrm/XrdFrmConfig.hh" #include "XrdOss/XrdOss.hh" #include "XrdOss/XrdOssOpaque.hh" #include "XrdOss/XrdOssPath.hh" #include "XrdOss/XrdOssSpace.hh" #include "XrdOuc/XrdOucEnv.hh" #include "XrdOuc/XrdOucName2Name.hh" #include "XrdOuc/XrdOucTList.hh" #include "XrdSys/XrdSysPlatform.hh" using namespace XrdFrc; using namespace XrdFrm; /******************************************************************************/ /* R e l o c */ /******************************************************************************/ int XrdFrmAdmin::Reloc(char *srcLfn, char *Space) { static const int crOpts = (O_CREAT|O_EXCL)<<8; class relocRecover {public: char *Lfn; relocRecover() : Lfn(0) {} ~relocRecover() {if (Lfn) Config.ossFS->Unlink(Lfn);} } Recover; XrdOucTList *pP; XrdOucEnv myEnv; struct stat srcStat, lokStat; struct utimbuf tBuff; char trgLfn[1032], trgPfn[1032], trgSpace[XrdOssSpace::minSNbsz]; char srcLnk[1032], srcPfn[1032], srcSpace[XrdOssSpace::minSNbsz]; char lokPfn[1032], ASize[32], *fsTarget = 0; int rc, srcLsz = 0; // Obtain the target space information, verify that it exists // if (!(pP = ParseSpace(Space, &fsTarget))) return 4; strcpy(trgSpace, Space); if (fsTarget) *(fsTarget-1) = ':'; // Get the pfn for the incomming path // if (!Config.LocalPath(srcLfn, srcPfn, sizeof(srcPfn)-8)) {finalRC = 4; return 0;} // Make sure the source file exists and get its attributes // if ( lstat(srcPfn, &srcStat)) {Emsg(errno, "stat ", srcLfn); return 0;} if ((srcStat.st_mode & S_IFMT) == S_IFLNK) {if (stat(srcPfn, &srcStat)) {Emsg(errno, "stat ", srcLfn); return 0;} if ((srcLsz = readlink(srcPfn, srcLnk, sizeof(srcLnk)-1) < 0)) {Emsg(errno, "read link ", srcLfn); return 0;} srcLnk[srcLsz] = '\0'; } else *srcLnk = 0; XrdOssPath::getCname(srcPfn, srcSpace); // Check this operation really makes sense // if (!strcmp(srcSpace, trgSpace) || (fsTarget && !strncmp(fsTarget, srcLnk, strlen(fsTarget)))) {Emsg(srcLfn, " already in space ", Space); return 0;} // Get the current lock file time // strcpy(lokPfn, srcPfn); strcat(lokPfn, ".lock"); if (stat(lokPfn, &lokStat)) *lokPfn = '\0'; // Generate the target lfn and pfn // strcpy(trgLfn, srcLfn); strcat(trgLfn, ".anew"); if (!Config.LocalPath(trgLfn, trgPfn, sizeof(trgPfn))) {finalRC = 4; return 0;} // Set environmental variables // sprintf(ASize,"%lld", static_cast(srcStat.st_size)); myEnv.Put("oss.asize", ASize); myEnv.Put("oss.cgroup",Space); // Allocate a new file in the target space // rc = Config.ossFS->Create("admin",trgLfn,srcStat.st_mode&S_IAMB,myEnv,crOpts); if (rc) {Emsg(rc, "create placeholder for ", trgLfn); return 0;} // Now copy the source file to the target location. While we could possibly // have done a rename, this could have potentially disrupted access to the file. // Perform the reloc based on src/trg location // Recover.Lfn = trgPfn; if (!RelocCP(srcPfn, trgPfn, srcStat.st_size)) return 0; // Set the time of the file to it's original value // tBuff.actime = srcStat.st_atime; tBuff.modtime= srcStat.st_mtime; if (utime(trgPfn, &tBuff)) Emsg(errno, "set mtime for ", trgPfn); // Set the lock file time (do not let the reloc complete unless we can) // if (*lokPfn) {strcpy(lokPfn, trgPfn); strcat(lokPfn, ".lock"); tBuff.actime = lokStat.st_atime; tBuff.modtime= lokStat.st_mtime; if (utime(lokPfn, &tBuff)) Emsg(errno, "set mtime for ", srcLfn); return 0; } // Rename the new file to the old file // if ((rc = Config.ossFS->Rename(trgLfn, srcLfn))) {Emsg(-rc, "rename ", trgPfn); return 0;} Recover.Lfn = 0; // Now adjust space as needed // XrdOssSpace::Adjust(trgSpace, srcStat.st_size, XrdOssSpace::Pstg); XrdOssSpace::Adjust(srcSpace, -srcStat.st_size, XrdOssSpace::Purg); // If the source was another cache file syste, we need to remove the remnants // if (srcLsz) {if (symlink(srcLnk, trgPfn)) {Emsg(errno, "create symlink to ", srcLnk); return 0;} if ((rc = Config.ossFS->Unlink(trgLfn))) {Emsg(errno, "remove ", trgPfn); return 0;} } // All done // Msg(srcLfn, " relocated from space ", srcSpace, " to ", Space); return 0; } /******************************************************************************/ /* R e l o c C P */ /******************************************************************************/ int XrdFrmAdmin::RelocCP(const char *inFn, const char *outFn, off_t inSz) { static const size_t segSize = 1024*1024; class ioFD {public: int FD; ioFD() : FD(-1) {} ~ioFD() {if (FD >= 0) close(FD);} } In, Out; char *inBuff, ioBuff[segSize], *bP; off_t inOff=0, Offset=0, Size=inSz, outSize=segSize, inSize=segSize; size_t ioSize; ssize_t rLen; // Open the input file // if ((In.FD = open(inFn, O_RDONLY)) < 0) {Emsg(errno, "open ", inFn); return 1;} // Open the output file // if ((Out.FD = open(outFn, O_WRONLY)) < 0) {Emsg(errno, "open ", outFn); return 1;} // We now copy 1MB segments using direct I/O // ioSize = (Size < (int)segSize ? Size : segSize); while(Size) {if ((inBuff = (char *)mmap(0, ioSize, PROT_READ, MAP_NORESERVE|MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED) {Emsg(errno, "memory map ", inFn); break;} if (!RelocWR(outFn, Out.FD, inBuff, ioSize, Offset)) break; Size -= ioSize; Offset += ioSize; if (munmap(inBuff, ioSize) < 0) {Emsg(errno, "unmap memory for ", inFn); break;} if (Size < (int)segSize) ioSize = Size; } // Return if all went well, otherwise check if we can recover // if (!Size || Size != inSz) return Size == 0; Msg("Trying traditional copy...."); // Do a traditional copy // inSize = (inSz < (int)segSize ? Size : segSize); while(Size) {if (Size < (int)ioSize) outSize = inSize = Size; bP = ioBuff; while(inSize) {if ((rLen = pread(In.FD, bP, inSize, inOff)) < 0) {if (errno == EINTR) continue; else {Emsg(errno, "read ", inFn); return 0;} } bP += rLen; inSize -= rLen; inOff += rLen; } if (!RelocWR(outFn, Out.FD, ioBuff, outSize, Offset)) return 0; Size -= outSize; Offset += outSize; } // Success // return 1; } /******************************************************************************/ int XrdFrmAdmin::RelocWR(const char *outFn, int oFD, char *Buff, size_t BLen, off_t BOff) { ssize_t wLen; // Copy out a segment // while(BLen) {if ((wLen = pwrite(oFD, Buff, BLen, BOff)) < 0) {if (errno == EINTR) continue; else break; } Buff += wLen; BLen -= wLen; BOff += wLen; } // Check for errors // if (!BLen) return 1; Emsg(errno, "write ", outFn); return 0; }