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 
215 // Create the checkpoint file and set its attribute
216 //
217  if ((ckpFD = XrdSysFD_Open(ckpFN, oFlag, oMode)) < 0
218  || XATTR.Set(attrName, srcFN, strlen(srcFN)+1, ckpFN, ckpFD) < 0)
219  {rc = -errno;
220  if (ckpFD >= 0) {close(ckpFD); ckpFD = -1;}
221  unlink(ckpFN);
222  free(ckpFN);
223  ckpFN = 0;
224  }
225 
226 // Construct the header
227 //
228  theHdr.lfnLen = strlen(srcFN) + 1;
229  theHdr.hdrLen = hdrSZ + theHdr.lfnLen;
230  theHdr.fSize = Stat.st_size;
231  theHdr.mTime = Stat.st_mtime;
232  memcpy(theHdr.srcUrl, " file://", sizeof(theHdr.srcUrl));
233  memset(theHdr.rsvd, 0, sizeof(theHdr.rsvd));
234 
235 // Generate CRC32C checksum for the header and source filename
236 //
237  theHdr.crc32C = XrdOucCRC::Calc32C(((char *)&theHdr)+crcSZ, hdrSZ-crcSZ);
238  theHdr.crc32C = XrdOucCRC::Calc32C(srcFN, theHdr.lfnLen, theHdr.crc32C);
239 
240 // Construct I/O vector to write out the header
241 //
242  ioV[0].iov_base = &theHdr;
243  ioV[0].iov_len = sizeof(theHdr);
244  ioV[1].iov_base = (void *)srcFN;
245  ioV[1].iov_len = theHdr.lfnLen;
246  ckpSize = sizeof(theHdr) + theHdr.lfnLen;
247 
248 // Write out the header and make sure it gets stored
249 //
250  retval = writev(ckpFD, ioV, 2);
251  if (retval != ckpSize) rc = (retval < 0 ? -errno : -EIO);
252  else if (fsync(ckpFD)) rc = -errno;
253 
254 // Eliminate the checkpoint file if we encountered any error
255 //
256  if (rc) {if (ftruncate(ckpFD, 0) && unlink(ckpFN)) {}}
257  return rc;
258 }
259 
260 /******************************************************************************/
261 /* D e s t r o y */
262 /******************************************************************************/
263 
265 {
266  int rc;
267 
268 // Attempt to destroy the checkpoint file
269 //
270  if (ckpFN && unlink(ckpFN))
271  {rc = errno;
272  if (!truncate(ckpFN, 0) || !ErrState()) rc = 0;
273  } else rc = 0;
274 
275 // All done
276 //
277  return rc;
278 }
279 
280 /******************************************************************************/
281 /* E r r S t a t e */
282 /******************************************************************************/
283 
285 {
286  char buff[MAXPATHLEN+8];
287 
288 // Place checkpoint file in error state. If the rename fails, then the
289 // checkpoint will be applied again which should fail anyway. This just
290 // tries to avoid that issue and leave a trail.
291 //
292  snprintf(buff, sizeof(buff), "%serr", ckpFN);
293  return (rename(ckpFN, buff) ? -errno : 0);
294 }
295 
296 /******************************************************************************/
297 /* F N a m e */
298 /******************************************************************************/
299 
300 const char *XrdOfsCPFile::FName(bool trim)
301 {
302  if (ckpFN)
303  {if (trim)
304  {char *slash = rindex(ckpFN, '/');
305  if (slash) return slash+1;
306  }
307  return ckpFN;
308  }
309  return "???";
310 }
311 
312 /******************************************************************************/
313 /* Static Private: g e n C k p P a t h */
314 /******************************************************************************/
315 
316 char *XrdOfsCPFile::genCkpPath()
317 {
318  static XrdSysMutex mtx;
319  char ckpPath[MAXPATHLEN];
320  uint32_t seq;
321 
322  mtx.Lock(); seq = ckpSeq++; mtx.UnLock();
323 
324  snprintf(ckpPath, sizeof(ckpPath), "%s%s-%u.ckp",
325  XrdOfsConfigCP::Path, ckpHdr, seq);
326  return strdup(ckpPath);
327 }
328 
329 /******************************************************************************/
330 /* Static Private: g e t S r c L f n */
331 /******************************************************************************/
332 
333 int XrdOfsCPFile::getSrcLfn(const char *cFN, XrdOfsCPFile::rInfo &rinfo,
334  int fd, int rc)
335 {
336  char srcfn[MAXPATHLEN+80];
337  int n;
338 
339 
340  if ((n = XATTR.Get(attrName, srcfn, sizeof(srcfn)-1, cFN, fd)) > 0)
341  {srcfn[n] = 0;
342  if (rinfo.rBuff) free(rinfo.rBuff);
343  rinfo.rBuff = strdup(srcfn);
344  rinfo.srcLFN = (const char *)rinfo.rBuff;
345  }
346  return -rc;
347 }
348 
349 /******************************************************************************/
350 /* R e s e r v e */
351 /******************************************************************************/
352 
353 bool XrdOfsCPFile::Reserve(int dlen, int nseg)
354 {
355 // Make sure paramenters are valid
356 //
357  if (dlen < 0 || nseg < 0 || ckpFD < 0) return false;
358 
359 // Calculate the amount of space to reserve
360 //
361  dlen += nseg*segSZ;
362 
363 // Now allocate the space
364 //
365 #ifdef __APPLE__
366  fstore_t Store = {F_ALLOCATEALL, F_PEOFPOSMODE, ckpSize, dlen};
367  if (fcntl(ckpFD, F_PREALLOCATE, &Store) == -1
368  && ftruncate(ckpFD, ckpSize + dlen) == -1) return false;
369 #else
370  if (posix_fallocate(ckpFD, ckpSize, dlen))
371  {if (ftruncate(ckpFD, ckpSize)) {}
372  return false;
373  }
374 #endif
375 
376 // All done
377 //
378  return true;
379 }
380 
381 /******************************************************************************/
382 /* Static: R e s t o r e I n f o */
383 /******************************************************************************/
384 
385 int XrdOfsCPFile::RestoreInfo(XrdOfsCPFile::rInfo &rinfo, const char *&eWhy)
386 {
387  std::vector<XrdOucIOVec> vecIO;
388  struct stat Stat;
389  XrdOucIOVec *ioV, ioItem;
390  char *ckpRec, *ckpEnd;
391  cpSeg theSeg;
392  cUp cup;
393  int retval;
394  bool aOK;
395 
396 // Open the file
397 //
398  if ((cup.fd = XrdSysFD_Open(ckpFN, O_RDONLY)) < 0)
399  {if (errno == ENOENT) return -ENOENT;
400  eWhy = "open failed";
401  return getSrcLfn(ckpFN, rinfo, cup.fd, errno);
402  }
403 
404 // Get the size of the file
405 //
406  if (fstat(cup.fd, &Stat))
407  {eWhy = "stat failed";
408  return getSrcLfn(ckpFN, rinfo, cup.fd, errno);
409  }
410 
411 // If this is a zero length file, then it has not been comitted which is OK
412 //
413  if (Stat.st_size == 0) return getSrcLfn(ckpFN, rinfo, cup.fd, ENODATA);
414 
415 // The file must be at least the basic record size
416 //
417  if (Stat.st_size < hdrSZ+1)
418  {eWhy = "truncated header";
419  return getSrcLfn(ckpFN, rinfo, cup.fd, EDOM);
420  }
421 
422 // Allocate memory to read the whole file
423 //
424  if (!(ckpRec = (char *)malloc(Stat.st_size)))
425  return getSrcLfn(ckpFN, rinfo, cup.fd, ENOMEM);
426  rinfo.rBuff = ckpRec;
427 
428 // Now read the whole file into the buffer
429 //
430  if ((retval = read(cup.fd, ckpRec, Stat.st_size)) != Stat.st_size)
431  {eWhy = "read failed";
432  return getSrcLfn(ckpFN, rinfo, cup.fd, (retval < 0 ? errno : EIO));
433  }
434 
435 // We have a catch-22 as we need to use the record length to verify the checksum
436 // but it may have been corrupted. So, we first verify the value is reasonably
437 // correct relative to the value of the lfn length and the fixed header length.
438 //
439  cpHdr &theHdr = *((cpHdr *)ckpRec);
440  if (theHdr.hdrLen > Stat.st_size
441  || (theHdr.hdrLen - theHdr.lfnLen) != (int)hdrSZ)
442  {eWhy = "corrupted header";
443  return getSrcLfn(ckpFN, rinfo, cup.fd, EDOM);
444  }
445 
446 // Verify the header checksum
447 //
448  if (!XrdOucCRC::Ver32C(ckpRec+crcSZ, theHdr.hdrLen-crcSZ, theHdr.crc32C))
449  {eWhy = "header checksum mismatch";
450  return getSrcLfn(ckpFN, rinfo, cup.fd, EDOM);
451  }
452 
453 // Set the source file name and other information
454 //
455  rinfo.srcLFN = ckpRec+hdrSZ;
456  rinfo.fSize = theHdr.fSize;
457  rinfo.mTime = theHdr.mTime;
458 
459 // Prepare to verify and record the segments
460 //
461  ckpEnd = ckpRec + Stat.st_size;
462  ckpRec = ckpRec + theHdr.hdrLen;
463  ioItem.info = 0;
464  vecIO.reserve(16);
465 
466 // Verify all of the segments
467 //
468  aOK = false; eWhy = 0;
469  while(ckpRec+sizeof(cpSeg) < ckpEnd)
470  {memcpy(&theSeg, ckpRec, segSZ);
471  if (!theSeg.dataLen && !theSeg.dataOfs && !theSeg.crc32C)
472  {aOK = true;
473  break;
474  }
475  char *ckpData = ckpRec + segSZ;
476  if (theSeg.dataLen <= 0 || ckpData + theSeg.dataLen > ckpEnd) break;
477  int cLen = theSeg.dataLen+sizeof(cpSeg)-crcSZ;
478  if (!XrdOucCRC::Ver32C(ckpRec+crcSZ, cLen, theSeg.crc32C))
479  {eWhy = "data checksum mismatch";
480  break;
481  }
482  ioItem.offset = theSeg.dataOfs;
483  ioItem.size = theSeg.dataLen;
484  ioItem.data = ckpRec + segSZ;
485  rinfo.DataLen += theSeg.dataLen;
486  vecIO.push_back(ioItem);
487  ckpRec += (segSZ + theSeg.dataLen);
488  }
489 
490 // Check that we ended perfectly (we accept a failed write as long as the
491 // space was already allocated).
492 //
493  if (!aOK && ckpRec != ckpEnd)
494  {if (!eWhy) eWhy = "truncated file";
495  return -EDOM;
496  }
497 
498 // If the file had no data changed, return as only the size changed. Otherwise,
499 // allocate an iovec for all of the segments we need to restore.
500 //
501  if (!vecIO.size()) return 0;
502  ioV = new XrdOucIOVec[vecIO.size()];
503 
504 // Fill in the vector in reverse order as this is the restore sequence
505 //
506  int j = vecIO.size() - 1;
507  for (int i = 0; i < (int)vecIO.size(); i++) ioV[j--] = vecIO[i];
508 
509 // All done
510 //
511  rinfo.DataVec = ioV;
512  rinfo.DataNum = vecIO.size();
513  return 0;
514 }
515 
516 /******************************************************************************/
517 /* S y n c */
518 /******************************************************************************/
519 
521 {
522  if (fsync(ckpFD)) return -errno;
523  return 0;
524 }
525 
526 /******************************************************************************/
527 /* Static: T a r g e t */
528 /******************************************************************************/
529 
530 char *XrdOfsCPFile::Target(const char *ckpfn)
531 {
532  struct {cpHdr hdr; char srcfn[MAXPATHLEN+8];} ckpRec;
533  cUp cup;
534  const char *eMsg = "Target unknown; corrupt checkpoint file";
535  int n;
536 
537 // Try to get the name via the extended attributes first
538 //
539  if ((n = XATTR.Get(attrName,ckpRec.srcfn,sizeof(ckpRec.srcfn)-1,ckpfn)) > 0)
540  {ckpRec.srcfn[n] = 0;
541  return strdup(ckpRec.srcfn);
542  }
543 
544 // Open the file
545 //
546  if ((cup.fd = XrdSysFD_Open(ckpfn, O_RDONLY)) < 0)
547  {char buff[256];
548  snprintf(buff, sizeof(buff), "Target unknown; %s", XrdSysE2T(errno));
549  return strdup(buff);
550  }
551 
552 // Now read the file header
553 //
554  if ((n = read(cup.fd, &ckpRec, sizeof(ckpRec))) <= (int)sizeof(cpHdr))
555  return strdup(eMsg);
556 
557 // Make sure the length of the lfn is reasonable
558 //
559  if (ckpRec.hdr.lfnLen <= 1 || ckpRec.hdr.lfnLen > (int)MAXPATHLEN)
560  return strdup(eMsg);
561 
562 // Return a copy of the filename
563 //
564  ckpRec.srcfn[ckpRec.hdr.lfnLen-1] = 0;
565  return strdup(ckpRec.srcfn);
566 }
567 
568 /******************************************************************************/
569 /* U s e d */
570 /******************************************************************************/
571 
572 int XrdOfsCPFile::Used(int nseg) {return ckpSize + (nseg*segSZ);}
struct stat Stat
Definition: XrdCks.cc:49
void trim(std::string &str)
Definition: XrdHttpReq.cc:76
#define ENODATA
Definition: XrdOfsCPFile.cc:52
#define XATTR
Definition: XrdOfsCPFile.cc:61
XrdSysXAttr & XrdSysXAttrNative
Definition: XrdSysFAttr.cc:57
int truncate(const char *path, off_t offset)
int stat(const char *path, struct stat *buf)
int ftruncate(int fildes, off_t offset)
int fstat(int fildes, struct stat *buf)
int fcntl(int fd, int cmd,...)
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:43
#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