XRootD
XrdCksManager.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d O s s C k s M a n a g e r . c c */
4 /* */
5 /* (c) 2011 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 <cstring>
34 #include <ctime>
35 #include <cstdio>
36 #include <unistd.h>
37 #include <sys/mman.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 
41 #include "XrdCks/XrdCksCalc.hh"
45 #include "XrdCks/XrdCksCalcmd5.hh"
46 #include "XrdCks/XrdCksLoader.hh"
47 #include "XrdCks/XrdCksManager.hh"
48 #include "XrdCks/XrdCksXAttr.hh"
51 #include "XrdOuc/XrdOucUtils.hh"
52 #include "XrdOuc/XrdOucXAttr.hh"
53 #include "XrdSys/XrdSysError.hh"
54 #include "XrdSys/XrdSysFAttr.hh"
55 #include "XrdSys/XrdSysPlugin.hh"
56 #include "XrdSys/XrdSysPthread.hh"
57 
58 #ifndef ENOATTR
59 #define ENOATTR ENODATA
60 #endif
61 
62 /******************************************************************************/
63 /* C o n s t r u c t o r */
64 /******************************************************************************/
65 
66 XrdCksManager::XrdCksManager(XrdSysError *erP, int rdsz, XrdVersionInfo &vInfo,
67  bool autoload)
68  : XrdCks(erP), myVersion(vInfo)
69 {
70 
71 // Get a dynamic loader if so wanted
72 //
73  if (autoload) cksLoader = new XrdCksLoader(vInfo);
74  else cksLoader = 0;
75 
76 // Prefill the native digests we support
77 //
78  strcpy(csTab[0].Name, "adler32");
79  strcpy(csTab[1].Name, "crc32");
80  strcpy(csTab[2].Name, "crc32c");
81  strcpy(csTab[3].Name, "md5");
82  csLast = 3;
83 
84 // Compute the i/o size
85 //
86  if (rdsz <= 65536) segSize = 67108864;
87  else segSize = ((rdsz/65536) + (rdsz%65536 != 0)) * 65536;
88 }
89 
90 /******************************************************************************/
91 /* D e s t r u c t o r */
92 /******************************************************************************/
93 
95 {
96  int i;
97  for (i = 0; i <= csLast; i++)
98  {if (csTab[i].Obj && csTab[i].doDel) csTab[i].Obj->Recycle();
99  if (csTab[i].Path) free( csTab[i].Path);
100  if (csTab[i].Parms) free( csTab[i].Parms);
101  if (csTab[i].Plugin) delete csTab[i].Plugin;
102  }
103  if (cksLoader) delete cksLoader;
104 }
105 
106 /******************************************************************************/
107 /* C a l c */
108 /******************************************************************************/
109 
110 int XrdCksManager::Calc(const char *Pfn, XrdCksData &Cks, int doSet)
111 {
112  XrdCksCalc *csP;
113  csInfo *csIP = &csTab[0];
114  time_t MTime;
115  int rc;
116 
117 // Determine which checksum to get
118 //
119  if (csLast < 0) return -ENOTSUP;
120  if (!(*Cks.Name)) Cks.Set(csIP->Name);
121  else if (!(csIP = Find(Cks.Name))) return -ENOTSUP;
122 
123 // If we need not set the checksum then see if we can get it from the
124 // extended attributes.
125 
126 // Obtain a new checksum object
127 //
128  if (!(csP = csIP->Obj->New())) return -ENOMEM;
129 
130 // Use the calculator to get and possibly set the checksum
131 //
132  if (!(rc = Calc(Pfn, MTime, csP)))
133  {memcpy(Cks.Value, csP->Final(), csIP->Len);
134  Cks.fmTime = static_cast<long long>(MTime);
135  Cks.csTime = static_cast<int>(time(0) - MTime);
136  Cks.Length = csIP->Len;
137  csP->Recycle();
138  if (doSet)
140  memcpy(&xCS.Attr.Cks, &Cks, sizeof(xCS.Attr.Cks));
141  if ((rc = xCS.Set(Pfn))) return -rc;
142  }
143  }
144 
145 // All done
146 //
147  return rc;
148 }
149 
150 /******************************************************************************/
151 
152 int XrdCksManager::Calc(const char *Pfn, time_t &MTime, XrdCksCalc *csP)
153 {
154  class ioFD
155  {public:
156  int FD;
157  ioFD() : FD(-1) {}
158  ~ioFD() {if (FD >= 0) close(FD);}
159  } In;
160  struct stat Stat;
161  char *inBuff;
162  off_t Offset=0, fileSize;
163  size_t ioSize, calcSize;
164  int rc;
165 
166 // Open the input file
167 //
168  if ((In.FD = open(Pfn, O_RDONLY)) < 0) return -errno;
169 
170 // Get the file characteristics
171 //
172  if (fstat(In.FD, &Stat)) return -errno;
173  if (!(Stat.st_mode & S_IFREG)) return -EPERM;
174  calcSize = fileSize = Stat.st_size;
175  MTime = Stat.st_mtime;
176 
177 // We now compute checksum 64MB at a time using mmap I/O
178 //
179  ioSize = (fileSize < (off_t)segSize ? fileSize : segSize); rc = 0;
180  while(calcSize)
181  {if ((inBuff = (char *)mmap(0, ioSize, PROT_READ,
182 #if defined(__FreeBSD__)
183  MAP_RESERVED0040|MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED)
184 #elif defined(__GNU__)
185  MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED)
186 #else
187  MAP_NORESERVE|MAP_PRIVATE, In.FD, Offset)) == MAP_FAILED)
188 #endif
189  {rc = errno; eDest->Emsg("Cks", rc, "memory map", Pfn); break;}
190  madvise(inBuff, ioSize, MADV_SEQUENTIAL);
191  csP->Update(inBuff, ioSize);
192  calcSize -= ioSize; Offset += ioSize;
193  if (munmap(inBuff, ioSize) < 0)
194  {rc = errno; eDest->Emsg("Cks",rc,"unmap memory for",Pfn); break;}
195  if (calcSize < (size_t)segSize) ioSize = calcSize;
196  }
197 
198 // Return if we failed
199 //
200  if (calcSize) return (rc ? -rc : -EIO);
201  return 0;
202 }
203 
204 /******************************************************************************/
205 /* C o n f i g */
206 /******************************************************************************/
207 /*
208  Purpose: To parse the directive: ckslib <digest> <path> [<parms>]
209 
210  <digest> the name of the checksum.
211  <path> the path of the checksum library to be used.
212  <parms> optional parms to be passed
213 
214  Output: 0 upon success or !0 upon failure.
215 */
216 int XrdCksManager::Config(const char *Token, char *Line)
217 {
218  XrdOucTokenizer Cfg(Line);
219  char *val, *path = 0, name[XrdCksData::NameSize], *parms;
220  int i;
221 
222 // Get the the checksum name
223 //
224  Cfg.GetLine();
225  if (!(val = Cfg.GetToken()) || !val[0])
226  {eDest->Emsg("Config", "checksum name not specified"); return 1;}
227  if (int(strlen(val)) >= XrdCksData::NameSize)
228  {eDest->Emsg("Config", "checksum name too long"); return 1;}
229  strcpy(name, val); XrdOucUtils::toLower(name);
230 
231 // Get the path and optional parameters
232 //
233  val = Cfg.GetToken(&parms);
234  if (val && val[0]) path = strdup(val);
235  else {eDest->Emsg("Config","library path missing for ckslib digest",name);
236  return 1;
237  }
238 
239 // Check if this replaces an existing checksum
240 //
241  for (i = 0; i < csMax; i++)
242  if (!(*csTab[i].Name) || !strcmp(csTab[i].Name, name)) break;
243 
244 // See if we can insert a new checksum (or replace one)
245 //
246  if (i >= csMax)
247  {eDest->Emsg("Config", "too many checksums specified");
248  if (path) free(path);
249  return 1;
250  } else if (!(*csTab[i].Name)) csLast = i;
251 
252 // Insert the new checksum
253 //
254  strcpy(csTab[i].Name, name);
255  if (csTab[i].Path) free(csTab[i].Path);
256  csTab[i].Path = path;
257  if (csTab[i].Parms) free(csTab[i].Parms);
258  csTab[i].Parms = (parms && *parms ? strdup(parms) : 0);
259 
260 // All done
261 //
262  return 0;
263 }
264 
265 /******************************************************************************/
266 /* I n i t */
267 /******************************************************************************/
268 
269 int XrdCksManager::Init(const char *ConfigFN, const char *DfltCalc)
270 {
271  int i;
272 
273 // See if we need to set the default calculation
274 //
275  if (DfltCalc)
276  {for (i = 0; i < csLast; i++) if (!strcmp(csTab[i].Name, DfltCalc)) break;
277  if (i >= csMax)
278  {eDest->Emsg("Config", DfltCalc, "cannot be made the default; "
279  "not supported.");
280  return 0;
281  }
282  if (i) {csInfo Temp = csTab[i]; csTab[i] = csTab[0]; csTab[0] = Temp;}
283  }
284 
285 // See if there are any checksums to configure
286 //
287  if (csLast < 0)
288  {eDest->Emsg("Config", "No checksums defined; cannot configure!");
289  return 0;
290  }
291 
292 // Complete the checksum table
293 //
294  for (i = 0; i <= csLast; i++)
295  {if (csTab[i].Path) {if (!(Config(ConfigFN, csTab[i]))) return 0;}
296  else { if (!strcmp("adler32", csTab[i].Name))
297  csTab[i].Obj = new XrdCksCalcadler32;
298  else if (!strcmp("crc32", csTab[i].Name))
299  csTab[i].Obj = new XrdCksCalccrc32;
300  else if (!strcmp("crc32c", csTab[i].Name))
301  csTab[i].Obj = new XrdCksCalccrc32C;
302  else if (!strcmp("md5", csTab[i].Name))
303  csTab[i].Obj = new XrdCksCalcmd5;
304  else {eDest->Emsg("Config", "Invalid native checksum -",
305  csTab[i].Name);
306  return 0;
307  }
308  csTab[i].Obj->Type(csTab[i].Len);
309  }
310  }
311 
312 // All done
313 //
314  return 1;
315 }
316 
317 /******************************************************************************/
318 
319 #define XRDOSSCKSLIBARGS XrdSysError *, const char *, const char *, const char *
320 
321 int XrdCksManager::Config(const char *cFN, csInfo &Info)
322 {
323  XrdOucPinLoader myPin(eDest, &myVersion, "ckslib", Info.Path);
325  int n;
326 
327 // Find the entry point
328 //
329  Info.Plugin = 0;
330  if (!(ep = (XrdCksCalc *(*)(XRDOSSCKSLIBARGS))
331  (myPin.Resolve("XrdCksCalcInit"))))
332  {eDest->Emsg("Config", "Unable to configure cksum", Info.Name);
333  myPin.Unload();
334  return 0;
335  }
336 
337 // Get the initial object
338 //
339  if (!(Info.Obj = ep(eDest,cFN,Info.Name,(Info.Parms ? Info.Parms : ""))))
340  {eDest->Emsg("Config", Info.Name, "checksum initialization failed");
341  myPin.Unload();
342  return 0;
343  }
344 
345 // Verify the object
346 //
347  if (strcmp(Info.Name, Info.Obj->Type(n)))
348  {eDest->Emsg("Config",Info.Name,"cksum plugin returned wrong name -",
349  Info.Obj->Type(n));
350  myPin.Unload();
351  return 0;
352  }
353  if (n > XrdCksData::ValuSize || n <= 0)
354  {eDest->Emsg("Config",Info.Name,"cksum plugin has an unsupported "
355  "checksum length");
356  myPin.Unload();
357  return 0;
358  }
359 
360 // All is well
361 //
362  Info.Plugin = myPin.Export();
363  Info.Len = n;
364  return 1;
365 }
366 
367 /******************************************************************************/
368 /* F i n d */
369 /******************************************************************************/
370 
371 XrdCksManager::csInfo *XrdCksManager::Find(const char *Name)
372 {
373  static XrdSysMutex myMutex;
374  XrdCksCalc *myCalc;
375  int i;
376 
377 // Find the pre-loaded checksum
378 //
379  for (i = 0; i <= csLast; i++)
380  if (!strcmp(Name, csTab[i].Name)) return &csTab[i];
381 
382 // If we have loader see if we can auto-load this object
383 //
384  if (!cksLoader) return 0;
385  myMutex.Lock();
386 
387 // An entry could have been added as we were running unlocked
388 //
389  for (i = 0; i <= csLast; i++)
390  if (!strcmp(Name, csTab[i].Name))
391  {myMutex.UnLock();
392  return &csTab[i];
393  }
394 
395 // Check if we have room in the table
396 //
397  if (csLast >= csMax)
398  {myMutex.UnLock();
399  eDest->Emsg("CksMan","Unable to load",Name,"; checksum limit reached.");
400  return 0;
401  }
402 
403 // Attempt to dynamically load this object
404 //
405 { char buff[2048];
406  *buff = 0;
407  if (!(myCalc = cksLoader->Load(Name, 0, buff, sizeof(buff), true)))
408  {myMutex.UnLock();
409  eDest->Emsg("CksMan", "Unable to load", Name);
410  if (*buff) eDest->Emsg("CksMan", buff);
411  return 0;
412  }
413 }
414 
415 // Fill out the table
416 //
417  i = csLast + 1;
418  strncpy(csTab[i].Name, Name, XrdCksData::NameSize);
419  csTab[i].Obj = myCalc;
420  csTab[i].Path = 0;
421  csTab[i].Parms = 0;
422  csTab[i].Plugin = 0;
423  csTab[i].doDel = false;
424  myCalc->Type(csTab[i].Len);
425 
426 // Return the result
427 //
428  csLast = i;
429  myMutex.UnLock();
430  return &csTab[i];
431 }
432 
433 /******************************************************************************/
434 /* D e l */
435 /******************************************************************************/
436 
437 int XrdCksManager::Del(const char *Pfn, XrdCksData &Cks)
438 {
440 
441 // Set the checksum name
442 //
443  xCS.Attr.Cks.Set(Cks.Name);
444 
445 // Delete the attribute and return the result
446 //
447  return xCS.Del(Pfn);
448 }
449 
450 /******************************************************************************/
451 /* G e t */
452 /******************************************************************************/
453 
454 int XrdCksManager::Get(const char *Pfn, XrdCksData &Cks)
455 {
457  time_t MTime;
458  int rc, nFault;
459 
460 // Determine which checksum to get (we will accept unsupported ones as well)
461 //
462  if (csLast < 0) return -ENOTSUP;
463  if (!*Cks.Name) Cks.Set(csTab[0].Name);
464  if (!xCS.Attr.Cks.Set(Cks.Name)) return -ENOTSUP;
465 
466 // Retreive the attribute
467 //
468  if ((rc = xCS.Get(Pfn)) <= 0) return (rc && rc != -ENOATTR ? rc : -ESRCH);
469 
470 // Mark state of the name and copy the attribute over
471 //
472  nFault = strcmp(xCS.Attr.Cks.Name, Cks.Name);
473  Cks = xCS.Attr.Cks;
474 
475 // Verify the file
476 //
477  if ((rc = ModTime(Pfn, MTime))) return rc;
478 
479 // Return result
480 //
481  return (Cks.fmTime != MTime || nFault
482  || Cks.Length > XrdCksData::ValuSize || Cks.Length <= 0
483  ? -ESTALE : int(Cks.Length));
484 }
485 
486 /******************************************************************************/
487 /* L i s t */
488 /******************************************************************************/
489 
490 char *XrdCksManager::List(const char *Pfn, char *Buff, int Blen, char Sep)
491 {
492  static const char *vPfx = "XrdCks.";
493  static const int vPln = strlen(vPfx);
494  XrdSysFAttr::AList *vP, *axP = 0;
495  char *bP = Buff;
496  int i, n;
497 
498 // Verify that the buffer is large enough
499 //
500  if (Blen < 2) return 0;
501 
502 // Check if the default list is wanted
503 //
504  if (!Pfn)
505  {if (csLast < 0) return 0;
506  i = 0;
507  while(i <= csLast && Blen > 1)
508  {n = strlen(csTab[i].Name);
509  if (n >= Blen) break;
510  if (bP != Buff) *bP++ = Sep;
511  strcpy(bP, csTab[i].Name); bP += n; *bP = 0;
512  }
513  return (bP == Buff ? 0 : Buff);
514  }
515 
516 // Get a list of attributes for this file
517 //
518  if (XrdSysFAttr::Xat->List(&axP, Pfn) < 0 || !axP) return 0;
519 
520 // Go through the list extracting what we are looking for
521 //
522  vP = axP;
523  while(vP)
524  {if (vP->Nlen > vPln && !strncmp(vP->Name, vPfx, vPln))
525  {n = vP->Nlen - vPln;
526  if (n >= Blen) break;
527  if (bP != Buff) *bP++ = Sep;
528  strcpy(bP, vP->Name + vPln); bP += n; *bP = 0;
529  }
530  vP = vP->Next;
531  }
532 
533 // All done
534 //
535  XrdSysFAttr::Xat->Free(axP);
536  return (bP == Buff ? 0 : Buff);
537 }
538 
539 /******************************************************************************/
540 /* M o d T i m e */
541 /******************************************************************************/
542 
543 int XrdCksManager::ModTime(const char *Pfn, time_t &MTime)
544 {
545  struct stat Stat;
546 
547  if (stat(Pfn, &Stat)) return -errno;
548 
549  MTime = Stat.st_mtime;
550  return 0;
551 }
552 
553 /******************************************************************************/
554 /* N a m e */
555 /******************************************************************************/
556 
557 const char *XrdCksManager::Name(int seqNum)
558 {
559 
560  return (seqNum < 0 || seqNum > csLast ? 0 : csTab[seqNum].Name);
561 }
562 
563 /******************************************************************************/
564 /* O b j e c t */
565 /******************************************************************************/
566 
568 {
569  csInfo *csIP = &csTab[0];
570 
571 // Return an object it at all possible
572 //
573  if (name && !(csIP = Find(name))) return 0;
574  return csIP->Obj->New();
575 }
576 
577 /******************************************************************************/
578 /* S i z e */
579 /******************************************************************************/
580 
581 int XrdCksManager::Size(const char *Name)
582 {
583  csInfo *iP = (Name != 0 ? Find(Name) : &csTab[0]);
584  return (iP != 0 ? iP->Len : 0);
585 }
586 
587 /******************************************************************************/
588 /* S e t */
589 /******************************************************************************/
590 
591 int XrdCksManager::Set(const char *Pfn, XrdCksData &Cks, int myTime)
592 {
594  csInfo *csIP = &csTab[0];
595 
596 // Verify the incoming checksum for correctness
597 //
598  if (csLast < 0 || (*Cks.Name && !(csIP = Find(Cks.Name)))) return -ENOTSUP;
599  if (Cks.Length != csIP->Len) return -EDOM;
600  memcpy(&xCS.Attr.Cks, &Cks, sizeof(xCS.Attr.Cks));
601 
602 // Set correct times if need be
603 //
604  if (!myTime)
605  {time_t MTime;
606  int rc = ModTime(Pfn, MTime);
607  if (rc) return rc;
608  xCS.Attr.Cks.fmTime = static_cast<long long>(MTime);
609  xCS.Attr.Cks.csTime = static_cast<int>(time(0) - MTime);
610  }
611 
612 // Now set the checksum information in the extended attribute object
613 //
614  return xCS.Set(Pfn);
615 }
616 
617 /******************************************************************************/
618 /* V e r */
619 /******************************************************************************/
620 
621 int XrdCksManager::Ver(const char *Pfn, XrdCksData &Cks)
622 {
624  time_t MTime;
625  csInfo *csIP = &csTab[0];
626  int rc;
627 
628 // Determine which checksum to get
629 //
630  if (csLast < 0 || (*Cks.Name && !(csIP = Find(Cks.Name)))) return -ENOTSUP;
631  xCS.Attr.Cks.Set(csIP->Name);
632 
633 // Verify the file
634 //
635  if ((rc = ModTime(Pfn, MTime))) return rc;
636 
637 // Retreive the attribute. Return upon fatal error.
638 //
639  if ((rc = xCS.Get(Pfn)) < 0) return rc;
640 
641 // Verify the checksum and see if we need to recalculate it
642 //
643  if (!rc || xCS.Attr.Cks.fmTime != MTime
644  || strcmp(xCS.Attr.Cks.Name, csIP->Name)
645  || xCS.Attr.Cks.Length != csIP->Len)
646  {strcpy(xCS.Attr.Cks.Name, Cks.Name);
647  if ((rc = Calc(Pfn, xCS.Attr.Cks, 1)) < 0) return rc;
648  }
649 
650 // Compare the checksums
651 //
652  return (xCS.Attr.Cks.Length == Cks.Length
653  && !memcmp(xCS.Attr.Cks.Value, Cks.Value, csIP->Len));
654 }
#define ENOATTR
#define XRDOSSCKSLIBARGS
struct stat Stat
Definition: XrdCks.cc:49
XrdOucXAttr< XrdCksXAttr > xCS
Definition: XrdCks.cc:48
@ Info
int stat(const char *path, struct stat *buf)
int open(const char *path, int oflag,...)
int fstat(int fildes, struct stat *buf)
#define close(a)
Definition: XrdPosix.hh:43
XrdOucString Path
virtual char * Final()=0
virtual void Update(const char *Buff, int BLen)=0
virtual const char * Type(int &csSize)=0
virtual void Recycle()
Recycle the checksum object as it is no longer needed. A default is given.
Definition: XrdCksCalc.hh:95
virtual XrdCksCalc * New()=0
char Value[ValuSize]
Definition: XrdCksData.hh:53
static const int ValuSize
Definition: XrdCksData.hh:42
int Set(const char *csName)
Definition: XrdCksData.hh:81
char Length
Definition: XrdCksData.hh:52
static const int NameSize
Definition: XrdCksData.hh:41
char Name[NameSize]
Definition: XrdCksData.hh:44
XrdCksCalc * Load(const char *csName, const char *csParms=0, char *eBuff=0, int eBlen=0, bool orig=false)
virtual int Config(const char *Token, char *Line)
virtual int Get(const char *Pfn, XrdCksData &Cks)
virtual int Del(const char *Pfn, XrdCksData &Cks)
virtual int Size(const char *Name=0)
virtual const char * Name(int seqNum=0)
virtual ~XrdCksManager()
XrdCksManager(XrdSysError *erP, int iosz, XrdVersionInfo &vInfo, bool autoload=false)
virtual int Calc(const char *Pfn, XrdCksData &Cks, int doSet=1)
virtual int ModTime(const char *Pfn, time_t &MTime)
virtual char * List(const char *Pfn, char *Buff, int Blen, char Sep=' ')
virtual XrdCksCalc * Object(const char *name)
virtual int Ver(const char *Pfn, XrdCksData &Cks)
virtual int Set(const char *Pfn, XrdCksData &Cks, int myTime=0)
virtual int Init(const char *ConfigFN, const char *AddCalc=0)
Definition: XrdCks.hh:92
XrdSysError * eDest
Definition: XrdCks.hh:289
XrdSysPlugin * Export()
void * Resolve(const char *symbl, int mcnt=1)
void Unload(bool dodel=false)
static void toLower(char *str)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
static XrdSysXAttr * Xat
Definition: XrdSysFAttr.hh:51
char Name[1]
Start of the name (size of struct is dynamic)
Definition: XrdSysXAttr.hh:56
int Nlen
The length of the attribute name that follows.
Definition: XrdSysXAttr.hh:55
virtual void Free(AList *aPL)=0
AList * Next
-> next element.
Definition: XrdSysXAttr.hh:53
XrdVersionInfo myVersion