XRootD
Loading...
Searching...
No Matches
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
43#include "XrdOuc/XrdOucCRC.hh"
44#include "XrdOuc/XrdOucIOVec.hh"
45#include "XrdSys/XrdSysE2T.hh"
46#include "XrdSys/XrdSysFD.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
67namespace
68{
69struct cUp
70{ int fd;
71
72 cUp() : fd(-1) {}
73 ~cUp() {if (fd >= 0) close(fd);}
74};
75
76struct 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
87struct 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
93static const unsigned int crcSZ = sizeof(uint32_t);
94static const unsigned int hdrSZ = sizeof(cpHdr);
95static const unsigned int segSZ = sizeof(cpSeg);
96static 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
103namespace
104{
105
106uint32_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
113char ckpHdr[12];
114uint32_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
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
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
157int 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
199int 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
302const 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
318char *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
335int 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
355bool 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
387int 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
532char *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
574int 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 XATTR
XrdSysXAttr & XrdSysXAttrNative
#define ENODATA
#define close(a)
Definition XrdPosix.hh:48
#define fsync(a)
Definition XrdPosix.hh:64
#define fstat(a, b)
Definition XrdPosix.hh:62
#define writev(a, b, c)
Definition XrdPosix.hh:117
#define unlink(a)
Definition XrdPosix.hh:113
#define stat(a, b)
Definition XrdPosix.hh:101
#define rename(a, b)
Definition XrdPosix.hh:92
#define ftruncate(a, b)
Definition XrdPosix.hh:70
#define truncate(a, b)
Definition XrdPosix.hh:111
#define read(a, b, c)
Definition XrdPosix.hh:82
#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