/******************************************************************************/
/* */
/* X r d X r o o t d F i l e . c c */
/* */
/* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */
/* 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 "XrdSys/XrdSysError.hh"
#include "XrdSys/XrdSysPthread.hh"
#include "XrdSfs/XrdSfsInterface.hh"
#include "XrdXrootd/XrdXrootdFile.hh"
#include "XrdXrootd/XrdXrootdFileLock.hh"
#include "XrdXrootd/XrdXrootdMonFile.hh"
#include "XrdXrootd/XrdXrootdMonitor.hh"
#define TRACELINK this
#include "XrdXrootd/XrdXrootdTrace.hh"
/******************************************************************************/
/* G l o b a l s */
/******************************************************************************/
#ifndef NODEBUG
extern XrdOucTrace *XrdXrootdTrace;
#endif
XrdXrootdFileLock *XrdXrootdFile::Locker;
int XrdXrootdFile::sfOK = 1;
const char *XrdXrootdFile::TraceID = "File";
const char *XrdXrootdFileTable::TraceID = "FileTable";
const char *XrdXrootdFileTable::ID = "";
namespace
{
XrdSysError *eDest;
static const unsigned long heldSpotV = 1UL;;
static XrdXrootdFile *heldSpotP = (XrdXrootdFile *)1;
static const unsigned long heldMask = ~1UL;
}
/******************************************************************************/
/* x r d _ F i l e C l a s s */
/******************************************************************************/
/******************************************************************************/
/* C o n s t r u c t o r */
/******************************************************************************/
XrdXrootdFile::XrdXrootdFile(const char *id, const char *path, XrdSfsFile *fp,
char mode, bool async, int sfok, struct stat *sP)
{
static XrdSysMutex seqMutex;
struct stat buf;
off_t mmSize;
XrdSfsp = fp;
FileKey = strdup(path);
mmAddr = 0;
FileMode = mode;
AsyncMode= (async ? 1 : 0);
fhProc = 0;
ID = id;
Stats.Init();
// Get the file descriptor number (none if not a regular file)
//
if (fp->fctl(SFS_FCTL_GETFD, 0, fp->error) != SFS_OK) fdNum = -1;
else fdNum = fp->error.getErrInfo();
sfEnabled = (sfOK && sfok && (fdNum >= 0||fdNum==(int)SFS_SFIO_FDVAL) ? 1:0);
// Determine if file is memory mapped
//
if (fp->getMmap((void **)&mmAddr, mmSize) != SFS_OK) isMMapped = 0;
else {isMMapped = (mmSize ? 1 : 0);
Stats.fSize = static_cast(mmSize);
}
// Get file status information (we need it) and optionally return it to caller
//
if (sP || !isMMapped)
{if (!sP) sP = &buf;
fp->stat(sP);
if (!isMMapped) Stats.fSize = static_cast(sP->st_size);
}
}
/******************************************************************************/
/* */
/* I n i t */
/******************************************************************************/
void XrdXrootdFile::Init(XrdXrootdFileLock *lp, XrdSysError *erP, int sfok)
{
Locker = lp;
eDest = erP;
sfOK = sfok;
}
/******************************************************************************/
/* D e s t r u c t o r */
/******************************************************************************/
XrdXrootdFile::~XrdXrootdFile()
{
if (XrdSfsp)
{TRACEI(FS, "closing " <Unlock(FileKey, FileMode);
}
if (fhProc) fhProc->Avail(fHandle);
if (FileKey) free(FileKey);
}
/******************************************************************************/
/* x r d _ F i l e T a b l e C l a s s */
/******************************************************************************/
/******************************************************************************/
/* A d d */
/******************************************************************************/
int XrdXrootdFileTable::Add(XrdXrootdFile *fp)
{
const int allocsz = XRD_FTABSIZE*sizeof(fp);
XrdXrootdFile **newXTab, **oldXTab;
int i;
// If we have a file handle processor, see if it can give us a file handle
// that's already in our table.
//
if (fhProc && (i = fhProc->Get()) >= 0)
{XrdXrootdFile **fP;
if (i < XRD_FTABSIZE) fP = &FTab[i];
else {i -= XRD_FTABSIZE;
if (XTab && i < XTnum) fP = &XTab[i];
else fP = 0;
}
if (fP && *fP == heldSpotP)
{*fP = fp;
TRACEI(FS, "reusing fh " <FileKey);
return i;
}
char fhn[32];
snprintf(fhn, sizeof(fhn), "%d", i);
eDest->Emsg("FTab_Add", "Invalid recycled fHandle",fhn,"ignored.");
}
// Find a free spot in the internal table
//
for (i = FTfree; i < XRD_FTABSIZE; i++) if (!FTab[i]) break;
if (i < XRD_FTABSIZE)
{FTab[i] = fp; FTfree = i+1; return i;}
// Allocate an external table if we do not have one
//
if (!XTab)
{if (!(XTab = (XrdXrootdFile **)malloc(allocsz))) return -1;
memset((void *)XTab, 0, allocsz);
XTnum = XRD_FTABSIZE;
XTfree = 1;
XTab[0] = fp;
return XRD_FTABSIZE;
}
// Find a free spot in the external table
//
for (i = XTfree; i < XTnum; i++) if (!XTab[i]) break;
if (i < XTnum)
{XTab[i] = fp; XTfree = i+1; return i+XRD_FTABSIZE;}
// Extend the table
//
if (!(newXTab = (XrdXrootdFile **)malloc(XTnum*sizeof(XrdXrootdFile *)+allocsz)))
return -1;
memcpy((void *)newXTab, (const void *)XTab, XTnum*sizeof(XrdXrootdFile *));
memset((void *)(newXTab+XTnum), 0, allocsz);
oldXTab = XTab;
XTab = newXTab;
XTab[XTnum] = fp;
i = XTnum;
XTfree = XTnum+1;
XTnum += XRD_FTABSIZE;
free(oldXTab);
return i+XRD_FTABSIZE;
}
/******************************************************************************/
/* D e l */
/******************************************************************************/
XrdXrootdFile *XrdXrootdFileTable::Del(XrdXrootdMonitor *monP, int fnum,
bool dodel)
{
union {XrdXrootdFile *fp; unsigned long fv;};
XrdXrootdFile *repVal = (dodel ? 0 : heldSpotP);
int fh = fnum;
if (fnum < XRD_FTABSIZE)
{fp = FTab[fnum];
FTab[fnum] = repVal;
if (fnum < FTfree) FTfree = fnum;
} else {
fnum -= XRD_FTABSIZE;
if (XTab && fnum < XTnum)
{fp = XTab[fnum];
XTab[fnum] = repVal;
if (fnum < XTfree) XTfree = fnum;
}
else fp = 0;
}
fv &= heldMask;
if (fp)
{XrdXrootdFileStats &Stats = fp->Stats;
if (monP) monP->Close(Stats.FileID,
Stats.xfr.read + Stats.xfr.readv,
Stats.xfr.write);
if (Stats.MonEnt != -1) XrdXrootdMonFile::Close(&Stats, false);
if (dodel) {delete fp; fp = 0;} // Will do the close
else {if (!fhProc) fhProc = new XrdXrootdFileHP;
else fhProc->Ref();
fp->fHandle = fh;
fp->fhProc = fhProc;
TRACEI(FS, "defer fh " <FileKey);
}
}
return fp;
}
/******************************************************************************/
/* R e c y c l e */
/******************************************************************************/
// WARNING! The object subject to this method must be serialized. There can
// be no active requests on link associated with this object at the time the
// destructor is called. The same restrictions apply to Add() and Del().
//
void XrdXrootdFileTable::Recycle(XrdXrootdMonitor *monP)
{
int i;
// Delete all objects from the internal table (see warning)
//
FTfree = 0;
for (i = 0; i < XRD_FTABSIZE; i++)
if (FTab[i] && FTab[i] != heldSpotP)
{XrdXrootdFileStats &Stats = FTab[i]->Stats;
if (monP) monP->Close(Stats.FileID,
Stats.xfr.read+Stats.xfr.readv,
Stats.xfr.write);
if (Stats.MonEnt != -1) XrdXrootdMonFile::Close(&Stats, true);
delete FTab[i]; FTab[i] = 0;
}
// Delete all objects from the external table (see warning)
//
if (XTab)
{for (i = 0; i < XTnum; i++)
{if (XTab[i] && XTab[i] != heldSpotP)
{XrdXrootdFileStats &Stats = XTab[i]->Stats;
if (monP) monP->Close(Stats.FileID,
Stats.xfr.read+Stats.xfr.readv,
Stats.xfr.write);
if (Stats.MonEnt != -1) XrdXrootdMonFile::Close(&Stats, true);
delete XTab[i];
}
}
free(XTab); XTab = 0; XTnum = 0; XTfree = 0;
}
// If we have a filehandle processor, delete it. Note that it will stay alive
// until all requests for file handles against it are resolved.
//
if (fhProc) fhProc->Delete();
// Delete this object
//
delete this;
}
/******************************************************************************/
/* P r i v a t e M e t h o d s */
/******************************************************************************/
/******************************************************************************/
/* b i n 2 h e x */
/******************************************************************************/
int XrdXrootdFile::bin2hex(char *outbuff, char *inbuff, int inlen)
{
static char hv[] = "0123456789abcdef";
int i, j = 0;
// Skip leading zeroes
//
for (i = 0; i < inlen; i++) if (inbuff[i]) break;
if (i >= inlen)
{outbuff[0] = '0'; outbuff[1] = '\0'; return 1;}
// Format the data
//
for ( ; i < inlen; i++)
{outbuff[j++] = hv[(inbuff[i] >> 4) & 0x0f];
outbuff[j++] = hv[ inbuff[i] & 0x0f];
}
outbuff[j] = '\0';
return j;
}