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
121XrdOfsCPFile::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
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
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
300const 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
316char *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
333int 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
353bool 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, 0};
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
385int 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
530char *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
572int 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 XATTR
XrdSysXAttr & XrdSysXAttrNative
#define ENODATA
int fcntl(int fd, int cmd,...)
#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