XRootD
XrdOfsCPFile.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d O f s C h k R e c . c c */
4 /* */
5 /* (c) 2020 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 <cstdio>
34 #include <cstring>
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/uio.h>
39 #include <vector>
40 
41 #include "XrdOfs/XrdOfsConfigCP.hh"
42 #include "XrdOfs/XrdOfsCPFile.hh"
43 #include "XrdOuc/XrdOucCRC.hh"
44 #include "XrdOuc/XrdOucIOVec.hh"
45 #include "XrdSys/XrdSysE2T.hh"
46 #include "XrdSys/XrdSysFD.hh"
47 #include "XrdSys/XrdSysPlatform.hh"
48 #include "XrdSys/XrdSysPthread.hh"
49 #include "XrdSys/XrdSysXAttr.hh"
50 
51 #ifndef ENODATA
52 #define ENODATA ENOATTR
53 #endif
54 
55 /******************************************************************************/
56 /* E x t e r n a l L i n k a g e s */
57 /******************************************************************************/
58 
60 
61 #define XATTR XrdSysXAttrNative
62 
63 /******************************************************************************/
64 /* L o c a l C l a s s e s */
65 /******************************************************************************/
66 
67 namespace
68 {
69 struct cUp
70 { int fd;
71 
72  cUp() : fd(-1) {}
73  ~cUp() {if (fd >= 0) close(fd);}
74 };
75 
76 struct cpHdr
77 { uint32_t crc32C; // CRC32C of all following bytes in header
78  int16_t hdrLen; // Length of the header
79  int16_t lfnLen; // Length if lfn including null byte
80  uint64_t fSize; // Original size of the file
81  time_t mTime; // Original modification time
82  uint64_t rsvd[3]; // Reserved
83  char srcUrl[8]; // " file://" the lfn follows start at lfn
84 // char srcLfn[]; // Appended to this struct of length lfnLen
85 };
86 
87 struct cpSeg
88 { uint32_t crc32C; // CRC32C of all following bytes in segment
89  int32_t dataLen; // Length of data that follows
90  off_t dataOfs; // Offset from where the data came and goes
91 };
92 
93 static const unsigned int crcSZ = sizeof(uint32_t);
94 static const unsigned int hdrSZ = sizeof(cpHdr);
95 static const unsigned int segSZ = sizeof(cpSeg);
96 static const char *attrName = "xrdckp_srclfn";
97 }
98 
99 /******************************************************************************/
100 /* C h e c k p o i n t F i l e N a m e D a t a */
101 /******************************************************************************/
102 
103 namespace
104 {
105 
106 uint32_t InitSeq(char *buff, int n)
107 {
108  uint32_t tod = static_cast<uint32_t>(time(0));
109  snprintf(buff, n, "%08x", tod);
110  return 1;
111 }
112 
113 char ckpHdr[12];
114 uint32_t ckpSeq = InitSeq(ckpHdr, sizeof(ckpHdr));
115 }
116 
117 /******************************************************************************/
118 /* r p I n f o C o n s t r u c t o r a n d D e s t r u c t o r */
119 /******************************************************************************/
120 
121 XrdOfsCPFile::rInfo::rInfo() : srcLFN(0), fSize(0), mTime(0),
122  DataVec(0), DataNum(0), DataLen(0), rBuff(0) {}
123 
125 { if (DataVec) delete [] DataVec;
126  if (rBuff) free(rBuff);
127 }
128 
129 /******************************************************************************/
130 /* X r d O f s C P F i l e M e t h o d s */
131 /******************************************************************************/
132 /******************************************************************************/
133 /* C o n s t r u c t o r */
134 /******************************************************************************/
135 
136 XrdOfsCPFile::XrdOfsCPFile(const char *ckpfn)
137  : ckpFN(ckpfn ? strdup(ckpfn) : 0), ckpFD(-1),
138  ckpDLen(0), ckpSize(0) {}
139 
140 /******************************************************************************/
141 /* D e s t r u c t o r */
142 /******************************************************************************/
143 
145 {
146 
147 // Close the file descriptor if need be
148 //
149  if (ckpFD >= 0) close(ckpFD);
150  if (ckpFN) free(ckpFN);
151 }
152 
153 /******************************************************************************/
154 /* A p p e n d */
155 /******************************************************************************/
156 
157 int XrdOfsCPFile::Append(const char *data, off_t offset, int dlen)
158 {
159  struct iovec ioV[2];
160  cpSeg theSeg;
161  int retval;
162 
163 // Account for the data we will be writing
164 //
165  ckpDLen += dlen;
166  ckpSize += dlen + segSZ;
167 
168 // Construct the next segment
169 //
170  theSeg.dataOfs = offset;
171  theSeg.dataLen = dlen;
172 
173 // Compute checksum of the data and the segment information
174 //
175  theSeg.crc32C = XrdOucCRC::Calc32C(((char *)&theSeg)+crcSZ, segSZ-crcSZ);
176  theSeg.crc32C = XrdOucCRC::Calc32C(data, dlen, theSeg.crc32C);
177 
178 // Construct iovec to write both pieces out
179 //
180  ioV[0].iov_base = &theSeg;
181  ioV[0].iov_len = segSZ;
182  ioV[1].iov_base = (void *)data;
183  ioV[1].iov_len = dlen;
184 
185 // Write the data out
186 //
187  retval = writev(ckpFD, ioV, 2);
188  if (retval != (int)(dlen+segSZ)) return (retval < 0 ? -errno : -EIO);
189 
190 // All done
191 //
192  return 0;
193 }
194 
195 /******************************************************************************/
196 /* C r e a t e */
197 /******************************************************************************/
198 
199 int XrdOfsCPFile::Create(const char *srcFN, struct stat &Stat)
200 {
201  static const int oFlag = O_CREAT | O_EXCL | O_WRONLY;
202  static const int oMode = S_IRUSR | S_IWUSR | S_IRGRP;
203  struct iovec ioV[2];
204  cpHdr theHdr;
205  int retval, rc = 0;
206 
207 // Make sure we do not have an active checkpoint here
208 //
209  if (ckpFD >= 0 || ckpFN) return -EEXIST;
210 
211 // Generate the path to the checkpoint file
212 //
213  ckpFN = genCkpPath();
214  if (!ckpFN) return -ENOMEM;
215 
216 // Create the checkpoint file and set its attribute
217 //
218  if ((ckpFD = XrdSysFD_Open(ckpFN, oFlag, oMode)) < 0
219  || XATTR.Set(attrName, srcFN, strlen(srcFN)+1, ckpFN, ckpFD) < 0)
220  {rc = -errno;
221  if (ckpFD >= 0) {close(ckpFD); ckpFD = -1;}
222  unlink(ckpFN);
223  free(ckpFN);
224  ckpFN = 0;
225  return rc;
226  }
227 
228 // Construct the header
229 //
230  theHdr.lfnLen = strlen(srcFN) + 1;
231  theHdr.hdrLen = hdrSZ + theHdr.lfnLen;
232  theHdr.fSize = Stat.st_size;
233  theHdr.mTime = Stat.st_mtime;
234  memcpy(theHdr.srcUrl, " file://", sizeof(theHdr.srcUrl));
235  memset(theHdr.rsvd, 0, sizeof(theHdr.rsvd));
236 
237 // Generate CRC32C checksum for the header and source filename
238 //
239  theHdr.crc32C = XrdOucCRC::Calc32C(((char *)&theHdr)+crcSZ, hdrSZ-crcSZ);
240  theHdr.crc32C = XrdOucCRC::Calc32C(srcFN, theHdr.lfnLen, theHdr.crc32C);
241 
242 // Construct I/O vector to write out the header
243 //
244  ioV[0].iov_base = &theHdr;
245  ioV[0].iov_len = sizeof(theHdr);
246  ioV[1].iov_base = (void *)srcFN;
247  ioV[1].iov_len = theHdr.lfnLen;
248  ckpSize = sizeof(theHdr) + theHdr.lfnLen;
249 
250 // Write out the header and make sure it gets stored
251 //
252  retval = writev(ckpFD, ioV, 2);
253  if (retval != ckpSize) rc = (retval < 0 ? -errno : -EIO);
254  else if (fsync(ckpFD)) rc = -errno;
255 
256 // Eliminate the checkpoint file if we encountered any error
257 //
258  if (rc) {if (ftruncate(ckpFD, 0) && unlink(ckpFN)) {}}
259  return rc;
260 }
261 
262 /******************************************************************************/
263 /* D e s t r o y */
264 /******************************************************************************/
265 
267 {
268  int rc;
269 
270 // Attempt to destroy the checkpoint file
271 //
272  if (ckpFN && unlink(ckpFN))
273  {rc = errno;
274  if (!truncate(ckpFN, 0) || !ErrState()) rc = 0;
275  } else rc = 0;
276 
277 // All done
278 //
279  return rc;
280 }
281 
282 /******************************************************************************/
283 /* E r r S t a t e */
284 /******************************************************************************/
285 
287 {
288  char buff[MAXPATHLEN+8];
289 
290 // Place checkpoint file in error state. If the rename fails, then the
291 // checkpoint will be applied again which should fail anyway. This just
292 // tries to avoid that issue and leave a trail.
293 //
294  snprintf(buff, sizeof(buff), "%serr", ckpFN);
295  return (rename(ckpFN, buff) ? -errno : 0);
296 }
297 
298 /******************************************************************************/
299 /* F N a m e */
300 /******************************************************************************/
301 
302 const char *XrdOfsCPFile::FName(bool trim)
303 {
304  if (ckpFN)
305  {if (trim)
306  {char *slash = rindex(ckpFN, '/');
307  if (slash) return slash+1;
308  }
309  return ckpFN;
310  }
311  return "???";
312 }
313 
314 /******************************************************************************/
315 /* Static Private: g e n C k p P a t h */
316 /******************************************************************************/
317 
318 char *XrdOfsCPFile::genCkpPath()
319 {
320  static XrdSysMutex mtx;
321  char ckpPath[MAXPATHLEN];
322  uint32_t seq;
323 
324  mtx.Lock(); seq = ckpSeq++; mtx.UnLock();
325 
326  snprintf(ckpPath, sizeof(ckpPath), "%s%s-%u.ckp",
327  XrdOfsConfigCP::Path, ckpHdr, seq);
328  return strdup(ckpPath);
329 }
330 
331 /******************************************************************************/
332 /* Static Private: g e t S r c L f n */
333 /******************************************************************************/
334 
335 int XrdOfsCPFile::getSrcLfn(const char *cFN, XrdOfsCPFile::rInfo &rinfo,
336  int fd, int rc)
337 {
338  char srcfn[MAXPATHLEN+80];
339  int n;
340 
341 
342  if ((n = XATTR.Get(attrName, srcfn, sizeof(srcfn)-1, cFN, fd)) > 0)
343  {srcfn[n] = 0;
344  if (rinfo.rBuff) free(rinfo.rBuff);
345  rinfo.rBuff = strdup(srcfn);
346  rinfo.srcLFN = (const char *)rinfo.rBuff;
347  }
348  return -rc;
349 }
350 
351 /******************************************************************************/
352 /* R e s e r v e */
353 /******************************************************************************/
354 
355 bool XrdOfsCPFile::Reserve(int dlen, int nseg)
356 {
357 // Make sure paramenters are valid
358 //
359  if (dlen < 0 || nseg < 0 || ckpFD < 0) return false;
360 
361 // Calculate the amount of space to reserve
362 //
363  dlen += nseg*segSZ;
364 
365 // Now allocate the space
366 //
367 #ifdef __APPLE__
368  fstore_t Store = {F_ALLOCATEALL, F_PEOFPOSMODE, ckpSize, dlen, 0};
369  if (fcntl(ckpFD, F_PREALLOCATE, &Store) == -1
370  && ftruncate(ckpFD, ckpSize + dlen) == -1) return false;
371 #else
372  if (posix_fallocate(ckpFD, ckpSize, dlen))
373  {if (ftruncate(ckpFD, ckpSize)) {}
374  return false;
375  }
376 #endif
377 
378 // All done
379 //
380  return true;
381 }
382 
383 /******************************************************************************/
384 /* Static: R e s t o r e I n f o */
385 /******************************************************************************/
386 
387 int XrdOfsCPFile::RestoreInfo(XrdOfsCPFile::rInfo &rinfo, const char *&eWhy)
388 {
389  std::vector<XrdOucIOVec> vecIO;
390  struct stat Stat;
391  XrdOucIOVec *ioV, ioItem;
392  char *ckpRec, *ckpEnd;
393  cpSeg theSeg;
394  cUp cup;
395  int retval;
396  bool aOK;
397 
398 // Open the file
399 //
400  if ((cup.fd = XrdSysFD_Open(ckpFN, O_RDONLY)) < 0)
401  {if (errno == ENOENT) return -ENOENT;
402  eWhy = "open failed";
403  return getSrcLfn(ckpFN, rinfo, cup.fd, errno);
404  }
405 
406 // Get the size of the file
407 //
408  if (fstat(cup.fd, &Stat))
409  {eWhy = "stat failed";
410  return getSrcLfn(ckpFN, rinfo, cup.fd, errno);
411  }
412 
413 // If this is a zero length file, then it has not been comitted which is OK
414 //
415  if (Stat.st_size == 0) return getSrcLfn(ckpFN, rinfo, cup.fd, ENODATA);
416 
417 // The file must be at least the basic record size
418 //
419  if (Stat.st_size < hdrSZ+1)
420  {eWhy = "truncated header";
421  return getSrcLfn(ckpFN, rinfo, cup.fd, EDOM);
422  }
423 
424 // Allocate memory to read the whole file
425 //
426  if (!(ckpRec = (char *)malloc(Stat.st_size)))
427  return getSrcLfn(ckpFN, rinfo, cup.fd, ENOMEM);
428  rinfo.rBuff = ckpRec;
429 
430 // Now read the whole file into the buffer
431 //
432  if ((retval = read(cup.fd, ckpRec, Stat.st_size)) != Stat.st_size)
433  {eWhy = "read failed";
434  return getSrcLfn(ckpFN, rinfo, cup.fd, (retval < 0 ? errno : EIO));
435  }
436 
437 // We have a catch-22 as we need to use the record length to verify the checksum
438 // but it may have been corrupted. So, we first verify the value is reasonably
439 // correct relative to the value of the lfn length and the fixed header length.
440 //
441  cpHdr &theHdr = *((cpHdr *)ckpRec);
442  if (theHdr.hdrLen > Stat.st_size
443  || (theHdr.hdrLen - theHdr.lfnLen) != (int)hdrSZ)
444  {eWhy = "corrupted header";
445  return getSrcLfn(ckpFN, rinfo, cup.fd, EDOM);
446  }
447 
448 // Verify the header checksum
449 //
450  if (!XrdOucCRC::Ver32C(ckpRec+crcSZ, theHdr.hdrLen-crcSZ, theHdr.crc32C))
451  {eWhy = "header checksum mismatch";
452  return getSrcLfn(ckpFN, rinfo, cup.fd, EDOM);
453  }
454 
455 // Set the source file name and other information
456 //
457  rinfo.srcLFN = ckpRec+hdrSZ;
458  rinfo.fSize = theHdr.fSize;
459  rinfo.mTime = theHdr.mTime;
460 
461 // Prepare to verify and record the segments
462 //
463  ckpEnd = ckpRec + Stat.st_size;
464  ckpRec = ckpRec + theHdr.hdrLen;
465  ioItem.info = 0;
466  vecIO.reserve(16);
467 
468 // Verify all of the segments
469 //
470  aOK = false; eWhy = 0;
471  while(ckpRec+sizeof(cpSeg) < ckpEnd)
472  {memcpy(&theSeg, ckpRec, segSZ);
473  if (!theSeg.dataLen && !theSeg.dataOfs && !theSeg.crc32C)
474  {aOK = true;
475  break;
476  }
477  char *ckpData = ckpRec + segSZ;
478  if (theSeg.dataLen <= 0 || ckpData + theSeg.dataLen > ckpEnd) break;
479  int cLen = theSeg.dataLen+sizeof(cpSeg)-crcSZ;
480  if (!XrdOucCRC::Ver32C(ckpRec+crcSZ, cLen, theSeg.crc32C))
481  {eWhy = "data checksum mismatch";
482  break;
483  }
484  ioItem.offset = theSeg.dataOfs;
485  ioItem.size = theSeg.dataLen;
486  ioItem.data = ckpRec + segSZ;
487  rinfo.DataLen += theSeg.dataLen;
488  vecIO.push_back(ioItem);
489  ckpRec += (segSZ + theSeg.dataLen);
490  }
491 
492 // Check that we ended perfectly (we accept a failed write as long as the
493 // space was already allocated).
494 //
495  if (!aOK && ckpRec != ckpEnd)
496  {if (!eWhy) eWhy = "truncated file";
497  return -EDOM;
498  }
499 
500 // If the file had no data changed, return as only the size changed. Otherwise,
501 // allocate an iovec for all of the segments we need to restore.
502 //
503  if (!vecIO.size()) return 0;
504  ioV = new XrdOucIOVec[vecIO.size()];
505 
506 // Fill in the vector in reverse order as this is the restore sequence
507 //
508  int j = vecIO.size() - 1;
509  for (int i = 0; i < (int)vecIO.size(); i++) ioV[j--] = vecIO[i];
510 
511 // All done
512 //
513  rinfo.DataVec = ioV;
514  rinfo.DataNum = vecIO.size();
515  return 0;
516 }
517 
518 /******************************************************************************/
519 /* S y n c */
520 /******************************************************************************/
521 
523 {
524  if (fsync(ckpFD)) return -errno;
525  return 0;
526 }
527 
528 /******************************************************************************/
529 /* Static: T a r g e t */
530 /******************************************************************************/
531 
532 char *XrdOfsCPFile::Target(const char *ckpfn)
533 {
534  struct {cpHdr hdr; char srcfn[MAXPATHLEN+8];} ckpRec;
535  cUp cup;
536  const char *eMsg = "Target unknown; corrupt checkpoint file";
537  int n;
538 
539 // Try to get the name via the extended attributes first
540 //
541  if ((n = XATTR.Get(attrName,ckpRec.srcfn,sizeof(ckpRec.srcfn)-1,ckpfn)) > 0)
542  {ckpRec.srcfn[n] = 0;
543  return strdup(ckpRec.srcfn);
544  }
545 
546 // Open the file
547 //
548  if ((cup.fd = XrdSysFD_Open(ckpfn, O_RDONLY)) < 0)
549  {char buff[256];
550  snprintf(buff, sizeof(buff), "Target unknown; %s", XrdSysE2T(errno));
551  return strdup(buff);
552  }
553 
554 // Now read the file header
555 //
556  if ((n = read(cup.fd, &ckpRec, sizeof(ckpRec))) <= (int)sizeof(cpHdr))
557  return strdup(eMsg);
558 
559 // Make sure the length of the lfn is reasonable
560 //
561  if (ckpRec.hdr.lfnLen <= 1 || ckpRec.hdr.lfnLen > (int)MAXPATHLEN)
562  return strdup(eMsg);
563 
564 // Return a copy of the filename
565 //
566  ckpRec.srcfn[ckpRec.hdr.lfnLen-1] = 0;
567  return strdup(ckpRec.srcfn);
568 }
569 
570 /******************************************************************************/
571 /* U s e d */
572 /******************************************************************************/
573 
574 int XrdOfsCPFile::Used(int nseg) {return ckpSize + (nseg*segSZ);}
struct stat Stat
Definition: XrdCks.cc:49
void trim(std::string &str)
Definition: XrdHttpReq.cc:77
#define ENODATA
Definition: XrdOfsCPFile.cc:52
#define XATTR
Definition: XrdOfsCPFile.cc:61
XrdSysXAttr & XrdSysXAttrNative
Definition: XrdSysFAttr.cc:57
int unlink(const char *path)
int rename(const char *oldpath, const char *newpath)
int fsync(int fildes)
ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)
ssize_t read(int fildes, void *buf, size_t nbyte)
#define close(a)
Definition: XrdPosix.hh:48
#define fstat(a, b)
Definition: XrdPosix.hh:62
#define stat(a, b)
Definition: XrdPosix.hh:101
#define ftruncate(a, b)
Definition: XrdPosix.hh:70
#define truncate(a, b)
Definition: XrdPosix.hh:111
#define eMsg(x)
const char * XrdSysE2T(int errcode)
Definition: XrdSysE2T.cc:104
int64_t fSize
Original size of the source file.
const char * srcLFN
Pointer to the source filename.
XrdOucIOVec * DataVec
A vector of data that must be written back.
int DataLen
Number of bytes to write back (may be 0)
int DataNum
Number of elements in DataVec (may be 0)
time_t mTime
Original modification time of the source.
bool Reserve(int dlen, int nseg)
static char * Target(const char *ckpfn)
int Append(const char *data, off_t offset, int dlen)
int Used(int nseg=0)
XrdOfsCPFile(const char *cfn=0)
int RestoreInfo(rInfo &rinfo, const char *&ewhy)
~XrdOfsCPFile()
Destructor.
int Create(const char *lfn, struct stat &Stat)
const char * FName(bool trim=false)
static char * Path
static uint32_t Calc32C(const void *data, size_t count, uint32_t prevcs=0)
Definition: XrdOucCRC.cc:190
static bool Ver32C(const void *data, size_t count, const uint32_t csval, uint32_t *csbad=0)
Definition: XrdOucCRC.cc:222