/******************************************************************************/ /* */ /* X r d O s s C o p y . c c */ /* */ /* (c) 2009 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 "XrdOss/XrdOssCopy.hh" #include "XrdOss/XrdOssTrace.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysFAttr.hh" /******************************************************************************/ /* E r r o r R o u t i n g O b j e c t */ /******************************************************************************/ extern XrdSysError OssEroute; extern XrdOucTrace OssTrace; /******************************************************************************/ /* Public: C o p y */ /******************************************************************************/ off_t XrdOssCopy::Copy(const char *inFn, const char *outFn, int outFD) { static const size_t segSize = 1024*1024; class ioFD {public: int FD; ioFD(int fd=-1) : FD(fd) {} ~ioFD() {if (FD >= 0) close(FD);} } In, Out(outFD); struct utimbuf tBuff; struct stat buf, bufO, bufSL; char *inBuff, *bP; off_t Offset=0, fileSize; size_t ioSize, copySize; ssize_t rLen; int rc; // Open the input file // if ((In.FD = open(inFn, O_RDONLY)) < 0) return -OssEroute.Emsg("Copy", errno, "open", inFn); // Get the input filesize // if (fstat(In.FD, &buf)) return -OssEroute.Emsg("Copy", errno, "stat", inFn); copySize = fileSize = buf.st_size; // We can dispense with the copy if both files are in the same filesystem. // Note that the caller must have pre-allocate thed output file. We handle // avoiding creating a hard link to a symlink instead of the underlying file. // if (fstat(Out.FD, &bufO)) return -OssEroute.Emsg("Copy",errno,"stat",outFn); if (buf.st_dev == bufO.st_dev) {char lnkBuff[1024+8]; const char *srcFn = inFn; int n; if (lstat(inFn, &bufSL)) return -OssEroute.Emsg("Copy",errno,"lstat",inFn); if ((bufSL.st_mode & S_IFMT) == S_IFLNK) {if ((n = readlink(inFn, lnkBuff, sizeof(lnkBuff)-1)) < 0) return -OssEroute.Emsg("Copy", errno, "readlink", inFn); lnkBuff[n] = '\0'; srcFn = lnkBuff; } unlink(outFn); if (link(srcFn,outFn)) return -OssEroute.Emsg("Copy",errno,"link",outFn); return fileSize; } // We now copy 1MB segments using direct I/O // ioSize = (fileSize < (off_t)segSize ? fileSize : segSize); while(copySize) {if ((inBuff = (char *)mmap(0, ioSize, PROT_READ, #if defined(__FreeBSD__) MAP_RESERVED0040|MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED) #else MAP_NORESERVE|MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED) #endif {OssEroute.Emsg("Copy", errno, "memory map", inFn); break;} if (Write(outFn, Out.FD, inBuff, ioSize, Offset) < 0) break; copySize -= ioSize; Offset += ioSize; if (munmap(inBuff, ioSize) < 0) {OssEroute.Emsg("Copy", errno, "unmap memory for", inFn); break;} if (copySize < segSize) ioSize = copySize; } // check if there was an error and if we can recover if (copySize) { if ((off_t)copySize != fileSize) return -EIO; // Do a traditional copy (note that we didn't copy anything yet) OssEroute.Emsg("Copy", "Trying traditional copy for", inFn, "..."); char ioBuff[segSize]; off_t rdSize, wrSize = segSize, inOff=0; while(copySize) {if (copySize < segSize) rdSize = wrSize = copySize; else rdSize = segSize; bP = ioBuff; while(rdSize) {do {rLen = pread(In.FD, bP, rdSize, inOff);} while(rLen < 0 && errno == EINTR); if (rLen <= 0) return -OssEroute.Emsg("Copy", rLen ? errno : ECANCELED, "read", inFn); bP += rLen; rdSize -= rLen; inOff += rLen; } if ((rc = Write(outFn, Out.FD, ioBuff, wrSize, Offset)) < 0) return rc; copySize -= wrSize; Offset += wrSize; } } // Copy over any extended attributes // if (XrdSysFAttr::Xat->Copy(inFn, In.FD, outFn, Out.FD)) return -1; // Now set the time on the file to the original time // tBuff.actime = buf.st_atime; tBuff.modtime= buf.st_mtime; if (utime(outFn, &tBuff)) OssEroute.Emsg("Copy", errno, "set mtime for", outFn); // Success // return fileSize; } /******************************************************************************/ /* private: W r i t e */ /******************************************************************************/ int XrdOssCopy::Write(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 // return (BLen ? -OssEroute.Emsg("Copy", errno, "write", outFn) : 0); }