/******************************************************************************/ /* */ /* X r d O s s C k s M a n a g e r . c c */ /* */ /* (c) 2011 by the Board of Trustees of the Leland Stanford, Jr., University */ /* All Rights Reserved */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Department of Energy */ /* */ /* This file is part of the XRootD software suite. */ /* */ /* XRootD is free software: you can redistribute it and/or modify it under */ /* the terms of the GNU Lesser General Public License as published by the */ /* Free Software Foundation, either version 3 of the License, or (at your */ /* option) any later version. */ /* */ /* XRootD is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ /* COPYING (GPL license). If not, see . */ /* */ /* The copyright holder's institutional names and contributor's names may not */ /* be used to endorse or promote products derived from this software without */ /* specific prior written permission of the institution or contributor. */ /******************************************************************************/ #include #include #include #include #include #include #include #include #include #include "XrdCks/XrdCksCalc.hh" #include "XrdCks/XrdCksCalcadler32.hh" #include "XrdCks/XrdCksCalccrc32.hh" #include "XrdCks/XrdCksCalcmd5.hh" #include "XrdCks/XrdCksLoader.hh" #include "XrdCks/XrdCksManager.hh" #include "XrdCks/XrdCksXAttr.hh" #include "XrdOuc/XrdOucPinLoader.hh" #include "XrdOuc/XrdOucTokenizer.hh" #include "XrdOuc/XrdOucUtils.hh" #include "XrdOuc/XrdOucXAttr.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysFAttr.hh" #include "XrdSys/XrdSysPlugin.hh" #include "XrdSys/XrdSysPthread.hh" /******************************************************************************/ /* C o n s t r u c t o r */ /******************************************************************************/ XrdCksManager::XrdCksManager(XrdSysError *erP, int rdsz, XrdVersionInfo &vInfo, bool autoload) : XrdCks(erP), myVersion(vInfo) { // Get a dynamic loader if so wanted // if (autoload) cksLoader = new XrdCksLoader(vInfo); else cksLoader = 0; // Prefill the native digests we support // strcpy(csTab[0].Name, "adler32"); strcpy(csTab[1].Name, "crc32"); strcpy(csTab[2].Name, "md5"); csLast = 2; // Compute the i/o size // if (rdsz <= 65536) segSize = 67108864; else segSize = ((rdsz/65536) + (rdsz%65536 != 0)) * 65536; } /******************************************************************************/ /* D e s t r u c t o r */ /******************************************************************************/ XrdCksManager::~XrdCksManager() { int i; for (i = 0; i <= csLast; i++) {if (csTab[i].Obj && csTab[i].doDel) csTab[i].Obj->Recycle(); if (csTab[i].Path) free( csTab[i].Path); if (csTab[i].Parms) free( csTab[i].Parms); if (csTab[i].Plugin) delete csTab[i].Plugin; } if (cksLoader) delete cksLoader; } /******************************************************************************/ /* C a l c */ /******************************************************************************/ int XrdCksManager::Calc(const char *Pfn, XrdCksData &Cks, int doSet) { XrdCksCalc *csP; csInfo *csIP = &csTab[0]; time_t MTime; int rc; // Determine which checksum to get // if (csLast < 0) return -ENOTSUP; if (!(*Cks.Name)) Cks.Set(csIP->Name); else if (!(csIP = Find(Cks.Name))) return -ENOTSUP; // If we need not set the checksum then see if we can get it from the // extended attributes. // Obtain a new checksum object // if (!(csP = csIP->Obj->New())) return -ENOMEM; // Use the calculator to get and possibly set the checksum // if (!(rc = Calc(Pfn, MTime, csP))) {memcpy(Cks.Value, csP->Final(), csIP->Len); Cks.fmTime = static_cast(MTime); Cks.csTime = static_cast(time(0) - MTime); Cks.Length = csIP->Len; csP->Recycle(); if (doSet) {XrdOucXAttr xCS; memcpy(&xCS.Attr.Cks, &Cks, sizeof(xCS.Attr.Cks)); if ((rc = xCS.Set(Pfn))) return -rc; } } // All done // return rc; } /******************************************************************************/ int XrdCksManager::Calc(const char *Pfn, time_t &MTime, XrdCksCalc *csP) { class ioFD {public: int FD; ioFD() : FD(-1) {} ~ioFD() {if (FD >= 0) close(FD);} } In; struct stat Stat; char *inBuff; off_t Offset=0, fileSize; size_t ioSize, calcSize; int rc; // Open the input file // if ((In.FD = open(Pfn, O_RDONLY)) < 0) return -errno; // Get the file characteristics // if (fstat(In.FD, &Stat)) return -errno; if (!(Stat.st_mode & S_IFREG)) return -EPERM; calcSize = fileSize = Stat.st_size; MTime = Stat.st_mtime; // We now compute checksum 64MB at a time using mmap I/O // ioSize = (fileSize < (off_t)segSize ? fileSize : segSize); rc = 0; while(calcSize) {if ((inBuff = (char *)mmap(0, ioSize, PROT_READ, #if defined(__FreeBSD__) MAP_RESERVED0040|MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED) #else MAP_NORESERVE|MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED) #endif {rc = errno; eDest->Emsg("Cks", rc, "memory map", Pfn); break;} madvise(inBuff, ioSize, MADV_SEQUENTIAL); csP->Update(inBuff, ioSize); calcSize -= ioSize; Offset += ioSize; if (munmap(inBuff, ioSize) < 0) {rc = errno; eDest->Emsg("Cks",rc,"unmap memory for",Pfn); break;} if (calcSize < (size_t)segSize) ioSize = calcSize; } // Return if we failed // if (calcSize) return (rc ? -rc : -EIO); return 0; } /******************************************************************************/ /* C o n f i g */ /******************************************************************************/ /* Purpose: To parse the directive: ckslib [] the name of the checksum. the path of the checksum library to be used. optional parms to be passed Output: 0 upon success or !0 upon failure. */ int XrdCksManager::Config(const char *Token, char *Line) { XrdOucTokenizer Cfg(Line); char *val, *path = 0, name[XrdCksData::NameSize], *parms; int i; // Get the the checksum name // Cfg.GetLine(); if (!(val = Cfg.GetToken()) || !val[0]) {eDest->Emsg("Config", "checksum name not specified"); return 1;} if (int(strlen(val)) >= XrdCksData::NameSize) {eDest->Emsg("Config", "checksum name too long"); return 1;} strcpy(name, val); XrdOucUtils::toLower(name); // Get the path and optional parameters // val = Cfg.GetToken(&parms); if (val && val[0]) path = strdup(val); else {eDest->Emsg("Config","library path missing for ckslib digest",name); return 1; } // Check if this replaces an existing checksum // for (i = 0; i < csMax; i++) if (!(*csTab[i].Name) || !strcmp(csTab[i].Name, name)) break; // See if we can insert a new checksum (or replace one) // if (i >= csMax) {eDest->Emsg("Config", "too many checksums specified"); if (path) free(path); return 1; } else if (!(*csTab[i].Name)) csLast = i; // Insert the new checksum // strcpy(csTab[i].Name, name); if (csTab[i].Path) free(csTab[i].Path); csTab[i].Path = path; if (csTab[i].Parms) free(csTab[i].Parms); csTab[i].Parms = (parms && *parms ? strdup(parms) : 0); // All done // return 0; } /******************************************************************************/ /* I n i t */ /******************************************************************************/ int XrdCksManager::Init(const char *ConfigFN, const char *DfltCalc) { int i; // See if we need to set the default calculation // if (DfltCalc) {for (i = 0; i < csLast; i++) if (!strcmp(csTab[i].Name, DfltCalc)) break; if (i >= csMax) {eDest->Emsg("Config", DfltCalc, "cannot be made the default; " "not supported."); return 0; } if (i) {csInfo Temp = csTab[i]; csTab[i] = csTab[0]; csTab[0] = Temp;} } // See if there are any chacksums to configure // if (csLast < 0) {eDest->Emsg("Config", "No checksums defined; cannot configure!"); return 0; } // Complete the checksum table // for (i = 0; i <= csLast; i++) {if (csTab[i].Path) {if (!(Config(ConfigFN, csTab[i]))) return 0;} else { if (!strcmp("adler32", csTab[i].Name)) csTab[i].Obj = new XrdCksCalcadler32; else if (!strcmp("crc32", csTab[i].Name)) csTab[i].Obj = new XrdCksCalccrc32; else if (!strcmp("md5", csTab[i].Name)) csTab[i].Obj = new XrdCksCalcmd5; else {eDest->Emsg("Config", "Invalid native checksum -", csTab[i].Name); return 0; } csTab[i].Obj->Type(csTab[i].Len); } } // All done // return 1; } /******************************************************************************/ #define XRDOSSCKSLIBARGS XrdSysError *, const char *, const char *, const char * int XrdCksManager::Config(const char *cFN, csInfo &Info) { XrdOucPinLoader myPin(eDest, &myVersion, "ckslib", Info.Path); XrdCksCalc *(*ep)(XRDOSSCKSLIBARGS); int n; // Find the entry point // Info.Plugin = 0; if (!(ep = (XrdCksCalc *(*)(XRDOSSCKSLIBARGS)) (myPin.Resolve("XrdCksCalcInit")))) {eDest->Emsg("Config", "Unable to configure cksum", Info.Name); myPin.Unload(); return 0; } // Get the initial object // if (!(Info.Obj = ep(eDest,cFN,Info.Name,(Info.Parms ? Info.Parms : "")))) {eDest->Emsg("Config", Info.Name, "checksum initialization failed"); myPin.Unload(); return 0; } // Verify the object // if (strcmp(Info.Name, Info.Obj->Type(n))) {eDest->Emsg("Config",Info.Name,"cksum plugin returned wrong name -", Info.Obj->Type(n)); myPin.Unload(); return 0; } if (n > XrdCksData::ValuSize || n <= 0) {eDest->Emsg("Config",Info.Name,"cksum plugin has an unsupported " "checksum length"); myPin.Unload(); return 0; } // All is well // Info.Plugin = myPin.Export(); Info.Len = n; return 1; } /******************************************************************************/ /* F i n d */ /******************************************************************************/ XrdCksManager::csInfo *XrdCksManager::Find(const char *Name) { static XrdSysMutex myMutex; XrdCksCalc *myCalc; int i; // Find the pre-loaded checksum // for (i = 0; i <= csLast; i++) if (!strcmp(Name, csTab[i].Name)) return &csTab[i]; // If we have loader see if we can auto-load this object // if (!cksLoader) return 0; myMutex.Lock(); // An entry could have been added as we were running unlocked // for (i = 0; i <= csLast; i++) if (!strcmp(Name, csTab[i].Name)) {myMutex.UnLock(); return &csTab[i]; } // Check if we have room in the table // if (csLast >= csMax) {myMutex.UnLock(); eDest->Emsg("CksMan","Unable to load",Name,"; checksum limit reached."); return 0; } // Attempte to dynamically load this object // { char buff[2048]; *buff = 0; if (!(myCalc = cksLoader->Load(Name, 0, buff, sizeof(buff), true))) {myMutex.UnLock(); eDest->Emsg("CksMan", "Unable to load", Name); if (*buff) eDest->Emsg("CksMan", buff); return 0; } } // Fill out the table // i = csLast + 1; strncpy(csTab[i].Name, Name, XrdCksData::NameSize); csTab[i].Obj = myCalc; csTab[i].Path = 0; csTab[i].Parms = 0; csTab[i].Plugin = 0; csTab[i].doDel = false; myCalc->Type(csTab[i].Len); // Return the result // csLast = i; myMutex.UnLock(); return &csTab[i]; } /******************************************************************************/ /* D e l */ /******************************************************************************/ int XrdCksManager::Del(const char *Pfn, XrdCksData &Cks) { XrdOucXAttr xCS; // Set the checksum name // xCS.Attr.Cks.Set(Cks.Name); // Delete the attribute and return the result // return xCS.Del(Pfn); } /******************************************************************************/ /* G e t */ /******************************************************************************/ int XrdCksManager::Get(const char *Pfn, XrdCksData &Cks) { XrdOucXAttr xCS; time_t MTime; int rc, nFault; // Determine which checksum to get (we will accept unsupported ones as well) // if (csLast < 0) return -ENOTSUP; if (!*Cks.Name) Cks.Set(csTab[0].Name); if (!xCS.Attr.Cks.Set(Cks.Name)) return -ENOTSUP; // Retreive the attribute // if ((rc = xCS.Get(Pfn)) <= 0) return (rc ? rc : -ESRCH); // Mark state of the name and copy the attribute over // nFault = strcmp(xCS.Attr.Cks.Name, Cks.Name); Cks = xCS.Attr.Cks; // Verify the file // if ((rc = ModTime(Pfn, MTime))) return rc; // Return result // return (Cks.fmTime != MTime || nFault || Cks.Length > XrdCksData::ValuSize || Cks.Length <= 0 ? -ESTALE : int(Cks.Length)); } /******************************************************************************/ /* L i s t */ /******************************************************************************/ char *XrdCksManager::List(const char *Pfn, char *Buff, int Blen, char Sep) { static const char *vPfx = "XrdCks."; static const int vPln = strlen(vPfx); XrdSysFAttr::AList *vP, *axP = 0; char *bP = Buff; int i, n; // Verify that the buffer is large enough // if (Blen < 2) return 0; // Check if the default list is wanted // if (!Pfn) {if (csLast < 0) return 0; i = 0; while(i <= csLast && Blen > 1) {n = strlen(csTab[i].Name); if (n >= Blen) break; if (bP != Buff) *bP++ = Sep; strcpy(bP, csTab[i].Name); bP += n; *bP = 0; } return (bP == Buff ? 0 : Buff); } // Get a list of attributes for this file // if (XrdSysFAttr::Xat->List(&axP, Pfn) < 0 || !axP) return 0; // Go through the list extracting what we are looking for // vP = axP; while(vP) {if (vP->Nlen > vPln && !strncmp(vP->Name, vPfx, vPln)) {n = vP->Nlen - vPln; if (n >= Blen) break; if (bP != Buff) *bP++ = Sep; strcpy(bP, vP->Name + vPln); bP += n; *bP = 0; } vP = vP->Next; } // All done // XrdSysFAttr::Xat->Free(axP); return (bP == Buff ? 0 : Buff); } /******************************************************************************/ /* M o d T i m e */ /******************************************************************************/ int XrdCksManager::ModTime(const char *Pfn, time_t &MTime) { struct stat Stat; if (stat(Pfn, &Stat)) return -errno; MTime = Stat.st_mtime; return 0; } /******************************************************************************/ /* N a m e */ /******************************************************************************/ const char *XrdCksManager::Name(int seqNum) { return (seqNum < 0 || seqNum > csLast ? 0 : csTab[seqNum].Name); } /******************************************************************************/ /* O b j e c t */ /******************************************************************************/ XrdCksCalc *XrdCksManager::Object(const char *name) { csInfo *csIP = &csTab[0]; // Return an object it at all possible // if (name && !(csIP = Find(name))) return 0; return csIP->Obj->New(); } /******************************************************************************/ /* S i z e */ /******************************************************************************/ int XrdCksManager::Size(const char *Name) { csInfo *iP = (Name != 0 ? Find(Name) : &csTab[0]); return (iP != 0 ? iP->Len : 0); } /******************************************************************************/ /* S e t */ /******************************************************************************/ int XrdCksManager::Set(const char *Pfn, XrdCksData &Cks, int myTime) { XrdOucXAttr xCS; csInfo *csIP = &csTab[0]; // Verify the incomming checksum for correctness // if (csLast < 0 || (*Cks.Name && !(csIP = Find(Cks.Name)))) return -ENOTSUP; if (Cks.Length != csIP->Len) return -EDOM; memcpy(&xCS.Attr.Cks, &Cks, sizeof(xCS.Attr.Cks)); // Set correct times if need be // if (!myTime) {time_t MTime; int rc = ModTime(Pfn, MTime); if (rc) return rc; xCS.Attr.Cks.fmTime = static_cast(MTime); xCS.Attr.Cks.csTime = static_cast(time(0) - MTime); } // Now set the checksum information in the extended attribute object // return xCS.Set(Pfn); } /******************************************************************************/ /* V e r */ /******************************************************************************/ int XrdCksManager::Ver(const char *Pfn, XrdCksData &Cks) { XrdOucXAttr xCS; time_t MTime; csInfo *csIP = &csTab[0]; int rc; // Determine which checksum to get // if (csLast < 0 || (*Cks.Name && !(csIP = Find(Cks.Name)))) return -ENOTSUP; xCS.Attr.Cks.Set(csIP->Name); // Verify the file // if ((rc = ModTime(Pfn, MTime))) return rc; // Retreive the attribute. Return upon fatal error. // if ((rc = xCS.Get(Pfn)) < 0) return rc; // Verify the checksum and see if we need to recalculate it // if (!rc || xCS.Attr.Cks.fmTime != MTime || strcmp(xCS.Attr.Cks.Name, csIP->Name) || xCS.Attr.Cks.Length != csIP->Len) {strcpy(xCS.Attr.Cks.Name, Cks.Name); if ((rc = Calc(Pfn, xCS.Attr.Cks, 1)) < 0) return rc; } // Compare the checksums // return (xCS.Attr.Cks.Length == Cks.Length && !memcmp(xCS.Attr.Cks.Value, Cks.Value, csIP->Len)); }