XRootD
XrdOssCopy.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d O s s C o p y . c c */
4 /* */
5 /* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* All Rights Reserved */
7 /* Produced by Andrew Hanushevsky for Stanford University under contract */
8 /* DE-AC02-76-SFO0515 with the Department of Energy */
9 /* */
10 /* This file is part of the XRootD software suite. */
11 /* */
12 /* XRootD is free software: you can redistribute it and/or modify it under */
13 /* the terms of the GNU Lesser General Public License as published by the */
14 /* Free Software Foundation, either version 3 of the License, or (at your */
15 /* option) any later version. */
16 /* */
17 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20 /* License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25 /* */
26 /* The copyright holder's institutional names and contributor's names may not */
27 /* be used to endorse or promote products derived from this software without */
28 /* specific prior written permission of the institution or contributor. */
29 /******************************************************************************/
30 
31 #include <cerrno>
32 #include <fcntl.h>
33 #include <cstring>
34 #include <ctime>
35 #include <cstdio>
36 #include <unistd.h>
37 #include <utime.h>
38 #include <sys/mman.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 
42 #include "XrdOss/XrdOssCopy.hh"
43 #include "XrdOss/XrdOssTrace.hh"
44 #include "XrdSys/XrdSysError.hh"
45 #include "XrdSys/XrdSysFAttr.hh"
46 
47 /******************************************************************************/
48 /* E r r o r R o u t i n g O b j e c t */
49 /******************************************************************************/
50 
51 extern XrdSysError OssEroute;
52 
53 extern XrdSysTrace OssTrace;
54 
55 /******************************************************************************/
56 /* Public: C o p y */
57 /******************************************************************************/
58 
59 off_t XrdOssCopy::Copy(const char *inFn, const char *outFn, int outFD)
60 {
61  static const size_t segSize = 1024*1024;
62  class ioFD
63  {public:
64  int FD;
65  ioFD(int fd=-1) : FD(fd) {}
66  ~ioFD() {if (FD >= 0) close(FD);}
67  } In, Out(outFD);
68 
69  struct utimbuf tBuff;
70  struct stat buf, bufO, bufSL;
71  char *inBuff, *bP;
72  off_t Offset=0, fileSize;
73  size_t ioSize, copySize;
74  ssize_t rLen;
75  int rc;
76 
77 // Open the input file
78 //
79  if ((In.FD = open(inFn, O_RDONLY)) < 0)
80  return -OssEroute.Emsg("Copy", errno, "open", inFn);
81 
82 // Get the input filesize
83 //
84  if (fstat(In.FD, &buf)) return -OssEroute.Emsg("Copy", errno, "stat", inFn);
85  copySize = fileSize = buf.st_size;
86 
87 // We can dispense with the copy if both files are in the same filesystem.
88 // Note that the caller must have pre-allocate thed output file. We handle
89 // avoiding creating a hard link to a symlink instead of the underlying file.
90 //
91  if (fstat(Out.FD, &bufO)) return -OssEroute.Emsg("Copy",errno,"stat",outFn);
92  if (buf.st_dev == bufO.st_dev)
93  {char lnkBuff[1024+8]; const char *srcFn = inFn; int n;
94  if (lstat(inFn, &bufSL)) return -OssEroute.Emsg("Copy",errno,"lstat",inFn);
95  if ((bufSL.st_mode & S_IFMT) == S_IFLNK)
96  {if ((n = readlink(inFn, lnkBuff, sizeof(lnkBuff)-1)) < 0)
97  return -OssEroute.Emsg("Copy", errno, "readlink", inFn);
98  lnkBuff[n] = '\0'; srcFn = lnkBuff;
99  }
100  unlink(outFn);
101  if (link(srcFn,outFn)) return -OssEroute.Emsg("Copy",errno,"link",outFn);
102  return fileSize;
103  }
104 
105 // We now copy 1MB segments using direct I/O
106 //
107  ioSize = (fileSize < (off_t)segSize ? fileSize : segSize);
108  while(copySize)
109  {if ((inBuff = (char *)mmap(0, ioSize, PROT_READ,
110 #if defined(__FreeBSD__)
111  MAP_RESERVED0040|MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED)
112 #elif defined(__GNU__)
113  MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED)
114 #else
115  MAP_NORESERVE|MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED)
116 #endif
117  {OssEroute.Emsg("Copy", errno, "memory map", inFn); break;}
118  if (Write(outFn, Out.FD, inBuff, ioSize, Offset) < 0) break;
119  copySize -= ioSize; Offset += ioSize;
120  if (munmap(inBuff, ioSize) < 0)
121  {OssEroute.Emsg("Copy", errno, "unmap memory for", inFn); break;}
122  if (copySize < segSize) ioSize = copySize;
123  }
124 
125 // check if there was an error and if we can recover
126 
127  if (copySize)
128  { if ((off_t)copySize != fileSize) return -EIO;
129  // Do a traditional copy (note that we didn't copy anything yet)
130  OssEroute.Emsg("Copy", "Trying traditional copy for", inFn, "...");
131  char ioBuff[segSize];
132  off_t rdSize, wrSize = segSize, inOff=0;
133  while(copySize)
134  {if (copySize < segSize) rdSize = wrSize = copySize;
135  else rdSize = segSize;
136  bP = ioBuff;
137  while(rdSize)
138  {do {rLen = pread(In.FD, bP, rdSize, inOff);}
139  while(rLen < 0 && errno == EINTR);
140  if (rLen <= 0) return -OssEroute.Emsg("Copy",
141  rLen ? errno : ECANCELED, "read", inFn);
142  bP += rLen; rdSize -= rLen; inOff += rLen;
143  }
144  if ((rc = Write(outFn, Out.FD, ioBuff, wrSize, Offset)) < 0) return rc;
145  copySize -= wrSize; Offset += wrSize;
146  }
147  }
148 
149 // Copy over any extended attributes
150 //
151  if (XrdSysFAttr::Xat->Copy(inFn, In.FD, outFn, Out.FD)) return -1;
152 
153 // Now set the time on the file to the original time
154 //
155  tBuff.actime = buf.st_atime;
156  tBuff.modtime= buf.st_mtime;
157  if (utime(outFn, &tBuff))
158  OssEroute.Emsg("Copy", errno, "set mtime for", outFn);
159 
160 // Success
161 //
162  return fileSize;
163 }
164 
165 /******************************************************************************/
166 /* private: W r i t e */
167 /******************************************************************************/
168 
169 int XrdOssCopy::Write(const char *outFn,
170  int oFD, char *Buff, size_t BLen, off_t BOff)
171 {
172  ssize_t wLen;
173 
174 // Copy out a segment
175 //
176  while(BLen)
177  {if ((wLen = pwrite(oFD, Buff, BLen, BOff)) < 0)
178  {if (errno == EINTR) continue;
179  else break;
180  }
181  Buff += wLen; BLen -= wLen; BOff += wLen;
182  }
183 
184 // Check for errors
185 //
186  return (BLen ? -OssEroute.Emsg("Copy", errno, "write", outFn) : 0);
187 }
XrdSysTrace OssTrace
XrdSysError OssEroute
ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset)
int stat(const char *path, struct stat *buf)
int open(const char *path, int oflag,...)
ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset)
int fstat(int fildes, struct stat *buf)
int lstat(const char *path, struct stat *buf)
int unlink(const char *path)
#define close(a)
Definition: XrdPosix.hh:43
static off_t Copy(const char *inFn, const char *outFn, int outFD)
Definition: XrdOssCopy.cc:59
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
static XrdSysXAttr * Xat
Definition: XrdSysFAttr.hh:51