TCastorFile.cxx

Go to the documentation of this file.
00001 // @(#)root/castor:$Id: TCastorFile.cxx 35845 2010-09-28 15:20:07Z rdm $
00002 // Author: Fons Rademakers + Jean-Damien Durand 17/09/2003 + Ben Couturier 31/05/2005
00003 // + Giulia Taurelli 26/04/2006
00004 
00005 /*************************************************************************
00006  * Copyright (C) 1995-2006, Rene Brun and Fons Rademakers.               *
00007  * All rights reserved.                                                  *
00008  *                                                                       *
00009  * For the licensing terms see $ROOTSYS/LICENSE.                         *
00010  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
00011  *************************************************************************/
00012 
00013 //////////////////////////////////////////////////////////////////////////
00014 //                                                                      //
00015 // TCastorFile                                                          //
00016 //                                                                      //
00017 // A TCastorFile is like a normal TNetFile except that it obtains the   //
00018 // remote node (disk server) via the CASTOR API, once the disk server   //
00019 // and the local file path are determined, the file will be accessed    //
00020 // via the rootd daemon. File names have to be specified like:          //
00021 //    castor:/castor/cern.ch/user/r/rdm/bla.root.                       //
00022 //                                                                      //
00023 // If Castor 2.1 is used the file names can also be specified           //
00024 // in the following ways:                                               //
00025 //                                                                      //
00026 //  castor://stager_host:stager_port/?path=/castor/cern.ch/user/        //
00027 //    r/rdm/bla.root&svcClass=MYSVCLASS&castorVersion=MYCASTORVERSION   //
00028 //                                                                      //
00029 //  castor://stager_host/?path=/castor/cern.ch/user/                    //
00030 //    r/rdm/bla.root&svcClass=MYSVCLASS&castorVersion=MYCASTORVERSION   //
00031 //                                                                      //
00032 //  castor:///castor?path=/castor/cern.ch/user/                         //
00033 //    r/rdm/bla.root&svcClass=MYSVCLASS&castorVersion=MYCASTORVERSION   //
00034 //                                                                      //
00035 // path is mandatory as parameter but all the other ones are optional.  //
00036 //                                                                      //
00037 // Use "&rootAuth=<auth_prot_code>" in the option field to force the    //
00038 // specified authentication protocol when contacting the server, e.g.   //
00039 //                                                                      //
00040 //  castor:///castor?path=/castor/cern.ch/user/r/rdm/bla.root           //
00041 //    &svcClass=MYSVCLASS&castorVersion=MYCASTORVERSION&rootAuth=3      //
00042 //                                                                      //
00043 // will try first the globus/GSI protocol; available protocols are      //
00044 //  0: passwd, 1: srp, 2: krb5, 3: globus, 4: ssh, 5 uidgid             //
00045 // The defaul is taken from the env ROOTCASTORAUTH.                     //
00046 //                                                                      //
00047 //////////////////////////////////////////////////////////////////////////
00048 
00049 #include "NetErrors.h"
00050 #include "TCastorFile.h"
00051 #include "TError.h"
00052 
00053 #include <stdlib.h>
00054 #include <errno.h>
00055 
00056 #ifdef _WIN32
00057 #include <WinDef.h>
00058 #include <WinSock2.h>
00059 #endif
00060 
00061 #ifdef R__CASTOR2
00062 #include <stager_api.h>       // For the new CASTOR 2 Stager
00063 #endif
00064 #define RFIO_KERNEL           // Get access to extra symbols in the headers
00065 #include <stage_api.h>        // Dial with CASTOR stager
00066 #include <rfio_api.h>         // Util routines from rfio
00067 #include <Cns_api.h>          // Dial with CASTOR Name Server
00068 #include <Cglobals.h>
00069 #include <rfio_constants.h>
00070 
00071 #define RFIO_USE_CASTOR_V2 "RFIO_USE_CASTOR_V2"
00072 #define RFIO_HSM_BASETYPE  0x0
00073 #define RFIO_HSM_CNS       RFIO_HSM_BASETYPE+1
00074 
00075 extern "C" { int rfio_HsmIf_reqtoput (char *); }
00076 extern "C" { int DLL_DECL rfio_parse(char *, char **, char **); }
00077 extern "C" { int rfio_HsmIf_IsHsmFile (const char *); }
00078 extern "C" { char *getconfent(char *, char *, int); }
00079 
00080 #ifdef R__CASTOR2
00081 extern int tStageHostKey;
00082 extern int tStagePortKey;
00083 extern int tSvcClassKey;
00084 extern int tCastorVersionKey;
00085 extern "C" { int DLL_DECL use_castor2_api(); }
00086 
00087 //______________________________________________________________________________
00088 static int UseCastor2API()
00089 {
00090    // Function that checks whether we should use the old or new stager API.
00091 
00092    int version = use_castor2_api();
00093    return version;
00094 }
00095 
00096 #else
00097 
00098 //______________________________________________________________________________
00099 static int UseCastor2API()
00100 {
00101    // Function that checks whether we should use the old or new stager API.
00102 
00103    char *p;
00104 
00105    if (((p = getenv(RFIO_USE_CASTOR_V2)) == 0) &&
00106        ((p = getconfent("RFIO","USE_CASTOR_V2",0)) == 0)) {
00107       // Variable not set: compat mode
00108       return 0;
00109    }
00110    if ((strcmp(p,"YES") == 0) || (strcmp(p,"yes") == 0) || (atoi(p) == 1)) {
00111       // Variable set to yes or 1 but old CASTOR 1: compat mode + warning
00112       static int once = 0;
00113       if (!once) {
00114          ::Warning("UseCastor2API", "asked to use CASTOR 2, but linked with CASTOR 1");
00115          once = 1;
00116       }
00117       return 0;
00118    }
00119    // Variable set but not to 1 : compat mode
00120    return 0;
00121 }
00122 
00123 #endif
00124 
00125 //______________________________________________________________________________
00126 static const char *GetAuthProto(TString &url)
00127 {
00128    // Determine the authentication protocol to be tried first from the url
00129    // string or from defaults. The auth option, if any, is removed from 'url'.
00130 
00131    const Int_t rootNumSec = 6;
00132    const char *protoSec[rootNumSec] = {"rootup", "roots", "rootk",
00133                                        "rootg", "rooth", "rootug" };
00134    TString p = url;
00135    Int_t ii = p.Index("&rootAuth=");
00136    if (ii != kNPOS) {
00137       Int_t jj = p.Index("&", ii+1);
00138       if (jj != kNPOS)
00139          p.Remove(jj);
00140       p.Remove(0,ii);
00141       url.ReplaceAll(p, "");
00142       p.ReplaceAll("&rootAuth=", "");
00143    }
00144    if (p.Length() <= 0)
00145       // Use defaults
00146       p = getenv("ROOTCASTORAUTH");
00147 
00148    Int_t sec = -1;
00149    if (p.Length() > 0 && p.IsDigit()) {
00150       sec = p.Atoi();
00151       if (sec < 0 || sec > (rootNumSec - 1))
00152          sec = -1;
00153    }
00154 
00155    // Done
00156    return ((sec > -1 && sec < rootNumSec) ? protoSec[sec] : "root");
00157 }
00158 
00159 ClassImp(TCastorFile)
00160 
00161 //______________________________________________________________________________
00162 TCastorFile::TCastorFile(const char *url, Option_t *option, const char *ftitle,
00163                               Int_t compress, Int_t netopt)
00164       : TNetFile(url, ftitle, compress, kFALSE)
00165 {
00166    // Create a TCastorFile. A TCastorFile is like a normal TNetFile except
00167    // that it obtains the remote node (disk server) via the CASTOR API, once
00168    // the disk server and the local file path are determined, the file will
00169    // be accessed via the rootd daemon. File names have to be specified like:
00170    //    castor:/castor/cern.ch/user/r/rdm/bla.root.
00171    // The other arguments are the same as for TNetFile and TFile.
00172 
00173    fIsCastor  = kFALSE;
00174    fWrittenTo = kFALSE;
00175 
00176    // Extract the authentication info, if any; removing it from the options
00177    TString u(url);
00178    fAuthProto = GetAuthProto(u);
00179    fUrl.SetUrl(u);
00180 
00181    if (gDebug > 0)
00182       Info("TCastorFile","fAuthProto = %s, u: %s", fAuthProto.Data(), u.Data());
00183 
00184    // file is always created by stage_out_hsm() and therefore
00185    // exists when opened by rootd
00186    TString opt = option;
00187    opt.ToUpper();
00188    if (opt == "NEW" || opt == "CREATE")
00189       opt = "RECREATE";
00190 
00191    Create(url, opt, netopt);
00192 }
00193 
00194 //______________________________________________________________________________
00195 void TCastorFile::FindServerAndPath()
00196 {
00197    // Find the CASTOR disk server and internal file path.
00198 
00199    // just call rfio_parse and no extra parsing is added here to that
00200 
00201    TString castorturl;
00202    char *host=0;
00203    char *name=0;
00204 
00205    // to be able to use the turl starting with  castor:
00206    if (!strcmp(fUrl.GetProtocol(),"castor"))
00207       castorturl = Form("%s://%s", "rfio", fUrl.GetFile());
00208    else
00209       castorturl = Form("%s://%s", fUrl.GetProtocol(), fUrl.GetFile());
00210    if (strlen(fUrl.GetOptions()) > 0)
00211       castorturl += Form("?%s", fUrl.GetOptions());
00212 
00213    // the complete turl in fname
00214    TString fname = castorturl; // for compatibility with rfio_parse interface
00215    if (::rfio_parse((char *)fname.Data(), &host, &name)>=0) {
00216       castorturl = Form("%s",(!name || !strstr(name,"/castor"))?fname.Data():name);
00217       fname = castorturl.Data();
00218 
00219    } else {
00220       Error("FindServerAndPath", "error parsing %s", fUrl.GetUrl());
00221       return;
00222    }
00223 
00224    if (!UseCastor2API()) {
00225 
00226       struct stgcat_entry *stcp_output = 0;
00227       if (rfio_HsmIf_IsHsmFile(fUrl.GetFile()) == RFIO_HSM_CNS) {
00228          // This is a CASTOR file
00229          int flags = O_RDONLY;
00230          struct Cns_filestat st;
00231          int rc;
00232          char stageoutbuf[1025];
00233          char stageerrbuf[1025];
00234 
00235          // Check with internal stage limits - preventing overflow afterwards
00236          if (strlen(fUrl.GetFile()) > STAGE_MAX_HSMLENGTH) {
00237             serrno = ENAMETOOLONG;
00238             Error("FindServerAndPath", "can't open %s, error %d (%s)", fUrl.GetFile(), serrno, sstrerror(serrno));
00239             return;
00240          }
00241 
00242          // Prepare the flags
00243          if (fOption == "CREATE" || fOption == "RECREATE" || fOption == "UPDATE")
00244             flags |= O_RDWR;
00245          if (fOption == "CREATE" || fOption == "RECREATE")
00246             flags |= O_CREAT | O_TRUNC;
00247 
00248          // Check if an existing file is going to be updated
00249          memset(&st, 0, sizeof(st));
00250          rc = Cns_stat(fUrl.GetFile(), &st);
00251 
00252          // Make sure that filesize is 0 if file doesn't exist
00253          // or that we will create (stage_out) if O_TRUNC.
00254          if (rc == -1 || ((flags & O_TRUNC) != 0))
00255             st.filesize = 0;
00256 
00257          // Makes sure stage api does not write automatically to stdout/stderr
00258          if (stage_setoutbuf(stageoutbuf, 1024) != 0) {
00259             Error("FindServerAndPath", "can't open %s, stage_setoutbuf, error %d (%s)",
00260                   fUrl.GetFile(), serrno, sstrerror(serrno));
00261             return;
00262          }
00263          if (stage_seterrbuf(stageerrbuf, 1024) != 0) {
00264             Error("FindServerAndPath", "can't open %s, stage_seterrbuf, error %d (%s)",
00265                   fUrl.GetFile(), serrno, sstrerror(serrno));
00266             return;
00267          }
00268 
00269          struct stgcat_entry stcp_input;
00270          int nstcp_output;
00271 
00272          memset(&stcp_input, 0, sizeof(struct stgcat_entry));
00273          strlcpy(stcp_input.u1.h.xfile, fUrl.GetFile(), sizeof(stcp_input.u1.h.xfile));
00274          if (flags == O_RDONLY || st.filesize > 0) {
00275          // Do a recall
00276             if (stage_in_hsm((u_signed64) 0,          // Ebusy is possible...
00277                              (int) flags,             // open flags
00278                              (char *) 0,              // hostname
00279                              (char *) 0,              // pooluser
00280                              (int) 1,                 // nstcp_input
00281                              (struct stgcat_entry *) &stcp_input, // stcp_input
00282                              (int *) &nstcp_output,   // nstcp_output
00283                              (struct stgcat_entry **) &stcp_output, // stcp_output
00284                              (int) 0,                 // nstpp_input
00285                              (struct stgpath_entry *) 0 // stpp_input
00286                             ) != 0) {
00287                Error("FindServerAndPath", "can't open %s, stage_in_hsm error %d (%s)",
00288                      fUrl.GetFile(), serrno, sstrerror(serrno));
00289                return;
00290             }
00291          } else {
00292             // Do a creation
00293             if (stage_out_hsm((u_signed64) 0,          // Ebusy is possible...
00294                               (int) flags,             // open flags
00295                               (mode_t) 0666,           // open mode (c.f. also umask)
00296                               // Note: This is S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, c.f. fopen(2)
00297                               (char *) 0,              // hostname
00298                               (char *) 0,              // pooluser
00299                               (int) 1,                 // nstcp_input
00300                               (struct stgcat_entry *) &stcp_input, // stcp_input
00301                               (int *) &nstcp_output,   // nstcp_output
00302                               (struct stgcat_entry **) &stcp_output, // stcp_output
00303                               (int) 0,                       // nstpp_input
00304                               (struct stgpath_entry *) 0     // stpp_input
00305                              ) != 0) {
00306                Error("FindServerAndPath", "can't open %s, stage_out_hsm error %d (%s)",
00307                fUrl.GetFile(), serrno, sstrerror(serrno));
00308                return;
00309             }
00310          }
00311          if ((nstcp_output != 1) || (stcp_output == 0) ||
00312             (*(stcp_output->ipath) == '\0')) {
00313             // Impossible
00314             serrno = SEINTERNAL;
00315             if (stcp_output != 0) free(stcp_output);
00316             Error("FindServerAndPath", "can't open %s, error %d (%s)",
00317             fUrl.GetFile(), serrno, sstrerror(serrno));
00318             return;
00319          }
00320 
00321          // Parse orig string to get disk server host
00322          char *filename;
00323          char *realhost = 0;
00324 
00325          rfio_parse(stcp_output->ipath, &realhost, &filename);
00326          if (realhost == 0) {
00327             serrno = SEINTERNAL;
00328             Error("FindServerAndPath", "can't open %s, get disk server hostname from %s error %d (%s)",
00329                   fUrl.GetFile(), stcp_output->ipath, errno, sstrerror(serrno));
00330             free(stcp_output);
00331             return;
00332          }
00333          // Save real host and internal path
00334          fDiskServer = realhost;
00335          if (filename[0] != '/') {
00336             // Make file 'local' to the host
00337             fInternalPath  = "/";
00338             fInternalPath += filename;
00339          } else {
00340             fInternalPath = filename;
00341          }
00342 
00343          if (st.filesize == 0) {
00344             // Will force notification to stage when the file is closed
00345             fWrittenTo = kTRUE;
00346          }
00347       }
00348 
00349       // Set the protocol prefix for TNetFile.
00350       // For the cern.ch domain we set the default authentication
00351       // method to UidGid, i.e. as for rfiod, unless there is a specific
00352       // request (from options or envs); for this we need
00353       // the full FQDN or address in "nnn.mmm.iii.jjj" form
00354       TString r = fAuthProto;
00355       if (fAuthProto == "root") {
00356          TString fqdn;
00357          TInetAddress addr = gSystem->GetHostByName(fDiskServer);
00358          if (addr.IsValid()) {
00359             fqdn = addr.GetHostName();
00360             if (fqdn.EndsWith(".cern.ch") || fqdn.BeginsWith("137.138."))
00361                r = "rootug://";
00362             else
00363                r = "root://";
00364          } else
00365             r = "root://";
00366       } else {
00367          // Fix the format
00368          r += "://";
00369       }
00370 
00371       // Update fUrl with new path
00372       r += fDiskServer + "/";
00373       r += fInternalPath;
00374       TUrl rurl(r);
00375       fUrl = rurl;
00376 
00377       if (gDebug > 0)
00378          Info("FindServerAndPath"," fDiskServer: %s, r: %s", fDiskServer.Data(), r.Data());
00379 
00380       // Now ipath is not null and contains the real internal path on the disk
00381       // server 'host', e.g. it is fDiskServer:fInternalPath
00382       fInternalPath = stcp_output==0?0:stcp_output->ipath;
00383       if (stcp_output)
00384          free(stcp_output);
00385    } else {
00386 
00387 #ifdef R__CASTOR2
00388 
00389       // We use the new stager API
00390       // I use fname which has the Turl already parsed correctly
00391 
00392       int flags = O_RDONLY;
00393       int rc;
00394       struct stage_io_fileresp *response = 0;
00395       char *requestId = 0, *url = 0;
00396       char stageerrbuf[1025];
00397 
00398       // Prepare the flags
00399       if (fOption == "CREATE" || fOption == "RECREATE" || fOption == "UPDATE")
00400          flags |= O_RDWR;
00401       if (fOption == "CREATE" || fOption == "RECREATE")
00402          flags |= O_CREAT | O_TRUNC;
00403 
00404       stage_seterrbuf(stageerrbuf, 1024);
00405 
00406       int* auxVal;
00407       char ** auxPoint;
00408       struct stage_options opts;
00409       opts.stage_host=0;
00410       opts.stage_port=0;
00411       opts.service_class=0;
00412       opts.stage_version=0;
00413 
00414       void *ptrPoint = &auxPoint;
00415       void *ptrVal = &auxVal;
00416       int ret=Cglobals_get(& tStageHostKey, (void**)ptrPoint,sizeof(void*));
00417       if(ret==0){
00418          opts.stage_host=*auxPoint;
00419       }
00420       ret=Cglobals_get(& tStagePortKey, (void**)ptrVal,sizeof(int));
00421       if(ret==0){
00422          opts.stage_port=*auxVal;
00423       }
00424       opts.stage_version=2;
00425       ret=Cglobals_get(& tSvcClassKey, (void**)ptrPoint,sizeof(void*));
00426       if (ret==0){
00427          opts.service_class=*auxPoint;
00428       }
00429 
00430       // in stage_open I use the fname which is the result of the rfio_parsing
00431       rc = stage_open(0,
00432                       MOVER_PROTOCOL_ROOT,
00433                       (char *)fname.Data(),
00434                       flags,
00435                       (mode_t) 0666,
00436                       0,
00437                       &response,
00438                       &requestId,
00439                       &opts); // global values used as options
00440 
00441       if (rc != 0) {
00442          Error("FindServerAndPath", "stage_open failed: %s (%s)",
00443                sstrerror(serrno), stageerrbuf);
00444          if (response) free(response);
00445          if (requestId) free(requestId);
00446          return;
00447       }
00448 
00449       if (response == 0) {
00450          Error("FindServerAndPath", "response was null for %s (Request %s) %d/%s",
00451                fname.Data(), requestId,
00452                serrno, sstrerror(serrno));
00453          if (requestId) free(requestId);
00454          return;
00455       }
00456 
00457       if (response->errorCode != 0) {
00458          serrno = response->errorCode;
00459          Error("FindServerAndPath", "error getting file %s (Request %s) %d/%s",
00460                fname.Data(), requestId,
00461                serrno, sstrerror(serrno));
00462          free(response);
00463          if (requestId) free(requestId);
00464          return;
00465       }
00466 
00467       url = stage_geturl(response);
00468 
00469       if (url == 0) {
00470          Error("FindServerAndPath", "error getting file %s (Request %s) %d/%s",
00471                fname.Data(), requestId,
00472                serrno, sstrerror(serrno));
00473          free(response);
00474          if (requestId) free(requestId);
00475          return;
00476       }
00477 
00478       TUrl rurl(url);
00479       // Set the protocol prefix for TNetFile.
00480       // For the cern.ch domain we set the default authentication
00481       // method to UidGid, i.e. as for rfiod, unless there is a specific
00482       // request (from options or envs); for this we need
00483       // the full FQDN or address in "nnn.mmm.iii.jjj" form
00484       TString p = fAuthProto;
00485       if (fAuthProto == "root") {
00486          TString fqdn = rurl.GetHostFQDN();
00487          if (fqdn.EndsWith(".cern.ch") || fqdn.BeginsWith("137.138."))
00488             fAuthProto = "rootug";
00489       }
00490 
00491       // Update protocol and fUrl
00492       rurl.SetProtocol(fAuthProto);
00493       fUrl = rurl;
00494 
00495       if (response) free(response);
00496       if (url) free(url);
00497       if (requestId) free(requestId);
00498 #endif
00499 
00500    }
00501 
00502    fIsCastor = kTRUE;
00503 }
00504 
00505 //______________________________________________________________________________
00506 Int_t TCastorFile::SysClose(Int_t fd)
00507 {
00508    // Close currently open file.
00509 
00510    Int_t r = TNetFile::SysClose(fd);
00511 
00512    if (!UseCastor2API()) {
00513       if (fIsCastor && fWrittenTo) {
00514          // CASTOR file was created or modified
00515          rfio_HsmIf_reqtoput((char *)fInternalPath.Data());
00516          fWrittenTo = kFALSE;
00517       }
00518    }
00519 
00520    return r;
00521 }
00522 
00523 //______________________________________________________________________________
00524 Bool_t TCastorFile::WriteBuffer(const char *buf, Int_t len)
00525 {
00526    // Write specified byte range to remote file via rootd daemon.
00527    // Returns kTRUE in case of error.
00528 
00529    if (TNetFile::WriteBuffer(buf, len))
00530       return kTRUE;
00531 
00532    if (!UseCastor2API()) {
00533       if (fIsCastor && !fWrittenTo && len > 0) {
00534          stage_hsm_t hsmfile;
00535 
00536          // Change status of file in stage catalog from STAGED to STAGEOUT
00537          memset(&hsmfile, 0, sizeof(hsmfile));
00538          hsmfile.upath = StrDup(fInternalPath);
00539          if (stage_updc_filchg(0, &hsmfile) < 0) {
00540             Error("WriteBuffer", "error calling stage_updc_filchg");
00541             delete [] hsmfile.upath;
00542             return kTRUE;
00543          }
00544          delete [] hsmfile.upath;
00545          fWrittenTo = kTRUE;
00546       }
00547    }
00548 
00549    return kFALSE;
00550 }
00551 
00552 //______________________________________________________________________________
00553 void TCastorFile::ConnectServer(Int_t *stat, EMessageTypes *kind, Int_t netopt,
00554                                 Int_t tcpwindowsize, Bool_t forceOpen,
00555                                 Bool_t forceRead)
00556 {
00557    // Connect to remote rootd server on CASTOR disk server.
00558 
00559    FindServerAndPath();
00560 
00561    // Continue only if successful
00562    if (fIsCastor) {
00563       TNetFile::ConnectServer(stat, kind, netopt, tcpwindowsize, forceOpen, forceRead);
00564    } else {
00565       // Failure: fill these to signal it to TNetFile
00566       *stat = kErrFileOpen;
00567       *kind = kROOTD_ERR;
00568    }
00569 }

Generated on Tue Jul 5 14:28:34 2011 for ROOT_528-00b_version by  doxygen 1.5.1