/******************************************************************************/
/* */
/* 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);
}