/******************************************************************************/ /* */ /* X r d S u t P F i l e . c c */ /* */ /* (c) 2012 by the Board of Trustees of the Leland Stanford, Jr., University */ /* Produced by Gerri Ganis for CERN */ /* */ /* 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 "XrdSut/XrdSutAux.hh" #include "XrdSut/XrdSutPFEntry.hh" #include "XrdSut/XrdSutPFile.hh" #include "XrdSut/XrdSutTrace.hh" //_________________________________________________________________ XrdSutPFEntInd::XrdSutPFEntInd(const char *n, kXR_int32 no, kXR_int32 eo, kXR_int32 es) { // Constructor name = 0; if (n) { name = new char[strlen(n)+1]; if (name) strcpy(name,n); } nxtofs = no; entofs = eo; entsiz = es; } //_________________________________________________________________ XrdSutPFEntInd::XrdSutPFEntInd(const XrdSutPFEntInd &ei) { //Copy constructor name = 0; if (ei.name) { name = new char[strlen(ei.name)+1]; if (name) strcpy(name,ei.name); } nxtofs = ei.nxtofs; entofs = ei.entofs; entsiz = ei.entsiz; } //_________________________________________________________________ void XrdSutPFEntInd::SetName(const char *n) { // Name setter if (name) { delete[] name; name = 0; } if (n) { name = new char[strlen(n)+1]; if (name) strcpy(name,n); } } //______________________________________________________________________________ XrdSutPFEntInd& XrdSutPFEntInd::operator=(const XrdSutPFEntInd ei) { // Assign index entry ei to local index entry. name = 0; if (ei.name) { name = new char[strlen(ei.name)+1]; if (name) strcpy(name,ei.name); } nxtofs = ei.nxtofs; entofs = ei.entofs; entsiz = ei.entsiz; return *this; } //_________________________________________________________________ XrdSutPFHeader::XrdSutPFHeader(const char *id, kXR_int32 v, kXR_int32 ct, kXR_int32 it, kXR_int32 ent, kXR_int32 ofs) { // Constructor memset(fileID,0,kFileIDSize); if (id) { kXR_int32 lid = strlen(id); if (lid > kFileIDSize) lid = kFileIDSize; memcpy(fileID,id,lid); } version = v; ctime = ct; itime = it; entries = ent; indofs = ofs; jnksiz = 0; // At start everything is reachable } //_________________________________________________________________ XrdSutPFHeader::XrdSutPFHeader(const XrdSutPFHeader &fh) { // Copy constructor memcpy(fileID,fh.fileID,kFileIDSize); version = fh.version; ctime = fh.ctime; itime = fh.itime; entries = fh.entries; indofs = fh.indofs; jnksiz = fh.jnksiz; } //_________________________________________________________________ void XrdSutPFHeader::Print() const { // Header printout struct tm tst; // String form for time of last change char sctime[256] = {0}; time_t ttmp = ctime; localtime_r(&ttmp,&tst); asctime_r(&tst,sctime); sctime[strlen(sctime)-1] = 0; // String form for time of last index change char sitime[256] = {0}; ttmp = itime; localtime_r(&ttmp,&tst); asctime_r(&tst,sitime); sitime[strlen(sitime)-1] = 0; fprintf(stdout, "//------------------------------------" "------------------------------//\n" "// \n" "// File Header dump \n" "// \n" "// File ID: %s \n" "// version: %d \n" "// last changed on: %s (%d sec) \n" "// index changed on: %s (%d sec) \n" "// entries: %d \n" "// unreachable: %d \n" "// first ofs: %d \n" "// \n" "//------------------------------------" "------------------------------//\n", fileID,version,sctime,ctime,sitime,itime,entries,jnksiz,indofs); } //________________________________________________________________ XrdSutPFile::XrdSutPFile(const char *n, kXR_int32 openmode, kXR_int32 createmode, bool hashtab) { // Constructor name = 0; if (n) { name = new char[strlen(n)+1]; if (name) strcpy(name,n); } valid = 0; fFd = -1; fHTutime = -1; fHashTable = 0; valid = Init(n, openmode, createmode, hashtab); } //________________________________________________________________ XrdSutPFile::XrdSutPFile(const XrdSutPFile &f) { // Copy constructor name = 0; if (f.name) { name = new char[strlen(f.name)+1]; if (name) strcpy(name,f.name); } fFd = f.fFd ; } //________________________________________________________________ XrdSutPFile::~XrdSutPFile() { // Destructor if (name) delete[] name; name = 0; if (fHashTable) delete fHashTable; fHashTable = 0; Close(); } //________________________________________________________________ bool XrdSutPFile::Init(const char *n, kXR_int32 openmode, kXR_int32 createmode, bool hashtab) { // (re)initialize PFile // Make sure it is closed Close(); // Reset members if (name) delete[] name; name = 0; if (n) { name = new char[strlen(n)+1]; if (name) strcpy(name,n); } valid = 0; fFd = -1; fHTutime = -1; if (fHashTable) delete fHashTable; fHashTable = 0; // If name is missing nothing can be done if (!name) return 0; // open modes bool create = (openmode & kPFEcreate); bool leaveopen = (openmode & kPFEopen); // If file does not exists, create it with default header struct stat st; if (stat(name, &st) == -1) { if (errno == ENOENT) { if (create) { if (Open(1,0,0,createmode) > 0) { kXR_int32 ct = (kXR_int32)time(0); XrdSutPFHeader hdr(kDefFileID,kXrdIFVersion,ct,ct,0,0); WriteHeader(hdr); valid = 1; if (!leaveopen) Close(); } } else { Err(kPFErrNoFile,"Init",name); } } } else { // Fill the the hash table if (Open(1) > 0) { if (hashtab) UpdateHashTable(); valid = 1; if (!leaveopen) Close(); } } // We are done return valid; } //_________________________________________________________________ kXR_int32 XrdSutPFile::Open(kXR_int32 opt, bool *wasopen, const char *nam, kXR_int32 createmode) { // Open the stream, so defining fFd . // Valid options: // 0 read only // 1 read/write append // 2 read/write truncate // For options 1 and 2 the file is created, if not existing, // and permission set to createmode (default: 0600). // If the file name ends with 'XXXXXX' and it does not exist, // it is created as temporary using mkstemp. // The file is also exclusively locked. // If nam is defined it is used as file name // If the file is already open and wasopen is allocated, then *wasopen // is set to true // The file descriptor of the open file is returned XrdOucString copt(opt); // Reset was open flag if (wasopen) *wasopen = 0; // File name must be defined char *fnam = (char *)nam; if (!fnam) fnam = name; if (!fnam) return Err(kPFErrBadInputs,"Open"); // If already open, do nothing if (!nam && fFd > -1) { if (opt > 0) { // Make sure that the write flag is set long omode = 0; if (fcntl(fFd, F_GETFL, &omode) != -1) { if (!(omode | O_WRONLY)) return Err(kPFErrFileAlreadyOpen,"Open"); } } if (wasopen) *wasopen = 1; return fFd; } // Ok, we have a file name ... check if it exists already bool newfile = 0; struct stat st; if (stat(fnam, &st) == -1) { if (errno != ENOENT) { return Err(kPFErrNoFile,"Open",fnam); } else { if (opt == 0) return Err(kPFErrStat,"Open",fnam); newfile = 1; } } // Now open it if (!nam) fFd = -1; kXR_int32 fd = -1; // // If we have to create a new file and the file name ends with // 'XXXXXX', make it temporary with mkstemp char *pn = strstr(fnam,"XXXXXX"); if (pn && (pn == (fnam + strlen(fnam) - 6))) { if (opt > 0 && newfile) { fd = mkstemp(fnam); if (fd <= -1) return Err(kPFErrFileOpen,"Open",fnam); } } // // If normal file act according to requests if (fd <= -1) { kXR_int32 mode = 0; switch (opt) { case 2: // // Forcing truncation in Read / Write mode mode |= (O_TRUNC | O_RDWR) ; if (newfile) mode |= O_CREAT ; break; case 1: // // Read / Write mode |= O_RDWR ; if (newfile) mode |= O_CREAT ; break; case 0: // // Read only mode = O_RDONLY ; break; default: // // Unknown option return Err(kPFErrBadOp,"Open",copt.c_str()); } // Open file (createmode is only used if O_CREAT is set) fd = open(fnam, mode, createmode); if (fd <= -1) return Err(kPFErrFileOpen,"Open",fnam); } // // Shared or exclusive lock of the whole file int lockmode = (opt > 0) ? (F_WRLCK | F_RDLCK) : F_RDLCK; int lck = kMaxLockTries; int rc = 0; while (lck && rc == -1) { struct flock flck; memset(&flck, 0, sizeof(flck)); flck.l_type = lockmode; flck.l_whence = SEEK_SET; if ((rc = fcntl(fd, F_SETLK, &flck)) == 0) break; struct timespec lftp, rqtp = {1, 0}; while (nanosleep(&rqtp, &lftp) < 0 && errno == EINTR) { rqtp.tv_sec = lftp.tv_sec; rqtp.tv_nsec = lftp.tv_nsec; } } if (rc == -1) { if (errno == EACCES || errno == EAGAIN) { // File locked by other process int pid = -1; struct flock flck; memset(&flck, 0, sizeof(flck)); flck.l_type = lockmode; flck.l_whence = SEEK_SET; if (fcntl(fd,F_GETLK,&flck) != -1) pid = flck.l_pid; close(fd); return Err(kPFErrFileLocked,"Open",fnam,(const char *)&pid); } else { // Error return Err(kPFErrLocking,"Open",fnam,(const char *)&fd); } } // Ok, we got the file open and locked if (!nam) fFd = fd; return fd; } //_________________________________________________________________ kXR_int32 XrdSutPFile::Close(kXR_int32 fd) { // Close the open stream or descriptor fd, if > -1 . // The file is unlocked before. // If not open, do nothing if (fd < 0) fd = fFd; if (fd < 0) return 0; // // Unlock the file struct flock flck; memset(&flck, 0, sizeof(flck)); flck.l_type = F_UNLCK; flck.l_whence = SEEK_SET; if (fcntl(fd, F_SETLK, &flck) == -1) { close(fd); return Err(kPFErrUnlocking,"Close",(const char *)&fd); } // // Close it close(fd); // Reset file descriptor if (fd == fFd) fFd = -1; return 0; } //_________________________________________________________________ kXR_int32 XrdSutPFile::UpdateHeader(XrdSutPFHeader hd) { // Write/Update header to beginning of file // // Open the file if (Open(1) < 0) return -1; // Write kXR_int32 nw = WriteHeader(hd); // Close the file Close(); return nw; } //_________________________________________________________________ kXR_int32 XrdSutPFile::RetrieveHeader(XrdSutPFHeader &hd) { // Retrieve number of entries in the file // // Open the file bool wasopen = 0; if (Open(1, &wasopen) < 0) return -1; // Read header kXR_int32 rc = ReadHeader(hd); // Close the file if (!wasopen) Close(); return rc; } //_________________________________________________________________ kXR_int32 XrdSutPFile::WriteHeader(XrdSutPFHeader hd) { // Write/Update header to beginning of opne stream // // Build output buffer // Get total lenght needed kXR_int32 ltot = hd.Length(); // // Allocate the buffer char *bout = new char[ltot]; if (!bout) return Err(kPFErrOutOfMemory,"WriteHeader"); // // Fill the buffer kXR_int32 lp = 0; // File ID memcpy(bout+lp,hd.fileID,kFileIDSize); lp += kFileIDSize; // version memcpy(bout+lp,&hd.version,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // change time memcpy(bout+lp,&hd.ctime,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // index change time memcpy(bout+lp,&hd.itime,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // entries memcpy(bout+lp,&hd.entries,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // offset of the first index entry memcpy(bout+lp,&hd.indofs,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // number of unused bytes memcpy(bout+lp,&hd.jnksiz,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // Check length if (lp != ltot) { if (bout) delete[] bout; return Err(kPFErrLenMismatch,"WriteHeader", (const char *)&lp, (const char *)<ot); } // // Ready to write: check we got the file if (fFd < 0) return Err(kPFErrFileNotOpen,"WriteHeader"); // // Set the offset if (lseek(fFd, 0, SEEK_SET) == -1) { return Err(kPFErrSeek,"WriteHeader","SEEK_SET",(const char *)&fFd); } kXR_int32 nw = 0; // Now write the buffer to the stream while ((nw = write(fFd, bout, ltot)) < 0 && errno == EINTR) errno = 0; return nw; } //______________________________________________________________________ kXR_int32 XrdSutPFile::WriteEntry(XrdSutPFEntry ent) { // Write entry to file // Look first if an entry with the same name exists: in such // case try to overwrite the allocated file region; if the space // is not enough, set the existing entry inactive and write // the new entry at the end of the file, updating all the // pointers. // File must be opened in read/write mode (O_RDWR). // Make sure that the entry is named (otherwise we can't do nothing) if (!ent.name) return Err(kPFErrBadInputs,"WriteEntry"); // // Ready to write: open the file bool wasopen = 0; if (Open(1, &wasopen) < 0) return -1; kXR_int32 ofs = 0; kXR_int32 nw = 0; kXR_int32 indofs = 0; // Read the header XrdSutPFHeader header; if (ReadHeader(header) < 0) { if (!wasopen) Close(); return -1; } if ((ofs = lseek(fFd, 0, SEEK_CUR)) == -1) { if (!wasopen) Close(); return Err(kPFErrSeek,"WriteEntry","SEEK_CUR",(const char *)&fFd); } XrdSutPFEntInd ind; // If first entry, write it, update the info and return if (header.entries == 0) { if ((nw = WriteEnt(ofs, ent)) < 0) { if (!wasopen) Close(); return -1; } ind.SetName(ent.name); ind.nxtofs = 0; ind.entofs = ofs; ind.entsiz = nw; indofs = ofs + nw; if (WriteInd(indofs, ind) < 0) { if (!wasopen) Close(); return -1; } // Update header header.entries = 1; header.indofs = indofs; header.ctime = time(0); header.itime = header.ctime; if (WriteHeader(header) < 0) { if (!wasopen) Close(); return -1; } if (!wasopen) Close(); return nw; } // First Localize existing entry, if any kXR_int32 nr = 1; bool found = 0; indofs = header.indofs; kXR_int32 lastindofs = indofs; while (!found && nr > 0 && indofs > 0) { nr = ReadInd(indofs, ind); if (nr) { if (ind.entofs > 0 && !strcmp(ent.name,ind.name)) { found = 1; break; } lastindofs = indofs; indofs = ind.nxtofs; } } // // If an entry already exists and there is enough space to // store the update, write the update at the already allocated // space; if not, add it at the end. if (found) { // Update kXR_int32 ct = 0; if (ind.entsiz >= ent.Length()) { // The offset is set inside ... if ((nw = WriteEnt(ind.entofs, ent)) < 0) { if (!wasopen) Close(); return -1; } } else { // Add it at the end kXR_int32 entofs = 0; if ((entofs = lseek(fFd, 0, SEEK_END)) == -1) { if (!wasopen) Close(); return Err(kPFErrSeek,"WriteEntry", "SEEK_END",(const char *)&fFd); } if ((nw = WriteEnt(entofs, ent)) < 0) { if (!wasopen) Close(); return -1; } // Set existing entry inactive kXR_int32 wrtofs = ind.entofs; if (lseek(fFd, wrtofs, SEEK_SET) == -1) { if (!wasopen) Close(); return Err(kPFErrSeek,"WriteEntry", "SEEK_SET",(const char *)&fFd); } short status = kPFE_inactive; while (write(fFd, &status, sizeof(short)) < 0 && errno == EINTR) errno = 0; // Reset entry area if (Reset(wrtofs + sizeof(short), ind.entsiz - sizeof(short)) < 0) { if (!wasopen) Close(); return -1; } // Count as unused bytes header.jnksiz += ind.entsiz; if (lseek(fFd, kOfsJnkSiz, SEEK_SET) == -1) { if (!wasopen) Close(); return Err(kPFErrSeek,"WriteEntry", "SEEK_SET",(const char *)&fFd); } while (write(fFd, &header.jnksiz, sizeof(kXR_int32)) < 0 && errno == EINTR) errno = 0; // Update the entry index and new size wrtofs = indofs + 2*sizeof(kXR_int32); if (lseek(fFd, wrtofs, SEEK_SET) == -1) { if (!wasopen) Close(); return Err(kPFErrSeek,"WriteEntry", "SEEK_SET",(const char *)&fFd); } while (write(fFd, &entofs, sizeof(kXR_int32)) < 0 && errno == EINTR) errno = 0; while (write(fFd, &nw, sizeof(kXR_int32)) < 0 && errno == EINTR) errno = 0; // Update time of change of index ct = (kXR_int32)time(0); header.itime = ct; if (lseek(fFd, kOfsItime, SEEK_SET) == -1) { if (!wasopen) Close(); return Err(kPFErrSeek,"WriteEntry", "SEEK_SET",(const char *)&fFd); } while (write(fFd, &header.itime, sizeof(kXR_int32)) < 0 && errno == EINTR) errno = 0; } // Update time of change in header header.ctime = (ct > 0) ? ct : time(0); if (lseek(fFd, kOfsCtime, SEEK_SET) == -1) { if (!wasopen) Close(); return Err(kPFErrSeek,"WriteEntry", "SEEK_SET",(const char *)&fFd); } while (write(fFd, &header.ctime, sizeof(kXR_int32)) < 0 && errno == EINTR) errno = 0; if (!wasopen) Close(); return nw; } // // If new name, add the entry at the end if ((ofs = lseek(fFd, 0, SEEK_END)) == -1) { if (!wasopen) Close(); return Err(kPFErrSeek,"WriteEntry", "SEEK_END",(const char *)&fFd); } if ((nw = WriteEnt(ofs, ent)) < 0) { if (!wasopen) Close(); return -1; } // // Create new index entry XrdSutPFEntInd newind(ent.name, 0, ofs, nw); if (WriteInd(ofs+nw, newind) < 0) { if (!wasopen) Close(); return -1; } // // Update previous index entry ind.nxtofs = ofs + nw; kXR_int32 wrtofs = lastindofs + sizeof(kXR_int32); if (lseek(fFd, wrtofs, SEEK_SET) == -1) { if (!wasopen) Close(); return Err(kPFErrSeek,"WriteEntry", "SEEK_SET",(const char *)&fFd); } while (write(fFd, &ind.nxtofs, sizeof(kXR_int32)) < 0 && errno == EINTR) errno = 0; // Update header header.entries += 1; header.ctime = time(0); header.itime = header.ctime; if (WriteHeader(header) < 0) { if (!wasopen) Close(); return -1; } // Close the file if (!wasopen) Close(); return nw; } //________________________________________________________________ kXR_int32 XrdSutPFile::UpdateCount(const char *tag, int *cnt, int step, bool reset) { // Update counter for entry with 'tag', if any. // If reset is true, counter is firts reset. // The counter is updated by 'step'. // Default: no reset, increase by 1. // If cnt is defined, fill it with the updated counter. // Returns 0 or -1 in case of error // Make sure that we got a tag (otherwise we can't do nothing) if (!tag) return Err(kPFErrBadInputs,"UpdateCount"); // Make sure we got an open stream if (Open(1) < 0) return -1; // Read the header XrdSutPFHeader header; if (ReadHeader(header) < 0) { Close(); return -1; } // Check if the HashTable needs to be updated if (fHashTable && header.itime > fHTutime) { // Update the table if (UpdateHashTable() < 0) { Close(); return -1; } } // // Get index entry associated with tag, if any XrdSutPFEntInd ind; bool found = 0; if (fHashTable) { kXR_int32 *refofs = fHashTable->Find(tag); if (*refofs > 0) { // Read it out if (ReadInd(*refofs, ind) < 0) { Close(); return -1; } found = 1; } } else { // Get offset of the first index entry kXR_int32 indofs = header.indofs; while (indofs > 0) { // Read it out if (ReadInd(indofs, ind) < 0) { Close(); return -1; } // Check compatibility if (strlen(ind.name) == strlen(tag)) { if (!strncmp(ind.name,tag,strlen(tag))) { found = 1; break; } } // Next index entry indofs = ind.nxtofs; } } // // Read the entry, if found XrdSutPFEntry ent; bool changed = 0; if (found) { // Read entry if active if (ind.entofs) { if (ReadEnt(ind.entofs, ent) < 0) { Close(); return -1; } // // Reset counter if required if (reset && ent.cnt != 0) { changed = 1; ent.cnt = 0; } // // Update counter if (step != 0) { changed = 1; ent.cnt += step; } // // Update entry in file, if anything changed if (changed) { ent.mtime = (kXR_int32)time(0); if (WriteEnt(ind.entofs, ent) < 0) { Close(); return -1; } } // // Fill output if (cnt) *cnt = ent.cnt; } } // Close the file Close(); return 0; } //________________________________________________________________ kXR_int32 XrdSutPFile::ReadEntry(const char *tag, XrdSutPFEntry &ent, int opt) { // Read entry with tag from file // If it does not exist, if opt == 1 search also for wild-card // matching entries; if more than 1 return the one that matches // the best, base on the number of characters matching. // If more wild-card entries have the same level of matching, // the first found is returned. ent.Reset(); // Make sure that we got a tag (otherwise we can't do nothing) if (!tag) return Err(kPFErrBadInputs,"ReadEntry"); // Make sure we got an open stream bool wasopen = 0; if (Open(1 &wasopen) < 0) return -1; // Read the header XrdSutPFHeader header; if (ReadHeader(header) < 0) { if (!wasopen) Close(); return -1; } // Check if the HashTable needs to be updated if (fHashTable && header.itime > fHTutime) { // Update the table if (UpdateHashTable() < 0) { if (!wasopen) Close(); return -1; } } // // Get index entry associated with tag, if any XrdSutPFEntInd ind; bool found = 0; if (fHashTable) { kXR_int32 *reftmp = fHashTable->Find(tag); kXR_int32 refofs = reftmp ? *reftmp : -1; if (refofs > 0) { // Read it out if (ReadInd(refofs, ind) < 0) { if (!wasopen) Close(); return -1; } found = 1; } } else { // Get offset of the first index entry kXR_int32 indofs = header.indofs; while (indofs > 0) { // Read it out if (ReadInd(indofs, ind) < 0) { if (!wasopen) Close(); return -1; } // Check compatibility if (strlen(ind.name) == strlen(tag)) { if (!strncmp(ind.name,tag,strlen(tag))) { found = 1; break; } } // Next index entry indofs = ind.nxtofs; } } // // If not found and requested, try also wild-cards if (!found && opt == 1) { // // If > 1 we will keep the best matching, i.e. the one // matching most of the chars in tag kXR_int32 refofs = -1; kXR_int32 nmmax = 0; kXR_int32 iofs = header.indofs; XrdOucString stag(tag); while (iofs) { // // Read it out if (ReadInd(iofs, ind) < 0) { if (!wasopen) Close(); return -1; } // // Check compatibility, if active if (ind.entofs > 0) { int match = stag.matches(ind.name); if (match > nmmax && ind.entofs > 0) { nmmax = match; refofs = iofs; } } // // Next index entry iofs = ind.nxtofs; } // // Read it out if (refofs > 0) { if (ReadInd(refofs, ind) < 0) { if (!wasopen) Close(); return -1; } found = 1; } } // Read the entry, if found kXR_int32 nr = 0; if (found) { // Read entry if active if (ind.entofs) { if ((nr = ReadEnt(ind.entofs, ent)) < 0) { if (!wasopen) Close(); return -1; } // Fill the name ent.SetName(ind.name); } } // Close the file if (!wasopen) Close(); return nr; } //________________________________________________________________ kXR_int32 XrdSutPFile::ReadEntry(kXR_int32 ofs, XrdSutPFEntry &ent) { // Read entry at ofs from file // Make sure that ofs makes sense if (ofs <= 0) return Err(kPFErrBadInputs,"ReadEntry"); // Make sure we got an open stream bool wasopen = 0; if (Open(1, &wasopen) < 0) return -1; kXR_int32 nr = 0; // Read index entry out XrdSutPFEntInd ind; if (ReadInd(ofs, ind) < 0) { if (!wasopen) Close(); return -1; } // Read entry if ((nr = ReadEnt(ind.entofs, ent)) < 0) { if (!wasopen) Close(); return -1; } // Fill the name ent.SetName(ind.name); // Close the file if (!wasopen) Close(); return nr; } //________________________________________________________________ kXR_int32 XrdSutPFile::RemoveEntry(const char *tag) { // Remove entry with tag from file // The entry is set inactive, so that it is hidden and it will // be physically removed at next Trim // Make sure that we got a tag (otherwise we can't do nothing) if (!tag || !strlen(tag)) return Err(kPFErrBadInputs,"RemoveEntry"); // Make sure we got an open stream if (Open(1) < 0) return -1; // Read the header XrdSutPFHeader header; if (ReadHeader(header) < 0) { Close(); return -1; } // Check if the HashTable needs to be updated if (fHashTable && header.itime > fHTutime) { // Update the table if (UpdateHashTable() < 0) { Close(); return -1; } } // Get offset of the index entry associated with tag, if any XrdSutPFEntInd ind; bool found = 0; kXR_int32 indofs = -1; if (fHashTable) { kXR_int32 *indtmp = fHashTable->Find(tag); indofs = indtmp ? *indtmp : indofs; if (indofs > 0) { // Read it out if (ReadInd(indofs, ind) < 0) { Close(); return -1; } found = 1; } } else { // Get offset of the first index entry indofs = header.indofs; while (indofs > 0) { // Read it out if (ReadInd(indofs, ind) < 0) { Close(); return -1; } // Check compatibility if (strlen(ind.name) == strlen(tag)) { if (!strncmp(ind.name,tag,strlen(tag))) { found = 1; break; } } // Next index entry indofs = ind.nxtofs; } } // // Get entry now, if index found if (found) { // Reset entry area short status = kPFE_inactive; if (lseek(fFd, ind.entofs, SEEK_SET) == -1) { Close(); return Err(kPFErrSeek,"RemoveEntry", "SEEK_SET",(const char *)&fFd); } while (write(fFd, &status, sizeof(short)) < 0 && errno == EINTR) errno = 0; // Reset entry area if (Reset(ind.entofs + sizeof(short), ind.entsiz - sizeof(short)) < 0) { Close(); return -1; } // Set entofs to null ind.entofs = 0; if (WriteInd(indofs, ind) < 0) { Close(); return -1; } // Count as unused bytes header.jnksiz += ind.entsiz; // Decrease number of entries header.entries--; // Update times header.ctime = (kXR_int32)time(0); header.itime = header.ctime; // Update header if (WriteHeader(header) < 0) { Close(); return -1; } // Ok: close the file and return Close(); return 0; } // Close the file Close(); // entry non-existing return -1; } //________________________________________________________________ kXR_int32 XrdSutPFile::RemoveEntry(kXR_int32 ofs) { // Remove entry at entry index offset ofs from file // The entry is set inactive, so that it is hidden and it will // be physically removed at next Trim // Make sure that we got a tag (otherwise we can't do nothing) if (ofs <= 0) return Err(kPFErrBadInputs,"RemoveEntry"); // Make sure we got an open stream if (Open(1) < 0) return -1; // Read the header XrdSutPFHeader header; if (ReadHeader(header) < 0) { Close(); return -1; } // Check if the HashTable needs to be updated if (header.itime > fHTutime) { // Update the table if (UpdateHashTable() < 0) { Close(); return -1; } } // // Read it out XrdSutPFEntInd ind; if (ReadInd(ofs, ind) < 0) { Close(); return -1; } // // Reset entry area short status = kPFE_inactive; if (lseek(fFd, ind.entofs, SEEK_SET) == -1) { Close(); return Err(kPFErrSeek,"RemoveEntry", "SEEK_SET",(const char *)&fFd); } while (write(fFd, &status, sizeof(short)) < 0 && errno == EINTR) errno = 0; // Reset entry area if (Reset(ind.entofs + sizeof(short), ind.entsiz - sizeof(short)) < 0) { Close(); return -1; } // Set entofs to null ind.entofs = 0; if (WriteInd(ofs, ind) < 0) { Close(); return -1; } // Count as unused bytes header.jnksiz += ind.entsiz; // Decrease number of entries header.entries--; // Update times header.ctime = (kXR_int32)time(0); header.itime = header.ctime; // Update header if (WriteHeader(header) < 0) { Close(); return -1; } // // Ok: close the file and return Close(); return 0; } //_________________________________________________________________ kXR_int32 XrdSutPFile::Reset(kXR_int32 ofs, kXR_int32 siz) { // Reset size bytes starting at ofs in the open stream // // Set the offset if (lseek(fFd, ofs, SEEK_SET) == -1) return Err(kPFErrSeek,"Reset", "SEEK_SET",(const char *)&fFd); kXR_int32 nrs = 0; // Now write the buffer to the stream while (nrs < siz) { char c = 0; while (write(fFd, &c, 1) < 0 && errno == EINTR) errno = 0; nrs++; } return nrs; } //__________________________________________________________________ kXR_int32 XrdSutPFile::WriteInd(kXR_int32 ofs, XrdSutPFEntInd ind) { // Write entry index to open stream fFd // Make sure we got an open stream if (fFd < 0) return Err(kPFErrFileNotOpen,"WriteInd"); // // Set the offset if (lseek(fFd, ofs, SEEK_SET) == -1) return Err(kPFErrSeek,"WriteInd", "SEEK_SET",(const char *)&fFd); // // Build output buffer // // Get total lenght needed kXR_int32 ltot = ind.Length(); // // Allocate the buffer char *bout = new char[ltot]; if (!bout) return Err(kPFErrOutOfMemory,"WriteInd"); // // Fill the buffer kXR_int32 lp = 0; // Name length kXR_int32 lnam = strlen(ind.name); memcpy(bout+lp,&lnam,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // Offset of next index entry memcpy(bout+lp,&ind.nxtofs,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // Offset of entry memcpy(bout+lp,&ind.entofs,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // Size allocated for entry memcpy(bout+lp,&ind.entsiz,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // name memcpy(bout+lp,ind.name,lnam); lp += lnam; // Check length if (lp != ltot) { if (bout) delete[] bout; return Err(kPFErrLenMismatch,"WriteInd", (const char *)&lp, (const char *)<ot); } kXR_int32 nw = 0; // Now write the buffer to the stream while ((nw = write(fFd, bout, ltot)) < 0 && errno == EINTR) errno = 0; return nw; } //__________________________________________________________________ kXR_int32 XrdSutPFile::WriteEnt(kXR_int32 ofs, XrdSutPFEntry ent) { // Write ent to stream out // Make sure we got an open stream if (fFd < 0) return Err(kPFErrFileNotOpen,"WriteEnt"); // // Set the offset if (lseek(fFd, ofs, SEEK_SET) == -1) return Err(kPFErrSeek,"WriteEnt", "SEEK_SET",(const char *)&fFd); // // Build output buffer // // Get total lenght needed kXR_int32 ltot = ent.Length(); // // Allocate the buffer char *bout = new char[ltot]; if (!bout) return Err(kPFErrOutOfMemory,"WriteEnt"); // // Fill the buffer kXR_int32 lp = 0; // status memcpy(bout+lp,&ent.status,sizeof(short)); lp += sizeof(short); // count memcpy(bout+lp,&ent.cnt,sizeof(short)); lp += sizeof(short); // time of modification / creation memcpy(bout+lp,&ent.mtime,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // length of first buffer memcpy(bout+lp,&ent.buf1.len,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // length of second buffer memcpy(bout+lp,&ent.buf2.len,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // length of third buffer memcpy(bout+lp,&ent.buf3.len,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // length of fourth buffer memcpy(bout+lp,&ent.buf4.len,sizeof(kXR_int32)); lp += sizeof(kXR_int32); if (ent.buf1.len > 0) { // first buffer memcpy(bout+lp,ent.buf1.buf,ent.buf1.len); lp += ent.buf1.len; } if (ent.buf2.len > 0) { // second buffer memcpy(bout+lp,ent.buf2.buf,ent.buf2.len); lp += ent.buf2.len; } if (ent.buf3.len > 0) { // third buffer memcpy(bout+lp,ent.buf3.buf,ent.buf3.len); lp += ent.buf3.len; } if (ent.buf4.len > 0) { // third buffer memcpy(bout+lp,ent.buf4.buf,ent.buf4.len); lp += ent.buf4.len; } // Check length if (lp != ltot) { if (bout) delete[] bout; return Err(kPFErrLenMismatch,"WriteEnt", (const char *)&lp, (const char *)<ot); } kXR_int32 nw = 0; // Now write the buffer to the stream while ((nw = write(fFd, bout, ltot)) < 0 && errno == EINTR) errno = 0; return nw; } //__________________________________________________________________ kXR_int32 XrdSutPFile::ReadHeader(XrdSutPFHeader &hd) { // Read header from beginning of stream // // Make sure that we got an open file description if (fFd < 0) return Err(kPFErrFileNotOpen,"ReadHeader"); // // Set the offset if (lseek(fFd, 0, SEEK_SET) == -1) return Err(kPFErrSeek,"ReadHeader", "SEEK_SET",(const char *)&fFd); kXR_int32 nr = 0, nrdt = 0; // // Now read the information step by step: // the file ID ... if ((nr = read(fFd,hd.fileID,kFileIDSize)) != kFileIDSize) return Err(kPFErrRead,"ReadHeader",(const char *)&fFd); hd.fileID[kFileIDSize-1] = 0; nrdt += nr; // the version ... if ((nr = read(fFd,&hd.version,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadHeader",(const char *)&fFd); nrdt += nr; // the time of last change ... if ((nr = read(fFd,&hd.ctime,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadHeader",(const char *)&fFd); nrdt += nr; // the time of last index change ... if ((nr = read(fFd,&hd.itime,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadHeader",(const char *)&fFd); nrdt += nr; // the number of entries ... if ((nr = read(fFd,&hd.entries,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadHeader",(const char *)&fFd); nrdt += nr; // the offset of first index entry ... if ((nr = read(fFd,&hd.indofs,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadHeader",(const char *)&fFd); nrdt += nr; // the number of unused bytes ... if ((nr = read(fFd,&hd.jnksiz,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadHeader",(const char *)&fFd); nrdt += nr; return nrdt; } //_____________________________________________________________________ kXR_int32 XrdSutPFile::ReadInd(kXR_int32 ofs, XrdSutPFEntInd &ind) { // Read entry index from offset ofs of open stream fFd // // Make sure that we got an open file description if (fFd < 0) return Err(kPFErrFileNotOpen,"ReadInd"); // // Set the offset if (lseek(fFd, ofs, SEEK_SET) == -1) return Err(kPFErrSeek,"ReadInd", "SEEK_SET",(const char *)&fFd); kXR_int32 nr = 0, nrdt = 0; // // Now read the information step by step: // the length of the name ... kXR_int32 lnam = 0; if ((nr = read(fFd,&lnam,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadInd",(const char *)&fFd); nrdt += nr; // the offset of next entry index ... if ((nr = read(fFd,&ind.nxtofs,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadInd",(const char *)&fFd); nrdt += nr; // the offset of the entry ... if ((nr = read(fFd,&ind.entofs,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadInd",(const char *)&fFd); nrdt += nr; // the size allocated for the entry ... if ((nr = read(fFd,&ind.entsiz,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadInd",(const char *)&fFd); nrdt += nr; // the name ... cleanup first if (ind.name) { delete[] ind.name; ind.name = 0; } if (lnam) { ind.name = new char[lnam+1]; if (ind.name) { if ((nr = read(fFd,ind.name,lnam)) != lnam) return Err(kPFErrRead,"ReadInd",(const char *)&fFd); ind.name[lnam] = 0; // null-terminated nrdt += nr; } else return Err(kPFErrOutOfMemory,"ReadInd"); } return nrdt; } //____________________________________________________________________ kXR_int32 XrdSutPFile::ReadEnt(kXR_int32 ofs, XrdSutPFEntry &ent) { // Read ent from current position at stream // // Make sure that we got an open file description if (fFd < 0) return Err(kPFErrFileNotOpen,"ReadEnt"); // // Set the offset if (lseek(fFd, ofs, SEEK_SET) == -1) return Err(kPFErrSeek,"ReadEnt", "SEEK_SET",(const char *)&fFd); kXR_int32 nr = 0, nrdt = 0; // // Now read the information step by step: // the status ... if ((nr = read(fFd,&ent.status,sizeof(short))) != sizeof(short)) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; // the count var ... if ((nr = read(fFd,&ent.cnt,sizeof(short))) != sizeof(short)) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; // the the time of modification / creation ... if ((nr = read(fFd,&ent.mtime,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; // the length of the first buffer ... if ((nr = read(fFd,&ent.buf1.len,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; // the length of the second buffer ... if ((nr = read(fFd,&ent.buf2.len,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; // the length of the third buffer ... if ((nr = read(fFd,&ent.buf3.len,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; // the length of the fourth buffer ... if ((nr = read(fFd,&ent.buf4.len,sizeof(kXR_int32))) != sizeof(kXR_int32)) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; // Allocate space for the first buffer and read it (if any) ... if (ent.buf1.len) { ent.buf1.buf = new char[ent.buf1.len]; if (!ent.buf1.buf) return Err(kPFErrOutOfMemory,"ReadEnt"); if ((nr = read(fFd,ent.buf1.buf,ent.buf1.len)) != ent.buf1.len) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; } // Allocate space for the second buffer and read it (if any) ... if (ent.buf2.len) { ent.buf2.buf = new char[ent.buf2.len]; if (!ent.buf2.buf) return Err(kPFErrOutOfMemory,"ReadEnt"); if ((nr = read(fFd,ent.buf2.buf,ent.buf2.len)) != ent.buf2.len) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; } // Allocate space for the third buffer and read it (if any) ... if (ent.buf3.len) { ent.buf3.buf = new char[ent.buf3.len]; if (!ent.buf3.buf) return Err(kPFErrOutOfMemory,"ReadEnt"); if ((nr = read(fFd,ent.buf3.buf,ent.buf3.len)) != ent.buf3.len) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; } // Allocate space for the fourth buffer and read it (if any) ... if (ent.buf4.len) { ent.buf4.buf = new char[ent.buf4.len]; if (!ent.buf4.buf) return Err(kPFErrOutOfMemory,"ReadEnt"); if ((nr = read(fFd,ent.buf4.buf,ent.buf4.len)) != ent.buf4.len) return Err(kPFErrRead,"ReadEnt",(const char *)&fFd); nrdt += nr; } return nrdt; } //________________________________________________________________ kXR_int32 XrdSutPFile::Browse(void *oout) { // Display the content of the file // Make sure we got an open stream if (Open(1) < 0) return -1; // Read header XrdSutPFHeader hdr; if (ReadHeader(hdr) < 0) { Close(); return -1; } // Time strings struct tm tst; char sctime[256] = {0}; time_t ttmp = hdr.ctime; localtime_r(&ttmp,&tst); asctime_r(&tst,sctime); sctime[strlen(sctime)-1] = 0; char sitime[256] = {0}; ttmp = hdr.itime; localtime_r(&ttmp,&tst); asctime_r(&tst,sitime); sitime[strlen(sitime)-1] = 0; // Default is stdout FILE *out = oout ? (FILE *)oout : stdout; fprintf(out,"//-----------------------------------------------------" "--------------------//\n"); fprintf(out,"//\n"); fprintf(out,"// File: %s\n",name); fprintf(out,"// ID: %s\n",hdr.fileID); fprintf(out,"// Version: %d\n",hdr.version); fprintf(out,"// Last change : %s (%d sec)\n",sctime,hdr.ctime); fprintf(out,"// Index change: %s (%d sec)\n",sitime,hdr.itime); fprintf(out,"//\n"); fprintf(out,"// Number of Entries: %d\n",hdr.entries); fprintf(out,"// Bytes unreachable: %d\n",hdr.jnksiz); fprintf(out,"//\n"); if (hdr.entries > 0) { // Special entries first, if any kXR_int32 ns = SearchSpecialEntries(); if (ns > 0) { // Allocate space for offsets kXR_int32 *sofs = new kXR_int32[ns]; if (sofs) { // Get offsets ns = SearchSpecialEntries(sofs,ns); fprintf(out,"// Special entries (%d):\n",ns); int i = 0; for (; i ns) fprintf(out,"// Normal entries (%d):\n",hdr.entries-ns); kXR_int32 nn = 0; kXR_int32 nxtofs = hdr.indofs; while (nxtofs) { // Read entry index at ofs XrdSutPFEntInd ind; if (ReadInd(nxtofs, ind) < 0) { Close(); return -3; } if (ind.entofs) { // Read entry XrdSutPFEntry ent; if (ReadEnt(ind.entofs, ent) < 0) { Close(); return -4; } if (ent.status != kPFE_special) { char smt[20] = {0}; XrdSutTimeString(ent.mtime,smt); nn++; fprintf(out, "// #:%d st:%d cn:%d buf:%d,%d,%d,%d mod:%s name:%s\n", nn,ent.status,ent.cnt,ent.buf1.len,ent.buf2.len,ent.buf3.len, ent.buf4.len,smt,ind.name); } } // Read next nxtofs = ind.nxtofs; } fprintf(out,"//\n"); } fprintf(out,"//-----------------------------------------------------" "--------------------//\n"); // Close the file Close(); return 0; } //________________________________________________________________ kXR_int32 XrdSutPFile::Trim(const char *fbak) { // Trim away unreachable entries from the file // Previous content is save in a file name fbak, the default // being 'name'.bak EPNAME("PFile::Trim"); // Retrieve header, first, to check if there is anything to trim XrdSutPFHeader header; if (RetrieveHeader(header) < 0) return -1; if (header.jnksiz <= 0) { DEBUG("nothing to trim - return "); return -1; } // Get name of backup file char *nbak = (char *)fbak; if (!nbak) { // Use default nbak = new char[strlen(name)+5]; if (!nbak) return Err(kPFErrOutOfMemory,"Trim"); sprintf(nbak,"%s.bak",name); DEBUG("backup file: "< 0) { fFd = fdbck; if (ReadEnt(ind.entofs,ent) < 0) { Close(fdnew); Close(fdbck); return -1; } // Update index entry ind.entofs = wrofs; // Write active entry fFd = fdnew; if (WriteEnt(wrofs,ent) < 0) { Close(fdnew); Close(fdbck); return -1; } // Update write offset if ((wrofs = lseek(fdnew, 0, SEEK_CUR)) == -1) { Close(fdnew); Close(fdbck); return Err(kPFErrSeek,"Trim", "SEEK_CUR",(const char *)&fdnew); } if (firstind) { // Update header header.indofs = wrofs; firstind = 0; } else { // Update previous index entry indlast.nxtofs = wrofs; fFd = fdnew; if (WriteInd(lastofs,indlast) < 0) { Close(fdnew); Close(fdbck); return -1; } } // Save this index for later updates indlast = ind; lastofs = wrofs; // Last index entry, for now ind.nxtofs = 0; // Write active index entry fFd = fdnew; if (WriteInd(wrofs,ind) < 0) { Close(fdnew); Close(fdbck); return -1; } // Update write offset if ((wrofs = lseek(fdnew, 0, SEEK_CUR)) == -1) { Close(fdnew); Close(fdbck); return Err(kPFErrSeek,"Trim", "SEEK_CUR",(const char *)&fdnew); } } } // Close backup file Close(fdbck); fFd = fdnew; // Update header header.ctime = (kXR_int32)time(0); header.itime = header.ctime; header.jnksiz = 0; // Copy it to new file if (WriteHeader(header) < 0) { Close();; return -1; } // Close the file Close(); return 0; } //________________________________________________________________ kXR_int32 XrdSutPFile::UpdateHashTable(bool force) { // Update hash table reflecting the index of the file // If force is .true. the table is recreated even if no recent // change in the index has occured. // Returns the number of entries in the table. // The file must be open if (fFd < 0) return Err(kPFErrFileNotOpen,"UpdateHashTable"); // Read the header XrdSutPFHeader header; if (ReadHeader(header) < 0) return -1; // If no recent changes and no force option, return if (!force && header.itime < fHTutime) return 0; // Clean up the table or create it if (fHashTable) fHashTable->Purge(); else fHashTable = new XrdOucHash; // Make sure we have it if (!fHashTable) return Err(kPFErrOutOfMemory,"UpdateHashTable"); // Read entries kXR_int32 ne = 0; if (header.entries > 0) { XrdSutPFEntInd ind; kXR_int32 nxtofs = header.indofs; while (nxtofs > 0) { if (ReadInd(nxtofs, ind) < 0) return -1; ne++; // Fill the table kXR_int32 *key = new kXR_int32(nxtofs); fHashTable->Add(ind.name,key); // Go to next nxtofs = ind.nxtofs; } } // Update the time stamp fHTutime = (kXR_int32)time(0); return ne; } //________________________________________________________________ kXR_int32 XrdSutPFile::RemoveEntries(const char *tag, char opt) { // Remove entries whose tag is compatible with 'tag', according // to compatibility option 'opt'. // For opt = 0 tags starting with 'tag' // for opt = 1 tags containing the wild card '*' are matched. // Return number of entries removed EPNAME("PFile::RemoveEntries"); // // Get number of entries related int nm = SearchEntries(tag,opt); if (nm) { DEBUG("found "< 0 && ind.entofs > 0) { no++; if (ofs) { ofs[no-1] = indofs; if (no == nofs) { // We are done break; } } } // Next index entry indofs = ind.nxtofs; } // Close the file if (!wasopen) Close(); return no; } //________________________________________________________________ kXR_int32 XrdSutPFile::SearchSpecialEntries(kXR_int32 *ofs, kXR_int32 nofs) { // Get offsets of the first nofs entries with status // kPFE_special. // The caller is responsible for memory pointed by 'ofs'. // Return number of entries found (<= nofs). // If ofs = 0, return total number of special entries. // Make sure we got an open stream bool wasopen = 0; if (Open(1,&wasopen) < 0) return -1; // Read the header XrdSutPFHeader header; if (ReadHeader(header) < 0) { if (!wasopen) Close(); return -1; } // Get offset of the first index entry kXR_int32 indofs = header.indofs; // Scan entries kXR_int32 no = 0; while (indofs) { // Read index XrdSutPFEntInd ind; if (ReadInd(indofs, ind) < 0) { if (!wasopen) Close(); return -1; } // If active ... if (ind.entofs > 0) { // Read entry out XrdSutPFEntry ent; if (ReadEnt(ind.entofs, ent) < 0) { if (!wasopen) Close(); return -1; } // If special ... if (ent.status == kPFE_special) { // Record the offset ... no++; if (ofs) { ofs[no-1] = indofs; if (no == nofs) { // We are done break; } } } } // Next index entry indofs = ind.nxtofs; } // Close the file if (!wasopen) Close(); return no; } //________________________________________________________________ kXR_int32 XrdSutPFile::Err(kXR_int32 code, const char *loc, const char *em1, const char *em2) { // Save code and, if requested, format and print an error // message EPNAME("PFile::Err"); char buf[XrdSutMAXBUF]; int fd = 0, lp = 0, lt = 0; // Save code for later use fError = code; // Build string following the error code char *errbuf = strerror(errno); switch (code) { case kPFErrBadInputs: snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: bad input arguments",loc); break; case kPFErrFileAlreadyOpen: snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: file already open" " in incompatible mode",loc); break; case kPFErrNoFile: snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: file %s does not exists", loc,em1); break; case kPFErrFileRename: snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: error renaming file %s to %s" " (%s)",loc,em1,em2,errbuf); break; case kPFErrStat: snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: cannot file %s (%s)", loc,em1,errbuf); break; case kPFErrFileOpen: snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: cannot open file %s (%s)", loc,em1,errbuf); break; case kPFErrFileNotOpen: snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: file is not open", loc); break; case kPFErrLocking: fd = *((int *)em1); snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: cannot lock file descriptor %d (%s)", loc,fd,errbuf); break; case kPFErrUnlocking: fd = *((int *)em1); snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: cannot unlock file descriptor %d (%s)", loc,fd,errbuf); break; case kPFErrFileLocked: fd = *((int *)em2); snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: file %s is locked by process %d", loc,em1,fd); break; case kPFErrSeek: fd = *((int *)em2); snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: lseek %s error on descriptor %d (%s)", loc,em1,fd,errbuf); break; case kPFErrRead: fd = *((int *)em1); snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: read error on descriptor %d (%s)", loc,fd,errbuf); break; case kPFErrOutOfMemory: snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: out of memory (%s)", loc,errbuf); break; case kPFErrLenMismatch: lp = *((int *)em1); lt = *((int *)em2); snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: length mismatch: %d (expected: %d)", loc,lp,lt); break; case kPFErrBadOp: snprintf(buf,XrdSutMAXBUF, "XrdSutPFile::%s: bad option: %s", loc,em1); break; default: DEBUG("unknown error code: "<