Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TFile.cxx
Go to the documentation of this file.
1 // @(#)root/io:$Id: 3a19890259ad6443ee313e090166614971ad4296 $
2 // Author: Rene Brun 28/11/94
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /**
13  \defgroup IO Input/Output Library
14 
15  The library collecting the ROOT classes dedicated to data input and output.
16 
17 */
18 
19 /**
20 \file TFile.cxx
21 \class TFile
22 \ingroup IO
23 
24 A ROOT file is a suite of consecutive data records (TKey instances) with
25 a well defined format.
26 
27 If the key is located past the 32 bit file limit (> 2 GB) then some fields will
28 be 8 instead of 4 bytes:
29 
30 Byte Range | Member Name | Description
31 ----------------|-----------|--------------
32 1->4 | Nbytes | Length of compressed object (in bytes)
33 5->6 | Version | TKey version identifier
34 7->10 | ObjLen | Length of uncompressed object
35 11->14 | Datime | Date and time when object was written to file
36 15->16 | KeyLen | Length of the key structure (in bytes)
37 17->18 | Cycle | Cycle of key
38 19->22 [19->26] | SeekKey | Pointer to record itself (consistency check)
39 23->26 [27->34] | SeekPdir | Pointer to directory header
40 27->27 [35->35] | lname | Number of bytes in the class name
41 28->.. [36->..] | ClassName | Object Class Name
42 ..->.. | lname | Number of bytes in the object name
43 ..->.. | Name | lName bytes with the name of the object
44 ..->.. | lTitle | Number of bytes in the object title
45 ..->.. | Title | Title of the object
46 -----> | DATA | Data bytes associated to the object
47 
48 The first data record starts at byte fBEGIN (currently set to kBEGIN).
49 Bytes 1->kBEGIN contain the file description, when fVersion >= 1000000
50 it is a large file (> 2 GB) and the offsets will be 8 bytes long and
51 fUnits will be set to 8:
52 Byte Range | Record Name | Description
53 ----------------|-------------|------------
54 1->4 | "root" | Root file identifier
55 5->8 | fVersion | File format version
56 9->12 | fBEGIN | Pointer to first data record
57 13->16 [13->20] | fEND | Pointer to first free word at the EOF
58 17->20 [21->28] | fSeekFree | Pointer to FREE data record
59 21->24 [29->32] | fNbytesFree | Number of bytes in FREE data record
60 25->28 [33->36] | nfree | Number of free data records
61 29->32 [37->40] | fNbytesName | Number of bytes in TNamed at creation time
62 33->33 [41->41] | fUnits | Number of bytes for file pointers
63 34->37 [42->45] | fCompress | Compression level and algorithm
64 38->41 [46->53] | fSeekInfo | Pointer to TStreamerInfo record
65 42->45 [54->57] | fNbytesInfo | Number of bytes in TStreamerInfo record
66 46->63 [58->75] | fUUID | Universal Unique ID
67 
68 Begin_Macro
69 ../../../tutorials/io/file.C
70 End_Macro
71 The structure of a directory is shown in TDirectoryFile::TDirectoryFile
72 */
73 
74 #include <ROOT/RConfig.hxx>
75 
76 #ifdef R__LINUX
77 // for posix_fadvise
78 #ifndef _XOPEN_SOURCE
79 #define _XOPEN_SOURCE 600
80 #endif
81 #endif
82 #include <fcntl.h>
83 #include <errno.h>
84 #include <sys/stat.h>
85 #ifndef WIN32
86 # include <unistd.h>
87 #else
88 # define ssize_t int
89 # include <io.h>
90 # include <sys/types.h>
91 #endif
92 
93 #include "Bytes.h"
94 #include "Compression.h"
95 #include "Riostream.h"
96 #include "RConfigure.h"
97 #include "Strlen.h"
98 #include "TArrayC.h"
99 #include "TClass.h"
100 #include "TClassEdit.h"
101 #include "TClassTable.h"
102 #include "TDatime.h"
103 #include "TError.h"
104 #include "TFile.h"
105 #include "TFileCacheRead.h"
106 #include "TFileCacheWrite.h"
107 #include "TFree.h"
108 #include "TInterpreter.h"
109 #include "TKey.h"
110 #include "TMakeProject.h"
111 #include "TPluginManager.h"
112 #include "TProcessUUID.h"
113 #include "TRegexp.h"
114 #include "TPRegexp.h"
115 #include "TROOT.h"
116 #include "TStreamerInfo.h"
117 #include "TStreamerElement.h"
118 #include "TSystem.h"
119 #include "TTimeStamp.h"
120 #include "TVirtualPerfStats.h"
121 #include "TArchiveFile.h"
122 #include "TEnv.h"
123 #include "TVirtualMonitoring.h"
124 #include "TVirtualMutex.h"
125 #include "TMathBase.h"
126 #include "TObjString.h"
127 #include "TStopwatch.h"
128 #include "compiledata.h"
129 #include <cmath>
130 #include <set>
131 #include "TSchemaRule.h"
132 #include "TSchemaRuleSet.h"
133 #include "TThreadSlots.h"
134 #include "TGlobal.h"
135 #include "ROOT/RMakeUnique.hxx"
137 
138 using std::sqrt;
139 
140 std::atomic<Long64_t> TFile::fgBytesRead{0};
141 std::atomic<Long64_t> TFile::fgBytesWrite{0};
142 std::atomic<Long64_t> TFile::fgFileCounter{0};
143 std::atomic<Int_t> TFile::fgReadCalls{0};
144 Int_t TFile::fgReadaheadSize = 256000;
145 Bool_t TFile::fgReadInfo = kTRUE;
146 TList *TFile::fgAsyncOpenRequests = nullptr;
147 TString TFile::fgCacheFileDir;
148 Bool_t TFile::fgCacheFileForce = kFALSE;
149 Bool_t TFile::fgCacheFileDisconnected = kTRUE;
150 UInt_t TFile::fgOpenTimeout = TFile::kEternalTimeout;
151 Bool_t TFile::fgOnlyStaged = kFALSE;
152 #ifdef R__USE_IMT
153 ROOT::TRWSpinLock TFile::fgRwLock;
154 ROOT::Internal::RConcurrentHashColl TFile::fgTsSIHashes;
155 #endif
156 
157 const Int_t kBEGIN = 100;
158 
159 ClassImp(TFile);
160 
161 //*-*x17 macros/layout_file
162 // Needed to add the "fake" global gFile to the list of globals.
163 namespace {
164 static struct AddPseudoGlobals {
165 AddPseudoGlobals() {
166  // User "gCling" as synonym for "libCore static initialization has happened".
167  // This code here must not trigger it.
168  TGlobalMappedFunction::MakeFunctor("gFile", "TFile*", TFile::CurrentFile);
169 }
170 } gAddPseudoGlobals;
171 }
172 ////////////////////////////////////////////////////////////////////////////////
173 /// File default Constructor.
174 
175 TFile::TFile() : TDirectoryFile(), fCompress(ROOT::RCompressionSetting::EAlgorithm::kUseGlobal)
176 {
177  fCacheReadMap = new TMap();
178  SetBit(kBinaryFile, kTRUE);
179 
180  if (gDebug)
181  Info("TFile", "default ctor");
182 }
183 
184 ////////////////////////////////////////////////////////////////////////////////
185 /// Opens or creates a local ROOT file.
186 ///
187 /// \param[in] fname1 The name of the file
188 /// \param[in] option Specifies the mode in which the file is opened
189 /// \param[in] ftitle The title of the file
190 /// \param[in] compress Specifies the compression algorithm and level
191 ///
192 /// It is recommended to specify fname1 as "<file>.root". The suffix ".root"
193 /// will be used by object browsers to automatically identify the file as
194 /// a ROOT file. If the constructor fails in any way IsZombie() will
195 /// return true. Use IsOpen() to check if the file is (still) open.
196 /// To open non-local files use the static TFile::Open() method, that
197 /// will take care of opening the files using the correct remote file
198 /// access plugin.
199 ///
200 /// Option | Description
201 /// -------|------------
202 /// NEW or CREATE | Create a new file and open it for writing, if the file already exists the file is not opened.
203 /// RECREATE | Create a new file, if the file already exists it will be overwritten.
204 /// UPDATE | Open an existing file for writing. If no file exists, it is created.
205 /// READ | Open an existing file for reading (default).
206 /// NET | Used by derived remote file access classes, not a user callable option.
207 /// WEB | Used by derived remote http access class, not a user callable option.
208 ///
209 /// If option = "" (default), READ is assumed.
210 /// The file can be specified as a URL of the form:
211 ///
212 /// file:///user/rdm/bla.root or file:/user/rdm/bla.root
213 ///
214 /// The file can also be a member of an archive, in which case it is
215 /// specified as:
216 ///
217 /// multi.zip#file.root or multi.zip#0
218 ///
219 /// which will open file.root which is a member of the file multi.zip
220 /// archive or member 1 from the archive. For more on archive file
221 /// support see the TArchiveFile class.
222 /// TFile and its remote access plugins can also be used to open any
223 /// file, i.e. also non ROOT files, using:
224 ///
225 /// file.tar?filetype=raw
226 ///
227 /// This is convenient because the many remote file access plugins allow
228 /// easy access to/from the many different mass storage systems.
229 /// The title of the file (ftitle) will be shown by the ROOT browsers.
230 /// A ROOT file (like a Unix file system) may contain objects and
231 /// directories. There are no restrictions for the number of levels
232 /// of directories.
233 /// A ROOT file is designed such that one can write in the file in pure
234 /// sequential mode (case of BATCH jobs). In this case, the file may be
235 /// read sequentially again without using the file index written
236 /// at the end of the file. In case of a job crash, all the information
237 /// on the file is therefore protected.
238 /// A ROOT file can be used interactively. In this case, one has the
239 /// possibility to delete existing objects and add new ones.
240 /// When an object is deleted from the file, the freed space is added
241 /// into the FREE linked list (fFree). The FREE list consists of a chain
242 /// of consecutive free segments on the file. At the same time, the first
243 /// 4 bytes of the freed record on the file are overwritten by GAPSIZE
244 /// where GAPSIZE = -(Number of bytes occupied by the record).
245 /// Option compress is used to specify the compression level and algorithm:
246 ///
247 /// compress = 100 * algorithm + level
248 ///
249 /// Level | Explanation
250 /// ------|-------------
251 /// 0 | objects written to this file will not be compressed.
252 /// 1 | minimal compression level but fast.
253 /// ... | ....
254 /// 9 | maximal compression level but slower and might use more memory.
255 /// (For the currently supported algorithms, the maximum level is 9)
256 /// If compress is negative it indicates the compression level is not set yet.
257 /// The enumeration ROOT::RCompressionSetting::EAlgorithm associates each
258 /// algorithm with a number. There is a utility function to help
259 /// to set the value of compress. For example,
260 /// ROOT::CompressionSettings(ROOT::kLZMA, 1)
261 /// will build an integer which will set the compression to use
262 /// the LZMA algorithm and compression level 1. These are defined
263 /// in the header file <em>Compression.h</em>.
264 /// Note that the compression settings may be changed at any time.
265 /// The new compression settings will only apply to branches created
266 /// or attached after the setting is changed and other objects written
267 /// after the setting is changed.
268 /// In case the file does not exist or is not a valid ROOT file,
269 /// it is made a Zombie. One can detect this situation with a code like:
270 /// ~~~{.cpp}
271 /// TFile f("file.root");
272 /// if (f.IsZombie()) {
273 /// std::cout << "Error opening file" << std::endl;
274 /// exit(-1);
275 /// }
276 /// ~~~
277 /// When opening the file, the system checks the validity of this directory.
278 /// If something wrong is detected, an automatic Recovery is performed. In
279 /// this case, the file is scanned sequentially reading all logical blocks
280 /// and attempting to rebuild a correct directory (see TFile::Recover).
281 /// One can disable the automatic recovery procedure when reading one
282 /// or more files by setting the environment variable "TFile.Recover: 0"
283 /// in the system.rootrc file.
284 ///
285 /// A bit `TFile::kReproducible` can be enabled specifying
286 /// the `"reproducible"` url option when creating the file:
287 /// ~~~{.cpp}
288 /// TFile *f = TFile::Open("name.root?reproducible","RECREATE","File title");
289 /// ~~~
290 /// Unlike regular `TFile`s, the content of such file has reproducible binary
291 /// content when writing exactly same data. This achieved by writing pre-defined
292 /// values for creation and modification date of TKey/TDirectory objects and
293 /// null value for TUUID objects inside TFile. As drawback, TRef objects stored
294 /// in such file cannot be read correctly.
295 
296 TFile::TFile(const char *fname1, Option_t *option, const char *ftitle, Int_t compress)
297  : TDirectoryFile(), fCompress(compress), fUrl(fname1,kTRUE)
298 {
299  if (!gROOT)
300  ::Fatal("TFile::TFile", "ROOT system not initialized");
301 
302  // store name without the options as name and title
303  TString sfname1 = fname1;
304  if (sfname1.Index("?") != kNPOS) {
305  TString s = sfname1(0, sfname1.Index("?"));
306  SetName(s);
307  fNoAnchorInName = kTRUE;
308  } else
309  SetName(fname1);
310 
311  SetTitle(ftitle);
312 
313  // accept also URL like "file:..." syntax
314  fname1 = fUrl.GetFile();
315 
316  // if option contains filetype=raw then go into raw file mode
317  if (strstr(fUrl.GetOptions(), "filetype=raw"))
318  fIsRootFile = kFALSE;
319 
320  // if option contains filetype=pcm then go into ROOT PCM file mode
321  if (strstr(fUrl.GetOptions(), "filetype=pcm"))
322  fIsPcmFile = kTRUE;
323 
324  if (fUrl.HasOption("reproducible"))
325  SetBit(kReproducible);
326 
327  // We are opening synchronously
328  fAsyncOpenStatus = kAOSNotAsync;
329 
330  BuildDirectoryFile(this, nullptr);
331 
332  fVersion = gROOT->GetVersionInt(); //ROOT version in integer format
333  fUnits = 4;
334  fOption = option;
335  fCacheReadMap = new TMap();
336  SetBit(kBinaryFile, kTRUE);
337 
338  fOption.ToUpper();
339 
340  if (fIsRootFile && !fIsPcmFile && fOption != "NEW" && fOption != "CREATE"
341  && fOption != "RECREATE") {
342  // If !gPluginMgr then we are at startup and cannot handle plugins
343  // as TArchiveFile yet.
344  fArchive = gPluginMgr ? TArchiveFile::Open(fUrl.GetUrl(), this) : nullptr;
345  if (fArchive) {
346  fname1 = fArchive->GetArchiveName();
347  // if no archive member is specified then this TFile is just used
348  // to read the archive contents
349  if (!strlen(fArchive->GetMemberName()))
350  fIsArchive = kTRUE;
351  }
352  }
353 
354  if (fOption == "NET")
355  return;
356 
357  if (fOption == "WEB") {
358  fOption = "READ";
359  fWritable = kFALSE;
360  return;
361  }
362 
363  if (fOption == "NEW")
364  fOption = "CREATE";
365 
366  Bool_t create = (fOption == "CREATE") ? kTRUE : kFALSE;
367  Bool_t recreate = (fOption == "RECREATE") ? kTRUE : kFALSE;
368  Bool_t update = (fOption == "UPDATE") ? kTRUE : kFALSE;
369  Bool_t read = (fOption == "READ") ? kTRUE : kFALSE;
370  if (!create && !recreate && !update && !read) {
371  read = kTRUE;
372  fOption = "READ";
373  }
374 
375  Bool_t devnull = kFALSE;
376 
377  if (!fname1 || !fname1[0]) {
378  Error("TFile", "file name is not specified");
379  goto zombie;
380  }
381 
382  // support dumping to /dev/null on UNIX
383  if (!strcmp(fname1, "/dev/null") &&
384  !gSystem->AccessPathName(fname1, kWritePermission)) {
385  devnull = kTRUE;
386  create = kTRUE;
387  recreate = kFALSE;
388  update = kFALSE;
389  read = kFALSE;
390  fOption = "CREATE";
391  SetBit(kDevNull);
392  }
393 
394  const char *fname;
395  if ((fname = gSystem->ExpandPathName(fname1))) {
396  SetName(fname);
397  delete [] fname;
398  fRealName = GetName();
399  fname = fRealName.Data();
400  } else {
401  Error("TFile", "error expanding path %s", fname1);
402  goto zombie;
403  }
404 
405  if (recreate) {
406  if (!gSystem->AccessPathName(fname, kFileExists)) {
407  if (gSystem->Unlink(fname) != 0) {
408  SysError("TFile", "could not delete %s (errno: %d)",
409  fname, gSystem->GetErrno());
410  goto zombie;
411  }
412  }
413  recreate = kFALSE;
414  create = kTRUE;
415  fOption = "CREATE";
416  }
417  if (create && !devnull && !gSystem->AccessPathName(fname, kFileExists)) {
418  Error("TFile", "file %s already exists", fname);
419  goto zombie;
420  }
421  if (update) {
422  if (gSystem->AccessPathName(fname, kFileExists)) {
423  update = kFALSE;
424  create = kTRUE;
425  }
426  if (update && gSystem->AccessPathName(fname, kWritePermission)) {
427  Error("TFile", "no write permission, could not open file %s", fname);
428  goto zombie;
429  }
430  }
431  if (read) {
432  if (gSystem->AccessPathName(fname, kFileExists)) {
433  Error("TFile", "file %s does not exist", fname);
434  goto zombie;
435  }
436  if (gSystem->AccessPathName(fname, kReadPermission)) {
437  Error("TFile", "no read permission, could not open file %s", fname);
438  goto zombie;
439  }
440  }
441 
442  // Connect to file system stream
443  if (create || update) {
444 #ifndef WIN32
445  fD = TFile::SysOpen(fname, O_RDWR | O_CREAT, 0644);
446 #else
447  fD = TFile::SysOpen(fname, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
448 #endif
449  if (fD == -1) {
450  SysError("TFile", "file %s can not be opened", fname);
451  goto zombie;
452  }
453  fWritable = kTRUE;
454  } else {
455 #ifndef WIN32
456  fD = TFile::SysOpen(fname, O_RDONLY, 0644);
457 #else
458  fD = TFile::SysOpen(fname, O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
459 #endif
460  if (fD == -1) {
461  SysError("TFile", "file %s can not be opened for reading", fname);
462  goto zombie;
463  }
464  fWritable = kFALSE;
465  }
466 
467  // calling virtual methods from constructor not a good idea, but it is how code was developed
468  TFile::Init(create); // NOLINT: silence clang-tidy warnings
469 
470  return;
471 
472 zombie:
473  // error in file opening occurred, make this object a zombie
474  {
475  R__LOCKGUARD(gROOTMutex);
476  gROOT->GetListOfClosedObjects()->Add(this);
477  }
478  MakeZombie();
479  gDirectory = gROOT;
480 }
481 
482 ////////////////////////////////////////////////////////////////////////////////
483 /// File destructor.
484 
485 TFile::~TFile()
486 {
487  Close(); // NOLINT: silence clang-tidy warnings
488 
489  // In case where the TFile is still open at 'tear-down' time the order of operation will be
490  // call Close("nodelete")
491  // then later call delete TFile
492  // which means that at this point we might still have object held and those
493  // might requires a 'valid' TFile object in their desctructor (for example,
494  // TTree call's GetReadCache which expects a non-null fCacheReadMap).
495  // So delete the objects (if any) now.
496 
497  if (fList)
498  fList->Delete("slow");
499 
500  SafeDelete(fAsyncHandle);
501  SafeDelete(fCacheRead);
502  SafeDelete(fCacheReadMap);
503  SafeDelete(fCacheWrite);
504  SafeDelete(fProcessIDs);
505  SafeDelete(fFree);
506  SafeDelete(fArchive);
507  SafeDelete(fInfoCache);
508  SafeDelete(fOpenPhases);
509 
510  {
511  R__LOCKGUARD(gROOTMutex);
512  gROOT->GetListOfClosedObjects()->Remove(this);
513  gROOT->GetUUIDs()->RemoveUUID(GetUniqueID());
514  }
515 
516  if (IsOnHeap()) {
517  // Delete object from CINT symbol table so it can not be used anymore.
518  // CINT object are always on the heap.
519  gInterpreter->ResetGlobalVar(this);
520  }
521 
522  if (gDebug)
523  Info("~TFile", "dtor called for %s [%lx]", GetName(),(Long_t)this);
524 }
525 
526 ////////////////////////////////////////////////////////////////////////////////
527 /// Initialize a TFile object.
528 ///
529 /// \param[in] create Create a new file.
530 ///
531 /// TFile implementations providing asynchronous open functionality need to
532 /// override this method to run the appropriate checks before calling this
533 /// standard initialization part. See TXNetFile::Init for an example.
534 
535 void TFile::Init(Bool_t create)
536 {
537  if (fInitDone)
538  // Already called once
539  return;
540  fInitDone = kTRUE;
541 
542  if (!fIsRootFile) {
543  gDirectory = gROOT;
544  return;
545  }
546 
547  if (fArchive) {
548  if (fOption != "READ") {
549  Error("Init", "archive %s can only be opened in read mode", GetName());
550  delete fArchive;
551  fArchive = nullptr;
552  fIsArchive = kFALSE;
553  goto zombie;
554  }
555 
556  fArchive->OpenArchive();
557 
558  if (fIsArchive) return;
559 
560  // Make sure the anchor is in the name
561  if (!fNoAnchorInName)
562  if (!strchr(GetName(),'#'))
563  SetName(TString::Format("%s#%s", GetName(), fArchive->GetMemberName()));
564 
565  if (fArchive->SetCurrentMember() != -1)
566  fArchiveOffset = fArchive->GetMemberFilePosition();
567  else {
568  Error("Init", "member %s not found in archive %s",
569  fArchive->GetMemberName(), fArchive->GetArchiveName());
570  delete fArchive;
571  fArchive = nullptr;
572  fIsArchive = kFALSE;
573  goto zombie;
574  }
575  }
576 
577  Int_t nfree;
578  fBEGIN = (Long64_t)kBEGIN; //First used word in file following the file header
579 
580  // make newly opened file the current file and directory
581  cd();
582 
583  if (create) {
584  //*-*---------------NEW file
585  fFree = new TList;
586  fEND = fBEGIN; //Pointer to end of file
587  new TFree(fFree, fBEGIN, Long64_t(kStartBigFile)); //Create new free list
588 
589  //*-* Write Directory info
590  Int_t namelen= TNamed::Sizeof();
591  Int_t nbytes = namelen + TDirectoryFile::Sizeof();
592  TKey *key = new TKey(fName, fTitle, IsA(), nbytes, this);
593  fNbytesName = key->GetKeylen() + namelen;
594  fSeekDir = key->GetSeekKey();
595  fSeekFree = 0;
596  fNbytesFree = 0;
597  WriteHeader();
598  char *buffer = key->GetBuffer();
599  TNamed::FillBuffer(buffer);
600  TDirectoryFile::FillBuffer(buffer);
601  key->WriteFile();
602  delete key;
603  } else {
604  //*-*----------------UPDATE
605  //char *header = new char[kBEGIN];
606  char *header = new char[kBEGIN+200];
607  Seek(0); // NOLINT: silence clang-tidy warnings
608  //ReadBuffer(header, kBEGIN);
609  if (ReadBuffer(header, kBEGIN+200)) { // NOLINT: silence clang-tidy warnings
610  // ReadBuffer returns kTRUE in case of failure.
611  Error("Init","%s failed to read the file type data.",
612  GetName());
613  delete [] header;
614  goto zombie;
615  }
616 
617  // make sure this is a ROOT file
618  if (strncmp(header, "root", 4)) {
619  Error("Init", "%s not a ROOT file", GetName());
620  delete [] header;
621  goto zombie;
622  }
623 
624  char *buffer = header + 4; // skip the "root" file identifier
625  frombuf(buffer, &fVersion);
626  Int_t headerLength;
627  frombuf(buffer, &headerLength);
628  fBEGIN = (Long64_t)headerLength;
629  if (fVersion < 1000000) { //small file
630  Int_t send,sfree,sinfo;
631  frombuf(buffer, &send); fEND = (Long64_t)send;
632  frombuf(buffer, &sfree); fSeekFree= (Long64_t)sfree;
633  frombuf(buffer, &fNbytesFree);
634  frombuf(buffer, &nfree);
635  frombuf(buffer, &fNbytesName);
636  frombuf(buffer, &fUnits );
637  frombuf(buffer, &fCompress);
638  frombuf(buffer, &sinfo); fSeekInfo = (Long64_t)sinfo;
639  frombuf(buffer, &fNbytesInfo);
640  } else { // new format to support large files
641  frombuf(buffer, &fEND);
642  frombuf(buffer, &fSeekFree);
643  frombuf(buffer, &fNbytesFree);
644  frombuf(buffer, &nfree);
645  frombuf(buffer, &fNbytesName);
646  frombuf(buffer, &fUnits );
647  frombuf(buffer, &fCompress);
648  frombuf(buffer, &fSeekInfo);
649  frombuf(buffer, &fNbytesInfo);
650  }
651  if (fBEGIN < 0 || fBEGIN > fEND) {
652  // humm fBEGIN is wrong ....
653  Error("Init","file %s has an incorrect header length (%lld) or incorrect end of file length (%lld)",
654  GetName(),fBEGIN,fEND);
655  goto zombie;
656  }
657  fSeekDir = fBEGIN;
658  //*-*-------------Read Free segments structure if file is writable
659  if (fWritable) {
660  fFree = new TList;
661  if (fSeekFree > fBEGIN) {
662  ReadFree(); // NOLINT: silence clang-tidy warnings
663  } else {
664  Warning("Init","file %s probably not closed, cannot read free segments",GetName());
665  }
666  }
667  //*-*-------------Read directory info
668  // buffer_keyloc is the start of the key record.
669  char *buffer_keyloc = nullptr;
670 
671  Int_t nbytes = fNbytesName + TDirectoryFile::Sizeof();
672  if ( (nbytes + fBEGIN) > fEND) {
673  // humm fBEGIN is wrong ....
674  Error("Init","file %s has an incorrect header length (%lld) or incorrect end of file length (%lld)",
675  GetName(),fBEGIN+nbytes,fEND);
676  goto zombie;
677  }
678  if (nbytes+fBEGIN > kBEGIN+200) {
679  delete [] header;
680  header = new char[nbytes];
681  buffer = header;
682  Seek(fBEGIN); // NOLINT: silence clang-tidy warnings
683  if (ReadBuffer(buffer,nbytes)) { // NOLINT: silence clang-tidy warnings
684  // ReadBuffer returns kTRUE in case of failure.
685  Error("Init","%s failed to read the file header information at %lld (size=%d)",
686  GetName(),fBEGIN,nbytes);
687  delete [] header;
688  goto zombie;
689  }
690  buffer = header+fNbytesName;
691  buffer_keyloc = header;
692  } else {
693  buffer = header+fBEGIN+fNbytesName;
694  buffer_keyloc = header+fBEGIN;
695  }
696  Version_t version,versiondir;
697  frombuf(buffer,&version); versiondir = version%1000;
698  fDatimeC.ReadBuffer(buffer);
699  fDatimeM.ReadBuffer(buffer);
700  frombuf(buffer, &fNbytesKeys);
701  frombuf(buffer, &fNbytesName);
702  if (version > 1000) {
703  frombuf(buffer, &fSeekDir);
704  frombuf(buffer, &fSeekParent);
705  frombuf(buffer, &fSeekKeys);
706  } else {
707  Int_t sdir,sparent,skeys;
708  frombuf(buffer, &sdir); fSeekDir = (Long64_t)sdir;
709  frombuf(buffer, &sparent); fSeekParent = (Long64_t)sparent;
710  frombuf(buffer, &skeys); fSeekKeys = (Long64_t)skeys;
711  }
712  if (versiondir > 1) fUUID.ReadBuffer(buffer);
713 
714  //*-*---------read TKey::FillBuffer info
715  buffer_keyloc += sizeof(Int_t); // Skip NBytes;
716  Version_t keyversion;
717  frombuf(buffer_keyloc, &keyversion);
718  // Skip ObjLen, DateTime, KeyLen, Cycle, SeekKey, SeekPdir
719  if (keyversion > 1000) {
720  // Large files
721  buffer_keyloc += 2*sizeof(Int_t)+2*sizeof(Short_t)+2*sizeof(Long64_t);
722  } else {
723  buffer_keyloc += 2*sizeof(Int_t)+2*sizeof(Short_t)+2*sizeof(Int_t);
724  }
725  TString cname;
726  cname.ReadBuffer(buffer_keyloc);
727  cname.ReadBuffer(buffer_keyloc); // fName.ReadBuffer(buffer); file may have been renamed
728  fTitle.ReadBuffer(buffer_keyloc);
729  delete [] header;
730  if (fNbytesName < 10 || fNbytesName > 10000) {
731  Error("Init","cannot read directory info of file %s", GetName());
732  goto zombie;
733  }
734 
735  //*-* -------------Check if file is truncated
736  Long64_t size;
737  if ((size = GetSize()) == -1) { // NOLINT: silence clang-tidy warnings
738  Error("Init", "cannot stat the file %s", GetName());
739  goto zombie;
740  }
741 
742  //*-* -------------Check if, in case of inconsistencies, we are requested to
743  //*-* -------------attempt recovering the file
744  Bool_t tryrecover = (gEnv->GetValue("TFile.Recover", 1) == 1) ? kTRUE : kFALSE;
745 
746  //*-* -------------Read keys of the top directory
747  if (fSeekKeys > fBEGIN && fEND <= size) {
748  //normal case. Recover only if file has no keys
749  TDirectoryFile::ReadKeys(kFALSE);
750  gDirectory = this;
751  if (!GetNkeys()) {
752  if (tryrecover) {
753  Recover(); // NOLINT: silence clang-tidy warnings
754  } else {
755  Error("Init", "file %s has no keys", GetName());
756  goto zombie;
757  }
758  }
759  } else if ((fBEGIN+nbytes == fEND) && (fEND == size)) {
760  //the file might be open by another process and nothing written to the file yet
761  Warning("Init","file %s has no keys", GetName());
762  gDirectory = this;
763  } else {
764  //something had been written to the file. Trailer is missing, must recover
765  if (fEND > size) {
766  if (tryrecover) {
767  Error("Init","file %s is truncated at %lld bytes: should be %lld, "
768  "trying to recover", GetName(), size, fEND);
769  } else {
770  Error("Init","file %s is truncated at %lld bytes: should be %lld",
771  GetName(), size, fEND);
772  goto zombie;
773  }
774  } else {
775  if (tryrecover) {
776  Warning("Init","file %s probably not closed, "
777  "trying to recover", GetName());
778  } else {
779  Warning("Init","file %s probably not closed", GetName());
780  goto zombie;
781  }
782  }
783  Int_t nrecov = Recover(); // NOLINT: silence clang-tidy warnings
784  if (nrecov) {
785  Warning("Init", "successfully recovered %d keys", nrecov);
786  } else {
787  Warning("Init", "no keys recovered, file has been made a Zombie");
788  goto zombie;
789  }
790  }
791  }
792 
793  {
794  R__LOCKGUARD(gROOTMutex);
795  gROOT->GetListOfFiles()->Add(this);
796  gROOT->GetUUIDs()->AddUUID(fUUID,this);
797  }
798 
799  // Create StreamerInfo index
800  {
801  Int_t lenIndex = gROOT->GetListOfStreamerInfo()->GetSize()+1;
802  if (lenIndex < 5000) lenIndex = 5000;
803  fClassIndex = new TArrayC(lenIndex);
804  if (fgReadInfo) {
805  if (fSeekInfo > fBEGIN) {
806  ReadStreamerInfo(); // NOLINT: silence clang-tidy warnings
807  if (IsZombie()) {
808  R__LOCKGUARD(gROOTMutex);
809  gROOT->GetListOfFiles()->Remove(this);
810  goto zombie;
811  }
812  } else if (fVersion != gROOT->GetVersionInt() && fVersion > 30000) {
813  // Don't complain about missing streamer info for empty files.
814  if (fKeys->GetSize()) {
815  Warning("Init","no StreamerInfo found in %s therefore preventing schema evolution when reading this file."
816  " The file was produced with version %d.%02d/%02d of ROOT.",
817  GetName(), fVersion / 10000, (fVersion / 100) % (100), fVersion % 100);
818  }
819  }
820  }
821  }
822 
823  // Count number of TProcessIDs in this file
824  {
825  TIter next(fKeys);
826  TKey *key;
827  while ((key = (TKey*)next())) {
828  if (!strcmp(key->GetClassName(),"TProcessID")) fNProcessIDs++;
829  }
830  fProcessIDs = new TObjArray(fNProcessIDs+1);
831  }
832  return;
833 
834 zombie:
835  {
836  R__LOCKGUARD(gROOTMutex);
837  gROOT->GetListOfClosedObjects()->Add(this);
838  }
839  // error in file opening occurred, make this object a zombie
840  fWritable = kFALSE;
841  MakeZombie();
842  gDirectory = gROOT;
843 }
844 
845 ////////////////////////////////////////////////////////////////////////////////
846 /// Close a file.
847 ///
848 /// \param[in] option If option == "R", all TProcessIDs referenced by this file are deleted.
849 ///
850 /// Calling TFile::Close("R") might be necessary in case one reads a long list
851 /// of files having TRef, writing some of the referenced objects or TRef
852 /// to a new file. If the TRef or referenced objects of the file being closed
853 /// will not be referenced again, it is possible to minimize the size
854 /// of the TProcessID data structures in memory by forcing a delete of
855 /// the unused TProcessID.
856 
857 void TFile::Close(Option_t *option)
858 {
859  TString opt = option;
860 
861  opt.ToLower();
862 
863  if (!IsOpen()) return;
864 
865  if (fIsArchive || !fIsRootFile) {
866  FlushWriteCache();
867  SysClose(fD);
868  fD = -1;
869 
870  if (gMonitoringWriter)
871  gMonitoringWriter->SendFileCloseEvent(this);
872 
873  return;
874  }
875 
876  if (IsWritable()) {
877  WriteStreamerInfo();
878  }
879 
880  // Finish any concurrent I/O operations before we close the file handles.
881  if (fCacheRead) fCacheRead->Close();
882  {
883  TIter iter(fCacheReadMap);
884  TObject *key = nullptr;
885  while ((key = iter()) != nullptr) {
886  TFileCacheRead *cache = dynamic_cast<TFileCacheRead *>(fCacheReadMap->GetValue(key));
887  cache->Close();
888  }
889  }
890 
891  // Delete all supported directories structures from memory
892  // If gDirectory points to this object or any of the nested
893  // TDirectoryFile, TDirectoryFile::Close will induce the proper cd.
894  fMustFlush = kFALSE; // Make sure there is only one Flush.
895  TDirectoryFile::Close(option);
896 
897  if (IsWritable()) {
898  TFree *f1 = (TFree*)fFree->First();
899  if (f1) {
900  WriteFree(); //*-*- Write free segments linked list
901  WriteHeader(); //*-*- Now write file header ; this forces a Flush/fsync
902  } else {
903  Flush();
904  }
905  }
906  fMustFlush = kTRUE;
907 
908  FlushWriteCache();
909 
910  if (gMonitoringWriter)
911  gMonitoringWriter->SendFileCloseEvent(this);
912 
913  delete fClassIndex;
914  fClassIndex = nullptr;
915 
916  // Delete free segments from free list (but don't delete list header)
917  if (fFree) {
918  fFree->Delete();
919  }
920 
921  if (IsOpen()) {
922  SysClose(fD);
923  fD = -1;
924  }
925 
926  fWritable = kFALSE;
927 
928  // delete the TProcessIDs
929  TList pidDeleted;
930  TIter next(fProcessIDs);
931  TProcessID *pid;
932  while ((pid = (TProcessID*)next())) {
933  if (!pid->DecrementCount()) {
934  if (pid != TProcessID::GetSessionProcessID()) pidDeleted.Add(pid);
935  } else if(opt.Contains("r")) {
936  pid->Clear();
937  }
938  }
939  pidDeleted.Delete();
940 
941  if (!IsZombie()) {
942  R__LOCKGUARD(gROOTMutex);
943  gROOT->GetListOfFiles()->Remove(this);
944  gROOT->GetListOfBrowsers()->RecursiveRemove(this);
945  gROOT->GetListOfClosedObjects()->Add(this);
946  } else {
947  // If we are a zombie, we are already in the list of closed objects.
948  }
949 }
950 
951 ////////////////////////////////////////////////////////////////////////////////
952 /// Creates key for object and converts data to buffer.
953 
954 TKey* TFile::CreateKey(TDirectory* mother, const TObject* obj, const char* name, Int_t bufsize)
955 {
956  return new TKey(obj, name, bufsize, mother);
957 }
958 
959 ////////////////////////////////////////////////////////////////////////////////
960 /// Creates key for object and converts data to buffer.
961 
962 TKey* TFile::CreateKey(TDirectory* mother, const void* obj, const TClass* cl, const char* name, Int_t bufsize)
963 {
964  return new TKey(obj, cl, name, bufsize, mother);
965 }
966 
967 ////////////////////////////////////////////////////////////////////////////////
968 /// Return the current ROOT file if any.
969 ///
970 /// Note that if 'cd' has been called on a TDirectory that does not belong to a file,
971 /// gFile will be unchanged and still points to the file of the previous current
972 /// directory that was a file.
973 
974 TFile *&TFile::CurrentFile()
975 {
976  static TFile *currentFile = nullptr;
977  if (!gThreadTsd)
978  return currentFile;
979  else
980  return *(TFile**)(*gThreadTsd)(&currentFile,ROOT::kFileThreadSlot);
981 }
982 
983 ////////////////////////////////////////////////////////////////////////////////
984 /// Delete object namecycle.
985 ///
986 /// \param[in] namecycle Encodes the name and cycle of the objects to delete
987 ///
988 /// Namecycle identifies an object in the top directory of the file namecycle
989 /// has the format <em>name;cycle</em>.
990 /// - <em>name = *</em> means all objects
991 /// - <em>cycle = *</em> means all cycles (memory and keys)
992 /// - <em>cycle = ""</em> or cycle = 9999 ==> apply to a memory object
993 /// When name=* use T* to delete subdirectories also
994 ///
995 /// Examples:
996 /// name/cycle | Action
997 /// -----------|-------
998 /// foo | delete object named foo in memory
999 /// foo;1 | delete cycle 1 of foo on file
1000 /// foo;* | delete all cycles of foo on disk and also from memory
1001 /// *;2 | delete all objects on file having the cycle 2
1002 /// *;* | delete all objects from memory and file
1003 /// T*;* | delete all objects from memory and file and all subdirectories
1004 
1005 void TFile::Delete(const char *namecycle)
1006 {
1007  if (gDebug)
1008  Info("Delete", "deleting name = %s", namecycle);
1009 
1010  TDirectoryFile::Delete(namecycle);
1011 }
1012 
1013 ////////////////////////////////////////////////////////////////////////////////
1014 /// Fill Graphics Structure and Paint.
1015 ///
1016 /// Loop on all objects (memory or file) and all subdirectories.
1017 
1018 void TFile::Draw(Option_t *option)
1019 {
1020  GetList()->R__FOR_EACH(TObject,Draw)(option);
1021 }
1022 
1023 ////////////////////////////////////////////////////////////////////////////////
1024 /// Draw map of objects in this file.
1025 
1026 void TFile::DrawMap(const char *keys, Option_t *option)
1027 {
1028  TPluginHandler *h;
1029  if ((h = gROOT->GetPluginManager()->FindHandler("TFileDrawMap"))) {
1030  if (h->LoadPlugin() == -1)
1031  return;
1032  h->ExecPlugin(3, this, keys, option);
1033  }
1034 }
1035 
1036 ////////////////////////////////////////////////////////////////////////////////
1037 /// Synchronize a file's in-memory and on-disk states.
1038 
1039 void TFile::Flush()
1040 {
1041  if (IsOpen() && fWritable) {
1042  FlushWriteCache();
1043  if (SysSync(fD) < 0) {
1044  // Write the system error only once for this file
1045  SetBit(kWriteError); SetWritable(kFALSE);
1046  SysError("Flush", "error flushing file %s", GetName());
1047  }
1048  }
1049 }
1050 
1051 ////////////////////////////////////////////////////////////////////////////////
1052 /// Flush the write cache if active.
1053 ///
1054 /// Return kTRUE in case of error
1055 
1056 Bool_t TFile::FlushWriteCache()
1057 {
1058  if (fCacheWrite && IsOpen() && fWritable)
1059  return fCacheWrite->Flush();
1060  return kFALSE;
1061 }
1062 
1063 ////////////////////////////////////////////////////////////////////////////////
1064 /// Encode file output buffer.
1065 ///
1066 /// The file output buffer contains only the FREE data record.
1067 
1068 void TFile::FillBuffer(char *&buffer)
1069 {
1070  Version_t version = TFile::Class_Version();
1071  tobuf(buffer, version);
1072 }
1073 
1074 ////////////////////////////////////////////////////////////////////////////////
1075 /// Return the best buffer size of objects on this file.
1076 ///
1077 /// The best buffer size is estimated based on the current mean value
1078 /// and standard deviation of all objects written so far to this file.
1079 /// Returns mean value + one standard deviation.
1080 
1081 Int_t TFile::GetBestBuffer() const
1082 {
1083  if (!fWritten) return TBuffer::kInitialSize;
1084  Double_t mean = fSumBuffer/fWritten;
1085  Double_t rms2 = TMath::Abs(fSum2Buffer/fSumBuffer -mean*mean);
1086  Double_t result = mean + sqrt(rms2);
1087  if (result >= (double)std::numeric_limits<Int_t>::max()) {
1088  return std::numeric_limits<Int_t>::max() -1;
1089  } else {
1090  return (Int_t)result;
1091  }
1092 }
1093 
1094 ////////////////////////////////////////////////////////////////////////////////
1095 /// Return the file compression factor.
1096 ///
1097 /// Add total number of compressed/uncompressed bytes for each key.
1098 /// Returns the ratio of the two.
1099 
1100 Float_t TFile::GetCompressionFactor()
1101 {
1102  Short_t keylen;
1103  UInt_t datime;
1104  Int_t nbytes, objlen, nwh = 64;
1105  char *header = new char[fBEGIN];
1106  char *buffer;
1107  Long64_t idcur = fBEGIN;
1108  Float_t comp,uncomp;
1109  comp = uncomp = fBEGIN;
1110 
1111  while (idcur < fEND-100) {
1112  Seek(idcur);
1113  if (ReadBuffer(header, nwh)) {
1114  // ReadBuffer returns kTRUE in case of failure.
1115 // Error("GetCompressionFactor","%s failed to read the key header information at %lld (size=%d).",
1116 // GetName(),idcur,nwh);
1117  break;
1118  }
1119  buffer=header;
1120  frombuf(buffer, &nbytes);
1121  if (nbytes < 0) {
1122  idcur -= nbytes;
1123  Seek(idcur);
1124  continue;
1125  }
1126  if (nbytes == 0) break; //this may happen when the file is corrupted
1127  Version_t versionkey;
1128  frombuf(buffer, &versionkey);
1129  frombuf(buffer, &objlen);
1130  frombuf(buffer, &datime);
1131  frombuf(buffer, &keylen);
1132  if (!objlen) objlen = nbytes-keylen;
1133  comp += nbytes;
1134  uncomp += keylen + objlen;
1135  idcur += nbytes;
1136  }
1137  delete [] header;
1138  return uncomp/comp;
1139 }
1140 
1141 ////////////////////////////////////////////////////////////////////////////////
1142 /// Method returning errno.
1143 
1144 Int_t TFile::GetErrno() const
1145 {
1146  return TSystem::GetErrno();
1147 }
1148 
1149 ////////////////////////////////////////////////////////////////////////////////
1150 /// Method resetting the errno.
1151 
1152 void TFile::ResetErrno() const
1153 {
1154  TSystem::ResetErrno();
1155 }
1156 
1157 ////////////////////////////////////////////////////////////////////////////////
1158 /// Return a pointer to the current read cache.
1159 
1160 TFileCacheRead *TFile::GetCacheRead(const TObject* tree) const
1161 {
1162  if (!tree) {
1163  if (!fCacheRead && fCacheReadMap->GetSize() == 1) {
1164  TIter next(fCacheReadMap);
1165  return (TFileCacheRead *)fCacheReadMap->GetValue(next());
1166  }
1167  return fCacheRead;
1168  }
1169  TFileCacheRead *cache = (TFileCacheRead *)fCacheReadMap->GetValue(tree);
1170  if (!cache) return fCacheRead;
1171  return cache;
1172 }
1173 
1174 ////////////////////////////////////////////////////////////////////////////////
1175 /// Return a pointer to the current write cache.
1176 
1177 TFileCacheWrite *TFile::GetCacheWrite() const
1178 {
1179  return fCacheWrite;
1180 }
1181 
1182 ////////////////////////////////////////////////////////////////////////////////
1183 /// Read the logical record header starting at a certain postion.
1184 ///
1185 /// \param[in] maxbytes Bytes which are read into buf.
1186 /// \param[out] nbytes Number of bytes in record if negative, this is a deleted
1187 /// record if 0, cannot read record, wrong value of argument first
1188 /// \param[out] objlen Uncompressed object size
1189 /// \param[out] keylen Length of logical record header
1190 ///
1191 /// The function reads nread bytes
1192 /// where nread is the minimum of maxbytes and the number of bytes
1193 /// before the end of file. The function returns nread.
1194 /// Note that the arguments objlen and keylen are returned only
1195 /// if maxbytes >=16
1196 
1197 Int_t TFile::GetRecordHeader(char *buf, Long64_t first, Int_t maxbytes, Int_t &nbytes, Int_t &objlen, Int_t &keylen)
1198 {
1199  nbytes = 0;
1200  objlen = 0;
1201  keylen = 0;
1202  if (first < fBEGIN) return 0;
1203  if (first > fEND) return 0;
1204  Seek(first);
1205  Int_t nread = maxbytes;
1206  if (first+maxbytes > fEND) nread = fEND-maxbytes;
1207  if (nread < 4) {
1208  Warning("GetRecordHeader","%s: parameter maxbytes = %d must be >= 4",
1209  GetName(), nread);
1210  return nread;
1211  }
1212  if (ReadBuffer(buf,nread)) {
1213  // ReadBuffer return kTRUE in case of failure.
1214  Warning("GetRecordHeader","%s: failed to read header data (maxbytes = %d)",
1215  GetName(), nread);
1216  return nread;
1217  }
1218  Version_t versionkey;
1219  Short_t klen;
1220  UInt_t datime;
1221  Int_t nb,olen;
1222  char *buffer = buf;
1223  frombuf(buffer,&nb);
1224  nbytes = nb;
1225  if (nb < 0) return nread;
1226  // const Int_t headerSize = Int_t(sizeof(nb) +sizeof(versionkey) +sizeof(olen) +sizeof(datime) +sizeof(klen));
1227  const Int_t headerSize = 16;
1228  if (nread < headerSize) return nread;
1229  frombuf(buffer, &versionkey);
1230  frombuf(buffer, &olen);
1231  frombuf(buffer, &datime);
1232  frombuf(buffer, &klen);
1233  if (!olen) olen = nbytes-klen;
1234  objlen = olen;
1235  keylen = klen;
1236  return nread;
1237 }
1238 
1239 ////////////////////////////////////////////////////////////////////////////////
1240 /// Returns the current file size. Returns -1 in case the file could not
1241 /// be stat'ed.
1242 
1243 Long64_t TFile::GetSize() const
1244 {
1245  Long64_t size;
1246 
1247  if (fArchive && fArchive->GetMember()) {
1248  size = fArchive->GetMember()->GetDecompressedSize();
1249  } else {
1250  Long_t id, flags, modtime;
1251  if (const_cast<TFile*>(this)->SysStat(fD, &id, &size, &flags, &modtime)) { // NOLINT: silence clang-tidy warnings
1252  Error("GetSize", "cannot stat the file %s", GetName());
1253  return -1;
1254  }
1255  }
1256  return size;
1257 }
1258 
1259 ////////////////////////////////////////////////////////////////////////////////
1260 /// Returns the cached list of StreamerInfos used in this file.
1261 
1262 const TList *TFile::GetStreamerInfoCache()
1263 {
1264  return fInfoCache ? fInfoCache : (fInfoCache=GetStreamerInfoList());
1265 }
1266 
1267 ////////////////////////////////////////////////////////////////////////////////
1268 /// See documentation of GetStreamerInfoList for more details.
1269 /// This is an internal method which returns the list of streamer infos and also
1270 /// information about the success of the operation.
1271 
1272 TFile::InfoListRet TFile::GetStreamerInfoListImpl(bool lookupSICache)
1273 {
1274  ROOT::Internal::RConcurrentHashColl::HashValue hash;
1275 
1276  if (fIsPcmFile) return {nullptr, 1, hash}; // No schema evolution for ROOT PCM files.
1277 
1278  TList *list = nullptr;
1279  if (fSeekInfo) {
1280  TDirectory::TContext ctxt(this); // gFile and gDirectory used in ReadObj
1281  auto key = std::make_unique<TKey>(this);
1282  std::vector<char> buffer(fNbytesInfo+1);
1283  auto buf = buffer.data();
1284  Seek(fSeekInfo); // NOLINT: silence clang-tidy warnings
1285  if (ReadBuffer(buf,fNbytesInfo)) { // NOLINT: silence clang-tidy warnings
1286  // ReadBuffer returns kTRUE in case of failure.
1287  Warning("GetRecordHeader","%s: failed to read the StreamerInfo data from disk.",
1288  GetName());
1289  return {nullptr, 1, hash};
1290  }
1291 
1292 #ifdef R__USE_IMT
1293  if (lookupSICache) {
1294  hash = fgTsSIHashes.Hash(buf, fNbytesInfo);
1295  if (fgTsSIHashes.Find(hash)) {
1296  if (gDebug > 0) Info("GetStreamerInfo", "The streamer info record for file %s has already been treated, skipping it.", GetName());
1297  return {nullptr, 0, hash};
1298  }
1299  }
1300 #else
1301  (void) lookupSICache;
1302 #endif
1303  key->ReadKeyBuffer(buf);
1304  list = dynamic_cast<TList*>(key->ReadObjWithBuffer(buffer.data()));
1305  if (list) list->SetOwner();
1306  } else {
1307  list = (TList*)Get("StreamerInfo"); //for versions 2.26 (never released)
1308  }
1309 
1310  if (!list) {
1311  Info("GetStreamerInfoList", "cannot find the StreamerInfo record in file %s",
1312  GetName());
1313  return {nullptr, 1, hash};
1314  }
1315 
1316  return {list, 0, hash};
1317 }
1318 
1319 ////////////////////////////////////////////////////////////////////////////////
1320 /// Read the list of TStreamerInfo objects written to this file.
1321 ///
1322 /// The function returns a TList. It is the user's responsibility
1323 /// to delete the list created by this function.
1324 ///
1325 /// Note the list, in addition to TStreamerInfo object, contains sometimes
1326 /// a TList named 'listOfRules' and containing the schema evolution rules
1327 /// related to the file's content.
1328 ///
1329 /// Using the list, one can access additional information, e.g.:
1330 /// ~~~{.cpp}
1331 /// TFile f("myfile.root");
1332 /// auto list = f.GetStreamerInfoList();
1333 /// auto info = dynamic_cast<TStreamerInfo*>(list->FindObject("MyClass"));
1334 /// if (info) auto classversionid = info->GetClassVersion();
1335 /// delete list;
1336 /// ~~~
1337 ///
1338 
1339 TList *TFile::GetStreamerInfoList()
1340 {
1341  return GetStreamerInfoListImpl(/*lookupSICache*/ false).fList;
1342 }
1343 
1344 ////////////////////////////////////////////////////////////////////////////////
1345 /// List file contents.
1346 ///
1347 /// Indentation is used to identify the file tree.
1348 /// Subdirectories are listed first, then objects in memory,
1349 /// then objects on the file.
1350 
1351 void TFile::ls(Option_t *option) const
1352 {
1353  TROOT::IndentLevel();
1354  std::cout <<ClassName()<<"**\t\t"<<GetName()<<"\t"<<GetTitle()<<std::endl;
1355  TROOT::IncreaseDirLevel();
1356  TDirectoryFile::ls(option);
1357  TROOT::DecreaseDirLevel();
1358 }
1359 
1360 ////////////////////////////////////////////////////////////////////////////////
1361 /// Returns kTRUE in case file is open and kFALSE if file is not open.
1362 
1363 Bool_t TFile::IsOpen() const
1364 {
1365  return fD == -1 ? kFALSE : kTRUE;
1366 }
1367 
1368 ////////////////////////////////////////////////////////////////////////////////
1369 /// Mark unused bytes on the file.
1370 ///
1371 /// The list of free segments is in the fFree linked list.
1372 /// When an object is deleted from the file, the freed space is added
1373 /// into the FREE linked list (fFree). The FREE list consists of a chain
1374 /// of consecutive free segments on the file. At the same time, the first
1375 /// 4 bytes of the freed record on the file are overwritten by GAPSIZE
1376 /// where GAPSIZE = -(Number of bytes occupied by the record).
1377 
1378 void TFile::MakeFree(Long64_t first, Long64_t last)
1379 {
1380  TFree *f1 = (TFree*)fFree->First();
1381  if (!f1) return;
1382  TFree *newfree = f1->AddFree(fFree,first,last);
1383  if(!newfree) return;
1384  Long64_t nfirst = newfree->GetFirst();
1385  Long64_t nlast = newfree->GetLast();
1386  Long64_t nbytesl= nlast-nfirst+1;
1387  if (nbytesl > 2000000000) nbytesl = 2000000000;
1388  Int_t nbytes = -Int_t (nbytesl);
1389  Int_t nb = sizeof(Int_t);
1390  char * buffer = new char[nb];
1391  char * psave = buffer;
1392  tobuf(buffer, nbytes);
1393  if (last == fEND-1) fEND = nfirst;
1394  Seek(nfirst);
1395  // We could not update the meta data for this block on the file.
1396  // This is not fatal as this only means that we won't get it 'right'
1397  // if we ever need to Recover the file before the block is actually
1398  // (attempted to be reused.
1399  // coverity[unchecked_value]
1400  WriteBuffer(psave, nb);
1401  if (fMustFlush) Flush();
1402  delete [] psave;
1403 }
1404 
1405 ////////////////////////////////////////////////////////////////////////////////
1406 /// List the contents of a file sequentially.
1407 /// For each logical record found, it prints:
1408 ///
1409 /// Date/Time Record_Adress Logical_Record_Length ClassName CompressionFactor
1410 ///
1411 /// Example of output
1412 ///
1413 /// 20010404/150437 At:64 N=150 TFile
1414 /// 20010404/150440 At:214 N=28326 TBasket CX = 1.13
1415 /// 20010404/150440 At:28540 N=29616 TBasket CX = 1.08
1416 /// 20010404/150440 At:58156 N=29640 TBasket CX = 1.08
1417 /// 20010404/150440 At:87796 N=29076 TBasket CX = 1.10
1418 /// 20010404/150440 At:116872 N=10151 TBasket CX = 3.15
1419 /// 20010404/150441 At:127023 N=28341 TBasket CX = 1.13
1420 /// 20010404/150441 At:155364 N=29594 TBasket CX = 1.08
1421 /// 20010404/150441 At:184958 N=29616 TBasket CX = 1.08
1422 /// 20010404/150441 At:214574 N=29075 TBasket CX = 1.10
1423 /// 20010404/150441 At:243649 N=9583 TBasket CX = 3.34
1424 /// 20010404/150442 At:253232 N=28324 TBasket CX = 1.13
1425 /// 20010404/150442 At:281556 N=29641 TBasket CX = 1.08
1426 /// 20010404/150442 At:311197 N=29633 TBasket CX = 1.08
1427 /// 20010404/150442 At:340830 N=29091 TBasket CX = 1.10
1428 /// 20010404/150442 At:369921 N=10341 TBasket CX = 3.09
1429 /// 20010404/150442 At:380262 N=509 TH1F CX = 1.93
1430 /// 20010404/150442 At:380771 N=1769 TH2F CX = 4.32
1431 /// 20010404/150442 At:382540 N=1849 TProfile CX = 1.65
1432 /// 20010404/150442 At:384389 N=18434 TNtuple CX = 4.51
1433 /// 20010404/150442 At:402823 N=307 KeysList
1434 /// 20010404/150443 At:403130 N=4548 StreamerInfo CX = 3.65
1435 /// 20010404/150443 At:407678 N=86 FreeSegments
1436 /// 20010404/150443 At:407764 N=1 END
1437 ///
1438 /// If the parameter opt contains "forComp", the Date/Time is ommitted
1439 /// and the decompressed size is also printed.
1440 ///
1441 /// Record_Adress Logical_Record_Length Key_Length Object_Record_Length ClassName CompressionFactor
1442 ///
1443 /// Example of output
1444 ///
1445 
1446 
1447 void TFile::Map(Option_t *opt)
1448 {
1449  TString options(opt);
1450  options.ToLower();
1451  bool forComp = options.Contains("forcomp");
1452 
1453  Short_t keylen,cycle;
1454  UInt_t datime;
1455  Int_t nbytes,date,time,objlen,nwheader;
1456  date = 0;
1457  time = 0;
1458  Long64_t seekkey,seekpdir;
1459  char *buffer;
1460  char nwhc;
1461  Long64_t idcur = fBEGIN;
1462 
1463  nwheader = 64;
1464  Int_t nread = nwheader;
1465 
1466  char header[kBEGIN];
1467  char classname[512];
1468 
1469  unsigned char nDigits = std::log10(fEND) + 1;
1470 
1471  while (idcur < fEND) {
1472  Seek(idcur);
1473  if (idcur+nread >= fEND) nread = fEND-idcur-1;
1474  if (ReadBuffer(header, nread)) {
1475  // ReadBuffer returns kTRUE in case of failure.
1476  Warning("Map","%s: failed to read the key data from disk at %lld.",
1477  GetName(),idcur);
1478  break;
1479  }
1480 
1481  buffer=header;
1482  frombuf(buffer, &nbytes);
1483  if (!nbytes) {
1484  Printf("Address = %lld\tNbytes = %d\t=====E R R O R=======", idcur, nbytes);
1485  date = 0; time = 0;
1486  break;
1487  }
1488  if (nbytes < 0) {
1489  Printf("Address = %lld\tNbytes = %d\t=====G A P===========", idcur, nbytes);
1490  idcur -= nbytes;
1491  Seek(idcur);
1492  continue;
1493  }
1494  Version_t versionkey;
1495  frombuf(buffer, &versionkey);
1496  frombuf(buffer, &objlen);
1497  frombuf(buffer, &datime);
1498  frombuf(buffer, &keylen);
1499  frombuf(buffer, &cycle);
1500  if (versionkey > 1000) {
1501  frombuf(buffer, &seekkey);
1502  frombuf(buffer, &seekpdir);
1503  } else {
1504  Int_t skey,sdir;
1505  frombuf(buffer, &skey); seekkey = (Long64_t)skey;
1506  frombuf(buffer, &sdir); seekpdir = (Long64_t)sdir;
1507  }
1508  frombuf(buffer, &nwhc);
1509  for (int i = 0;i < nwhc; i++) frombuf(buffer, &classname[i]);
1510  classname[(int)nwhc] = '\0'; //cast to avoid warning with gcc3.4
1511  if (idcur == fSeekFree) strlcpy(classname,"FreeSegments",512);
1512  if (idcur == fSeekInfo) strlcpy(classname,"StreamerInfo",512);
1513  if (idcur == fSeekKeys) strlcpy(classname,"KeysList",512);
1514  TDatime::GetDateTime(datime, date, time);
1515  if (!forComp) {
1516  if (objlen != nbytes - keylen) {
1517  Float_t cx = Float_t(objlen + keylen) / Float_t(nbytes);
1518  Printf("%d/%06d At:%-*lld N=%-8d %-14s CX = %5.2f", date, time, nDigits + 1, idcur, nbytes, classname,
1519  cx);
1520  } else {
1521  Printf("%d/%06d At:%-*lld N=%-8d %-14s", date, time, nDigits + 1, idcur, nbytes, classname);
1522  }
1523  } else {
1524  // Printing to help compare two files.
1525  if (objlen != nbytes - keylen) {
1526  Float_t cx = Float_t(objlen + keylen) / Float_t(nbytes);
1527  Printf("At:%-*lld N=%-8d K=%-3d O=%-8d %-14s CX = %5.2f", nDigits+1, idcur, nbytes, keylen, objlen, classname, cx);
1528  } else {
1529  Printf("At:%-*lld N=%-8d K=%-3d O=%-8d %-14s CX = 1", nDigits+1, idcur, nbytes, keylen, objlen, classname);
1530  }
1531  }
1532  idcur += nbytes;
1533  }
1534  if (!forComp)
1535  Printf("%d/%06d At:%-*lld N=%-8d %-14s",date,time, nDigits+1, idcur,1,"END");
1536  else
1537  Printf("At:%-*lld N=%-8d K= O= %-14s", nDigits+1, idcur,1,"END");
1538 }
1539 
1540 ////////////////////////////////////////////////////////////////////////////////
1541 /// Paint all objects in the file.
1542 
1543 void TFile::Paint(Option_t *option)
1544 {
1545  GetList()->R__FOR_EACH(TObject,Paint)(option);
1546 }
1547 
1548 ////////////////////////////////////////////////////////////////////////////////
1549 /// Print all objects in the file.
1550 
1551 void TFile::Print(Option_t *option) const
1552 {
1553  Printf("TFile: name=%s, title=%s, option=%s", GetName(), GetTitle(), GetOption());
1554  GetList()->R__FOR_EACH(TObject,Print)(option);
1555 }
1556 
1557 ////////////////////////////////////////////////////////////////////////////////
1558 /// Read a buffer from the file at the offset 'pos' in the file.
1559 ///
1560 /// Returns kTRUE in case of failure.
1561 /// Compared to ReadBuffer(char*, Int_t), this routine does _not_
1562 /// change the cursor on the physical file representation (fD)
1563 /// if the data is in this TFile's cache.
1564 
1565 Bool_t TFile::ReadBuffer(char *buf, Long64_t pos, Int_t len)
1566 {
1567  if (IsOpen()) {
1568 
1569  SetOffset(pos);
1570 
1571  Int_t st;
1572  Double_t start = 0;
1573  if (gPerfStats) start = TTimeStamp();
1574 
1575  if ((st = ReadBufferViaCache(buf, len))) {
1576  if (st == 2)
1577  return kTRUE;
1578  return kFALSE;
1579  }
1580 
1581  Seek(pos);
1582  ssize_t siz;
1583 
1584  while ((siz = SysRead(fD, buf, len)) < 0 && GetErrno() == EINTR)
1585  ResetErrno();
1586 
1587  if (siz < 0) {
1588  SysError("ReadBuffer", "error reading from file %s", GetName());
1589  return kTRUE;
1590  }
1591  if (siz != len) {
1592  Error("ReadBuffer", "error reading all requested bytes from file %s, got %ld of %d",
1593  GetName(), (Long_t)siz, len);
1594  return kTRUE;
1595  }
1596  fBytesRead += siz;
1597  fgBytesRead += siz;
1598  fReadCalls++;
1599  fgReadCalls++;
1600 
1601  if (gMonitoringWriter)
1602  gMonitoringWriter->SendFileReadProgress(this);
1603  if (gPerfStats) {
1604  gPerfStats->FileReadEvent(this, len, start);
1605  }
1606  return kFALSE;
1607  }
1608  return kTRUE;
1609 }
1610 
1611 ////////////////////////////////////////////////////////////////////////////////
1612 /// Read a buffer from the file. This is the basic low level read operation.
1613 /// Returns kTRUE in case of failure.
1614 
1615 Bool_t TFile::ReadBuffer(char *buf, Int_t len)
1616 {
1617  if (IsOpen()) {
1618 
1619  Int_t st;
1620  if ((st = ReadBufferViaCache(buf, len))) {
1621  if (st == 2)
1622  return kTRUE;
1623  return kFALSE;
1624  }
1625 
1626  ssize_t siz;
1627  Double_t start = 0;
1628 
1629  if (gPerfStats) start = TTimeStamp();
1630 
1631  while ((siz = SysRead(fD, buf, len)) < 0 && GetErrno() == EINTR)
1632  ResetErrno();
1633 
1634  if (siz < 0) {
1635  SysError("ReadBuffer", "error reading from file %s", GetName());
1636  return kTRUE;
1637  }
1638  if (siz != len) {
1639  Error("ReadBuffer", "error reading all requested bytes from file %s, got %ld of %d",
1640  GetName(), (Long_t)siz, len);
1641  return kTRUE;
1642  }
1643  fBytesRead += siz;
1644  fgBytesRead += siz;
1645  fReadCalls++;
1646  fgReadCalls++;
1647 
1648  if (gMonitoringWriter)
1649  gMonitoringWriter->SendFileReadProgress(this);
1650  if (gPerfStats) {
1651  gPerfStats->FileReadEvent(this, len, start);
1652  }
1653  return kFALSE;
1654  }
1655  return kTRUE;
1656 }
1657 
1658 ////////////////////////////////////////////////////////////////////////////////
1659 /// Read the nbuf blocks described in arrays pos and len.
1660 ///
1661 /// The value pos[i] is the seek position of block i of length len[i].
1662 /// Note that for nbuf=1, this call is equivalent to TFile::ReafBuffer.
1663 /// This function is overloaded by TNetFile, TWebFile, etc.
1664 /// Returns kTRUE in case of failure.
1665 
1666 Bool_t TFile::ReadBuffers(char *buf, Long64_t *pos, Int_t *len, Int_t nbuf)
1667 {
1668  // called with buf=0, from TFileCacheRead to pass list of readahead buffers
1669  if (!buf) {
1670  for (Int_t j = 0; j < nbuf; j++) {
1671  if (ReadBufferAsync(pos[j], len[j])) {
1672  return kTRUE;
1673  }
1674  }
1675  return kFALSE;
1676  }
1677 
1678  Int_t k = 0;
1679  Bool_t result = kTRUE;
1680  TFileCacheRead *old = fCacheRead;
1681  fCacheRead = nullptr;
1682  Long64_t curbegin = pos[0];
1683  Long64_t cur;
1684  char *buf2 = nullptr;
1685  Int_t i = 0, n = 0;
1686  while (i < nbuf) {
1687  cur = pos[i]+len[i];
1688  Bool_t bigRead = kTRUE;
1689  if (cur -curbegin < fgReadaheadSize) {n++; i++; bigRead = kFALSE;}
1690  if (bigRead || (i>=nbuf)) {
1691  if (n == 0) {
1692  //if the block to read is about the same size as the read-ahead buffer
1693  //we read the block directly
1694  Seek(pos[i]);
1695  result = ReadBuffer(&buf[k], len[i]);
1696  if (result) break;
1697  k += len[i];
1698  i++;
1699  } else {
1700  //otherwise we read all blocks that fit in the read-ahead buffer
1701  Seek(curbegin);
1702  if (!buf2) buf2 = new char[fgReadaheadSize];
1703  //we read ahead
1704  Long64_t nahead = pos[i-1]+len[i-1]-curbegin;
1705  result = ReadBuffer(buf2, nahead);
1706  if (result) break;
1707  //now copy from the read-ahead buffer to the cache
1708  Int_t kold = k;
1709  for (Int_t j=0;j<n;j++) {
1710  memcpy(&buf[k],&buf2[pos[i-n+j]-curbegin],len[i-n+j]);
1711  k += len[i-n+j];
1712  }
1713  Int_t nok = k-kold;
1714  Long64_t extra = nahead-nok;
1715  fBytesReadExtra += extra;
1716  fBytesRead -= extra;
1717  fgBytesRead -= extra;
1718  n = 0;
1719  }
1720  curbegin = i < nbuf ? pos[i] : 0;
1721  }
1722  }
1723  if (buf2) delete [] buf2;
1724  fCacheRead = old;
1725  return result;
1726 }
1727 
1728 ////////////////////////////////////////////////////////////////////////////////
1729 /// Read buffer via cache.
1730 ///
1731 /// Returns 0 if the requested block is not in the cache, 1 in case read via
1732 /// cache was successful, 2 in case read via cache failed.
1733 
1734 Int_t TFile::ReadBufferViaCache(char *buf, Int_t len)
1735 {
1736  Long64_t off = GetRelOffset();
1737  if (fCacheRead) {
1738  Int_t st = fCacheRead->ReadBuffer(buf, off, len);
1739  if (st < 0)
1740  return 2; // failure reading
1741  else if (st == 1) {
1742  // fOffset might have been changed via TFileCacheRead::ReadBuffer(), reset it
1743  SetOffset(off + len);
1744  return 1;
1745  }
1746  // fOffset might have been changed via TFileCacheRead::ReadBuffer(), reset it
1747  Seek(off);
1748  } else {
1749  // if write cache is active check if data still in write cache
1750  if (fWritable && fCacheWrite) {
1751  if (fCacheWrite->ReadBuffer(buf, off, len) == 0) {
1752  SetOffset(off + len);
1753  return 1;
1754  }
1755  // fOffset might have been changed via TFileCacheWrite::ReadBuffer(), reset it
1756  SetOffset(off);
1757  }
1758  }
1759 
1760  return 0;
1761 }
1762 
1763 ////////////////////////////////////////////////////////////////////////////////
1764 /// Read the FREE linked list.
1765 ///
1766 /// Every file has a linked list (fFree) of free segments.
1767 /// This linked list has been written on the file via WriteFree
1768 /// as a single data record.
1769 
1770 void TFile::ReadFree()
1771 {
1772  // Avoid problem with file corruption.
1773  if (fNbytesFree < 0 || fNbytesFree > fEND) {
1774  fNbytesFree = 0;
1775  return;
1776  }
1777  TKey *headerfree = new TKey(fSeekFree, fNbytesFree, this);
1778  headerfree->ReadFile();
1779  char *buffer = headerfree->GetBuffer();
1780  headerfree->ReadKeyBuffer(buffer);
1781  buffer = headerfree->GetBuffer();
1782  while (1) {
1783  TFree *afree = new TFree();
1784  afree->ReadBuffer(buffer);
1785  fFree->Add(afree);
1786  if (afree->GetLast() > fEND) break;
1787  }
1788  delete headerfree;
1789 }
1790 
1791 ////////////////////////////////////////////////////////////////////////////////
1792 /// The TProcessID with number pidf is read from this file.
1793 ///
1794 /// If the object is not already entered in the gROOT list, it is added.
1795 
1796 TProcessID *TFile::ReadProcessID(UShort_t pidf)
1797 {
1798  TProcessID *pid = nullptr;
1799  TObjArray *pids = GetListOfProcessIDs();
1800  if (pidf < pids->GetSize()) pid = (TProcessID *)pids->UncheckedAt(pidf);
1801  if (pid) {
1802  pid->CheckInit();
1803  return pid;
1804  }
1805 
1806  //check if fProcessIDs[uid] is set in file
1807  //if not set, read the process uid from file
1808  char pidname[32];
1809  snprintf(pidname,32,"ProcessID%d",pidf);
1810  pid = (TProcessID *)Get(pidname);
1811  if (gDebug > 0) {
1812  printf("ReadProcessID, name=%s, file=%s, pid=%lx\n",pidname,GetName(),(Long_t)pid);
1813  }
1814  if (!pid) {
1815  //file->Error("ReadProcessID","Cannot find %s in file %s",pidname,file->GetName());
1816  return pid;
1817  }
1818 
1819  //check that a similar pid is not already registered in fgPIDs
1820  TObjArray *pidslist = TProcessID::GetPIDs();
1821  TIter next(pidslist);
1822  TProcessID *p;
1823  bool found = false;
1824  R__RWLOCK_ACQUIRE_READ(fgRwLock);
1825  while ((p = (TProcessID*)next())) {
1826  if (!strcmp(p->GetTitle(),pid->GetTitle())) {
1827  found = true;
1828  break;
1829  }
1830  }
1831  R__RWLOCK_RELEASE_READ(fgRwLock);
1832 
1833  if (found) {
1834  delete pid;
1835  pids->AddAtAndExpand(p,pidf);
1836  p->IncrementCount();
1837  return p;
1838  }
1839 
1840  pids->AddAtAndExpand(pid,pidf);
1841  pid->IncrementCount();
1842 
1843  R__RWLOCK_ACQUIRE_WRITE(fgRwLock);
1844  pidslist->Add(pid);
1845  Int_t ind = pidslist->IndexOf(pid);
1846  pid->SetUniqueID((UInt_t)ind);
1847  R__RWLOCK_RELEASE_WRITE(fgRwLock);
1848 
1849  return pid;
1850 }
1851 
1852 
1853 ////////////////////////////////////////////////////////////////////////////////
1854 /// Attempt to recover file if not correctly closed
1855 ///
1856 /// The function returns the number of keys that have been recovered.
1857 /// If no keys can be recovered, the file will be declared Zombie by
1858 /// the calling function. This function is automatically called when
1859 /// opening a file.
1860 /// If the file is open in read only mode, the file is not modified.
1861 /// If open in update mode and the function finds something to recover,
1862 /// a new directory header is written to the file. When opening the file gain
1863 /// no message from Recover will be reported.
1864 /// If keys have been recovered, the file is usable and you can safely
1865 /// read the corresponding objects.
1866 /// If the file is not usable (a zombie), you can test for this case
1867 /// with code like:
1868 ///
1869 /// ~~~{.cpp}
1870 /// TFile f("myfile.root");
1871 /// if (f.IsZombie()) {<actions to take if file is unusable>}
1872 /// ~~~
1873 ///
1874 /// If the file has been recovered, the bit kRecovered is set in the TFile object in memory.
1875 /// You can test if the file has been recovered with
1876 ///
1877 /// if (f.TestBit(TFile::kRecovered)) {... the file has been recovered}
1878 ///
1879 /// When writing TTrees to a file, it is important to save the Tree header
1880 /// at regular intervals (see TTree::AutoSave). If a file containing a Tree
1881 /// is recovered, the last Tree header written to the file will be used.
1882 /// In this case all the entries in all the branches written before writing
1883 /// the header are valid entries.
1884 /// One can disable the automatic recovery procedure by setting
1885 ///
1886 /// TFile.Recover 0
1887 ///
1888 /// in the <em>system.rootrc</em> file.
1889 
1890 Int_t TFile::Recover()
1891 {
1892  Short_t keylen,cycle;
1893  UInt_t datime;
1894  Int_t nbytes,date,time,objlen,nwheader;
1895  Long64_t seekkey,seekpdir;
1896  char header[1024];
1897  char *buffer, *bufread;
1898  char nwhc;
1899  Long64_t idcur = fBEGIN;
1900 
1901  Long64_t size;
1902  if ((size = GetSize()) == -1) { // NOLINT: silence clang-tidy warnings
1903  Error("Recover", "cannot stat the file %s", GetName());
1904  return 0;
1905  }
1906 
1907  fEND = Long64_t(size);
1908 
1909  if (fWritable && !fFree) fFree = new TList;
1910 
1911  TKey *key;
1912  Int_t nrecov = 0;
1913  nwheader = 1024;
1914  Int_t nread = nwheader;
1915 
1916  while (idcur < fEND) {
1917  Seek(idcur); // NOLINT: silence clang-tidy warnings
1918  if (idcur+nread >= fEND) nread = fEND-idcur-1;
1919  if (ReadBuffer(header, nread)) { // NOLINT: silence clang-tidy warnings
1920  // ReadBuffer returns kTRUE in case of failure.
1921  Error("Recover","%s: failed to read the key data from disk at %lld.",
1922  GetName(),idcur);
1923  break;
1924  }
1925  buffer = header;
1926  bufread = header;
1927  frombuf(buffer, &nbytes);
1928  if (!nbytes) {
1929  Error("Recover","Address = %lld\tNbytes = %d\t=====E R R O R=======", idcur, nbytes);
1930  break;
1931  }
1932  if (nbytes < 0) {
1933  idcur -= nbytes;
1934  if (fWritable) new TFree(fFree,idcur,idcur-nbytes-1);
1935  Seek(idcur);
1936  continue;
1937  }
1938  Version_t versionkey;
1939  frombuf(buffer, &versionkey);
1940  frombuf(buffer, &objlen);
1941  frombuf(buffer, &datime);
1942  frombuf(buffer, &keylen);
1943  frombuf(buffer, &cycle);
1944  if (versionkey > 1000) {
1945  frombuf(buffer, &seekkey);
1946  frombuf(buffer, &seekpdir);
1947  } else {
1948  Int_t skey,sdir;
1949  frombuf(buffer, &skey); seekkey = (Long64_t)skey;
1950  frombuf(buffer, &sdir); seekpdir = (Long64_t)sdir;
1951  }
1952  frombuf(buffer, &nwhc);
1953  char *classname = nullptr;
1954  if (nwhc <= 0 || nwhc > 100) break;
1955  classname = new char[nwhc+1];
1956  int i, nwhci = nwhc;
1957  for (i = 0;i < nwhc; i++) frombuf(buffer, &classname[i]);
1958  classname[nwhci] = '\0';
1959  TDatime::GetDateTime(datime, date, time);
1960  TClass *tclass = TClass::GetClass(classname);
1961  if (seekpdir == fSeekDir && tclass && !tclass->InheritsFrom(TFile::Class())
1962  && strcmp(classname,"TBasket")) {
1963  key = new TKey(this);
1964  key->ReadKeyBuffer(bufread);
1965  if (!strcmp(key->GetName(),"StreamerInfo")) {
1966  fSeekInfo = seekkey;
1967  SafeDelete(fInfoCache);
1968  fNbytesInfo = nbytes;
1969  } else {
1970  AppendKey(key);
1971  nrecov++;
1972  SetBit(kRecovered);
1973  Info("Recover", "%s, recovered key %s:%s at address %lld",GetName(),key->GetClassName(),key->GetName(),idcur);
1974  }
1975  }
1976  delete [] classname;
1977  idcur += nbytes;
1978  }
1979  if (fWritable) {
1980  Long64_t max_file_size = Long64_t(kStartBigFile);
1981  if (max_file_size < fEND) max_file_size = fEND+1000000000;
1982  TFree *last = (TFree*)fFree->Last();
1983  if (last) {
1984  last->AddFree(fFree,fEND,max_file_size);
1985  } else {
1986  new TFree(fFree,fEND,max_file_size);
1987  }
1988  if (nrecov) Write();
1989  }
1990  return nrecov;
1991 }
1992 
1993 ////////////////////////////////////////////////////////////////////////////////
1994 /// Reopen a file with a different access mode.
1995 ///
1996 /// For example, it is possible to change from READ to
1997 /// UPDATE or from NEW, CREATE, RECREATE, UPDATE to READ. Thus the
1998 /// mode argument can be either "READ" or "UPDATE". The method returns
1999 /// 0 in case the mode was successfully modified, 1 in case the mode
2000 /// did not change (was already as requested or wrong input arguments)
2001 /// and -1 in case of failure, in which case the file cannot be used
2002 /// anymore. The current directory (gFile) is changed to this file.
2003 
2004 Int_t TFile::ReOpen(Option_t *mode)
2005 {
2006  cd();
2007 
2008  TString opt = mode;
2009  opt.ToUpper();
2010 
2011  if (opt != "READ" && opt != "UPDATE") {
2012  Error("ReOpen", "mode must be either READ or UPDATE, not %s", opt.Data());
2013  return 1;
2014  }
2015 
2016  if (opt == fOption || (opt == "UPDATE" && fOption == "CREATE"))
2017  return 1;
2018 
2019  if (opt == "READ") {
2020  // switch to READ mode
2021 
2022  // flush data still in the pipeline and close the file
2023  if (IsOpen() && IsWritable()) {
2024  WriteStreamerInfo();
2025 
2026  // save directory key list and header
2027  Save();
2028 
2029  TFree *f1 = (TFree*)fFree->First();
2030  if (f1) {
2031  WriteFree(); // write free segments linked list
2032  WriteHeader(); // now write file header
2033  }
2034 
2035  FlushWriteCache();
2036 
2037  // delete free segments from free list
2038  fFree->Delete();
2039  SafeDelete(fFree);
2040 
2041  SysClose(fD);
2042  fD = -1;
2043 
2044  SetWritable(kFALSE);
2045  }
2046 
2047  // open in READ mode
2048  fOption = opt; // set fOption before SysOpen() for TNetFile
2049 #ifndef WIN32
2050  fD = SysOpen(fRealName, O_RDONLY, 0644);
2051 #else
2052  fD = SysOpen(fRealName, O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
2053 #endif
2054  if (fD == -1) {
2055  SysError("ReOpen", "file %s can not be opened in read mode", GetName());
2056  return -1;
2057  }
2058  SetWritable(kFALSE);
2059 
2060  } else {
2061  // switch to UPDATE mode
2062 
2063  // close readonly file
2064  if (IsOpen()) {
2065  SysClose(fD);
2066  fD = -1;
2067  }
2068 
2069  // open in UPDATE mode
2070  fOption = opt; // set fOption before SysOpen() for TNetFile
2071 #ifndef WIN32
2072  fD = SysOpen(fRealName, O_RDWR | O_CREAT, 0644);
2073 #else
2074  fD = SysOpen(fRealName, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
2075 #endif
2076  if (fD == -1) {
2077  SysError("ReOpen", "file %s can not be opened in update mode", GetName());
2078  return -1;
2079  }
2080  SetWritable(kTRUE);
2081 
2082  fFree = new TList;
2083  if (fSeekFree > fBEGIN)
2084  ReadFree();
2085  else
2086  Warning("ReOpen","file %s probably not closed, cannot read free segments", GetName());
2087  }
2088 
2089  return 0;
2090 }
2091 
2092 ////////////////////////////////////////////////////////////////////////////////
2093 /// Set position from where to start reading.
2094 
2095 void TFile::SetOffset(Long64_t offset, ERelativeTo pos)
2096 {
2097  switch (pos) {
2098  case kBeg:
2099  fOffset = offset + fArchiveOffset;
2100  break;
2101  case kCur:
2102  fOffset += offset;
2103  break;
2104  case kEnd:
2105  // this option is not used currently in the ROOT code
2106  if (fArchiveOffset)
2107  Error("SetOffset", "seeking from end in archive is not (yet) supported");
2108  fOffset = fEND + offset; // is fEND really EOF or logical EOF?
2109  break;
2110  }
2111 }
2112 
2113 ////////////////////////////////////////////////////////////////////////////////
2114 /// Seek to a specific position in the file. Pos it either kBeg, kCur or kEnd.
2115 
2116 void TFile::Seek(Long64_t offset, ERelativeTo pos)
2117 {
2118  int whence = 0;
2119  switch (pos) {
2120  case kBeg:
2121  whence = SEEK_SET;
2122  offset += fArchiveOffset;
2123  break;
2124  case kCur:
2125  whence = SEEK_CUR;
2126  break;
2127  case kEnd:
2128  whence = SEEK_END;
2129  // this option is not used currently in the ROOT code
2130  if (fArchiveOffset)
2131  Error("Seek", "seeking from end in archive is not (yet) supported");
2132  break;
2133  }
2134  Long64_t retpos;
2135  if ((retpos = SysSeek(fD, offset, whence)) < 0) // NOLINT: silence clang-tidy warnings
2136  SysError("Seek", "cannot seek to position %lld in file %s, retpos=%lld",
2137  offset, GetName(), retpos);
2138 
2139  // used by TFileCacheRead::ReadBuffer()
2140  fOffset = retpos;
2141 }
2142 
2143 ////////////////////////////////////////////////////////////////////////////////
2144 /// See comments for function SetCompressionSettings
2145 ///
2146 
2147 void TFile::SetCompressionAlgorithm(Int_t algorithm)
2148 {
2149  if (algorithm < 0 || algorithm >= ROOT::RCompressionSetting::EAlgorithm::kUndefined) algorithm = 0;
2150  if (fCompress < 0) {
2151  fCompress = 100 * algorithm + ROOT::RCompressionSetting::ELevel::kUseMin;
2152  } else {
2153  int level = fCompress % 100;
2154  fCompress = 100 * algorithm + level;
2155  }
2156 }
2157 
2158 ////////////////////////////////////////////////////////////////////////////////
2159 /// See comments for function SetCompressionSettings
2160 
2161 void TFile::SetCompressionLevel(Int_t level)
2162 {
2163  if (level < 0) level = 0;
2164  if (level > 99) level = 99;
2165  if (fCompress < 0) {
2166  // if the algorithm is not defined yet use 0 as a default
2167  fCompress = level;
2168  } else {
2169  int algorithm = fCompress / 100;
2170  if (algorithm >= ROOT::RCompressionSetting::EAlgorithm::kUndefined) algorithm = 0;
2171  fCompress = 100 * algorithm + level;
2172  }
2173 }
2174 
2175 ////////////////////////////////////////////////////////////////////////////////
2176 /// Used to specify the compression level and algorithm.
2177 ///
2178 /// See the TFile constructor for the details.
2179 
2180 void TFile::SetCompressionSettings(Int_t settings)
2181 {
2182  fCompress = settings;
2183 }
2184 
2185 ////////////////////////////////////////////////////////////////////////////////
2186 /// Set a pointer to the read cache.
2187 ///
2188 /// <b>This relinquishes ownership</b> of the previous cache, so if you do not
2189 /// already have a pointer to the previous cache (and there was a previous
2190 /// cache), you ought to retrieve (and delete it if needed) using:
2191 ///
2192 /// TFileCacheRead *older = myfile->GetCacheRead();
2193 ///
2194 /// The action specifies how to behave when detaching a cache from the
2195 /// the TFile. If set to (default) kDisconnect, the contents of the cache
2196 /// will be flushed when it is removed from the file, and it will disconnect
2197 /// the cache object from the file. In almost all cases, this is what you want.
2198 /// If you want to disconnect the cache temporarily from this tree and re-attach
2199 /// later to the same fil, you can set action to kDoNotDisconnect. This will allow
2200 /// things like prefetching to continue in the background while it is no longer the
2201 /// default cache for the TTree. Except for a few expert use cases, kDisconnect is
2202 /// likely the correct setting.
2203 ///
2204 /// WARNING: if action=kDoNotDisconnect, you MUST delete the cache before TFile.
2205 ///
2206 
2207 void TFile::SetCacheRead(TFileCacheRead *cache, TObject* tree, ECacheAction action)
2208 {
2209  if (tree) {
2210  if (cache) fCacheReadMap->Add(tree, cache);
2211  else {
2212  // The only addition to fCacheReadMap is via an interface that takes
2213  // a TFileCacheRead* so the C-cast is safe.
2214  TFileCacheRead* tpf = (TFileCacheRead *)fCacheReadMap->GetValue(tree);
2215  fCacheReadMap->Remove(tree);
2216  if (tpf && (tpf->GetFile() == this) && (action != kDoNotDisconnect)) tpf->SetFile(0, action);
2217  }
2218  }
2219  if (cache) cache->SetFile(this, action);
2220  else if (!tree && fCacheRead && (action != kDoNotDisconnect)) fCacheRead->SetFile(0, action);
2221  // For backward compatibility the last Cache set is the default cache.
2222  fCacheRead = cache;
2223 }
2224 
2225 ////////////////////////////////////////////////////////////////////////////////
2226 /// Set a pointer to the write cache.
2227 ///
2228 /// If file is null the existing write cache is deleted.
2229 
2230 void TFile::SetCacheWrite(TFileCacheWrite *cache)
2231 {
2232  if (!cache && fCacheWrite) delete fCacheWrite;
2233  fCacheWrite = cache;
2234 }
2235 
2236 ////////////////////////////////////////////////////////////////////////////////
2237 /// Return the size in bytes of the file header.
2238 
2239 Int_t TFile::Sizeof() const
2240 {
2241  return 0;
2242 }
2243 
2244 ////////////////////////////////////////////////////////////////////////////////
2245 /// Stream a TFile object.
2246 
2247 void TFile::Streamer(TBuffer &b)
2248 {
2249  if (b.IsReading()) {
2250  b.ReadVersion(); //Version_t v = b.ReadVersion();
2251  } else {
2252  b.WriteVersion(TFile::IsA());
2253  }
2254 }
2255 
2256 ////////////////////////////////////////////////////////////////////////////////
2257 /// Increment statistics for buffer sizes of objects in this file.
2258 
2259 void TFile::SumBuffer(Int_t bufsize)
2260 {
2261  fWritten++;
2262  fSumBuffer += double(bufsize);
2263  fSum2Buffer += double(bufsize) * double(bufsize); // avoid reaching MAXINT for temporary
2264 }
2265 
2266 ////////////////////////////////////////////////////////////////////////////////
2267 /// Write memory objects to this file.
2268 ///
2269 /// Loop on all objects in memory (including subdirectories).
2270 /// A new key is created in the KEYS linked list for each object.
2271 /// The list of keys is then saved on the file (via WriteKeys)
2272 /// as a single data record.
2273 /// For values of opt see TObject::Write().
2274 /// The directory header info is rewritten on the directory header record.
2275 /// The linked list of FREE segments is written.
2276 /// The file header is written (bytes 1->fBEGIN).
2277 
2278 Int_t TFile::Write(const char *, Int_t opt, Int_t bufsiz)
2279 {
2280  if (!IsWritable()) {
2281  if (!TestBit(kWriteError)) {
2282  // Do not print the warning if we already had a SysError.
2283  Warning("Write", "file %s not opened in write mode", GetName());
2284  }
2285  return 0;
2286  }
2287 
2288  if (gDebug) {
2289  if (!GetTitle() || strlen(GetTitle()) == 0)
2290  Info("Write", "writing name = %s", GetName());
2291  else
2292  Info("Write", "writing name = %s title = %s", GetName(), GetTitle());
2293  }
2294 
2295  fMustFlush = kFALSE;
2296  Int_t nbytes = TDirectoryFile::Write(0, opt, bufsiz); // Write directory tree
2297  WriteStreamerInfo();
2298  WriteFree(); // Write free segments linked list
2299  WriteHeader(); // Now write file header
2300  fMustFlush = kTRUE;
2301 
2302  return nbytes;
2303 }
2304 
2305 ////////////////////////////////////////////////////////////////////////////////
2306 /// One can not save a const TDirectory object.
2307 
2308 Int_t TFile::Write(const char *n, Int_t opt, Int_t bufsize) const
2309 {
2310  Error("Write const","A const TFile object should not be saved. We try to proceed anyway.");
2311  return const_cast<TFile*>(this)->Write(n, opt, bufsize);
2312 }
2313 
2314 ////////////////////////////////////////////////////////////////////////////////
2315 /// Write a buffer to the file. This is the basic low level write operation.
2316 /// Returns kTRUE in case of failure.
2317 
2318 Bool_t TFile::WriteBuffer(const char *buf, Int_t len)
2319 {
2320  if (IsOpen() && fWritable) {
2321 
2322  Int_t st;
2323  if ((st = WriteBufferViaCache(buf, len))) {
2324  if (st == 2)
2325  return kTRUE;
2326  return kFALSE;
2327  }
2328 
2329  ssize_t siz;
2330  gSystem->IgnoreInterrupt();
2331  while ((siz = SysWrite(fD, buf, len)) < 0 && GetErrno() == EINTR) // NOLINT: silence clang-tidy warnings
2332  ResetErrno(); // NOLINT: silence clang-tidy warnings
2333  gSystem->IgnoreInterrupt(kFALSE);
2334  if (siz < 0) {
2335  // Write the system error only once for this file
2336  SetBit(kWriteError); SetWritable(kFALSE);
2337  SysError("WriteBuffer", "error writing to file %s (%ld)", GetName(), (Long_t)siz);
2338  return kTRUE;
2339  }
2340  if (siz != len) {
2341  SetBit(kWriteError);
2342  Error("WriteBuffer", "error writing all requested bytes to file %s, wrote %ld of %d",
2343  GetName(), (Long_t)siz, len);
2344  return kTRUE;
2345  }
2346  fBytesWrite += siz;
2347  fgBytesWrite += siz;
2348 
2349  if (gMonitoringWriter)
2350  gMonitoringWriter->SendFileWriteProgress(this);
2351 
2352  return kFALSE;
2353  }
2354  return kTRUE;
2355 }
2356 
2357 ////////////////////////////////////////////////////////////////////////////////
2358 /// Write buffer via cache. Returns 0 if cache is not active, 1 in case
2359 /// write via cache was successful, 2 in case write via cache failed.
2360 
2361 Int_t TFile::WriteBufferViaCache(const char *buf, Int_t len)
2362 {
2363  if (!fCacheWrite) return 0;
2364 
2365  Int_t st;
2366  Long64_t off = GetRelOffset();
2367  if ((st = fCacheWrite->WriteBuffer(buf, off, len)) < 0) {
2368  SetBit(kWriteError);
2369  Error("WriteBuffer", "error writing to cache");
2370  return 2;
2371  }
2372  if (st > 0) {
2373  // fOffset might have been changed via TFileCacheWrite::WriteBuffer(), reset it
2374  Seek(off + len);
2375  return 1;
2376  }
2377  return 0;
2378 }
2379 
2380 ////////////////////////////////////////////////////////////////////////////////
2381 /// Write FREE linked list on the file.
2382 /// The linked list of FREE segments (fFree) is written as a single data
2383 /// record.
2384 
2385 void TFile::WriteFree()
2386 {
2387  //*-* Delete old record if it exists
2388  if (fSeekFree != 0) {
2389  MakeFree(fSeekFree, fSeekFree + fNbytesFree -1);
2390  }
2391 
2392  Bool_t largeFile = (fEND > TFile::kStartBigFile);
2393 
2394  auto createKey = [this]() {
2395  Int_t nbytes = 0;
2396  TFree *afree;
2397  TIter next (fFree);
2398  while ((afree = (TFree*) next())) {
2399  nbytes += afree->Sizeof();
2400  }
2401  if (!nbytes) return (TKey*)nullptr;
2402 
2403  TKey *key = new TKey(fName,fTitle,IsA(),nbytes,this);
2404 
2405  if (key->GetSeekKey() == 0) {
2406  delete key;
2407  return (TKey*)nullptr;
2408  }
2409  return key;
2410  };
2411 
2412  TKey *key = createKey();
2413  if (!key) return;
2414 
2415  if (!largeFile && (fEND > TFile::kStartBigFile)) {
2416  // The free block list is large enough to bring the file to larger
2417  // than 2Gb, the references/offsets are now 64bits in the output
2418  // so we need to redo the calculation since the list of free block
2419  // information will not fit in the original size.
2420  key->Delete();
2421  delete key;
2422 
2423  key = createKey();
2424  if (!key) return;
2425  }
2426 
2427  Int_t nbytes = key->GetObjlen();
2428  char *buffer = key->GetBuffer();
2429  char *start = buffer;
2430 
2431  TIter next (fFree);
2432  TFree *afree;
2433  while ((afree = (TFree*) next())) {
2434  // We could 'waste' time here and double check that
2435  // (buffer+afree->Sizeof() < (start+nbytes)
2436  afree->FillBuffer(buffer);
2437  }
2438  auto actualBytes = buffer-start;
2439  if ( actualBytes != nbytes ) {
2440  if (actualBytes < nbytes) {
2441  // Most likely one of the 'free' segment was used to store this
2442  // TKey, so we had one less TFree to store than we planned.
2443  memset(buffer,0,nbytes-actualBytes);
2444  } else {
2445  Error("WriteFree","The free block list TKey wrote more data than expected (%d vs %ld). Most likely there has been an out-of-bound write.",nbytes,(long int)actualBytes);
2446  }
2447  }
2448  fNbytesFree = key->GetNbytes();
2449  fSeekFree = key->GetSeekKey();
2450  key->WriteFile();
2451  delete key;
2452 }
2453 
2454 ////////////////////////////////////////////////////////////////////////////////
2455 /// Write File Header.
2456 
2457 void TFile::WriteHeader()
2458 {
2459  SafeDelete(fInfoCache);
2460  TFree *lastfree = (TFree*)fFree->Last();
2461  if (lastfree) fEND = lastfree->GetFirst();
2462  const char *root = "root";
2463  char *psave = new char[fBEGIN];
2464  char *buffer = psave;
2465  Int_t nfree = fFree->GetSize();
2466  memcpy(buffer, root, 4); buffer += 4;
2467  Int_t version = fVersion;
2468  if (version <1000000 && fEND > kStartBigFile) {version += 1000000; fUnits = 8;}
2469  tobuf(buffer, version);
2470  tobuf(buffer, (Int_t)fBEGIN);
2471  if (version < 1000000) {
2472  tobuf(buffer, (Int_t)fEND);
2473  tobuf(buffer, (Int_t)fSeekFree);
2474  tobuf(buffer, fNbytesFree);
2475  tobuf(buffer, nfree);
2476  tobuf(buffer, fNbytesName);
2477  tobuf(buffer, fUnits);
2478  tobuf(buffer, fCompress);
2479  tobuf(buffer, (Int_t)fSeekInfo);
2480  tobuf(buffer, fNbytesInfo);
2481  } else {
2482  tobuf(buffer, fEND);
2483  tobuf(buffer, fSeekFree);
2484  tobuf(buffer, fNbytesFree);
2485  tobuf(buffer, nfree);
2486  tobuf(buffer, fNbytesName);
2487  tobuf(buffer, fUnits);
2488  tobuf(buffer, fCompress);
2489  tobuf(buffer, fSeekInfo);
2490  tobuf(buffer, fNbytesInfo);
2491  }
2492  if (TestBit(kReproducible))
2493  TUUID("00000000-0000-0000-0000-000000000000").FillBuffer(buffer);
2494  else
2495  fUUID.FillBuffer(buffer);
2496  Int_t nbytes = buffer - psave;
2497  Seek(0); // NOLINT: silence clang-tidy warnings
2498  WriteBuffer(psave, nbytes); // NOLINT: silence clang-tidy warnings
2499  Flush(); // NOLINT: silence clang-tidy warnings, Intentionally not conditional on fMustFlush, this is the 'obligatory' flush.
2500  delete [] psave;
2501 }
2502 
2503 ////////////////////////////////////////////////////////////////////////////////
2504 /// Generate source code necessary to access the objects stored in the file.
2505 ///
2506 /// Generate code in directory dirname for all classes specified in
2507 /// argument classes If classes = "*" (default and currently the
2508 /// only supported value), the function generates an include file
2509 /// for each class in the StreamerInfo list for which a TClass
2510 /// object does not exist.
2511 ///
2512 /// The code generated includes:
2513 /// - <em>dirnameProjectHeaders.h</em>, which contains one #include statement per generated header file
2514 /// - <em>dirnameProjectSource.cxx</em>,which contains all the constructors and destructors implementation.
2515 /// and one header per class that is not nested inside another class.
2516 /// The header file name is the fully qualified name of the class after all the special characters
2517 /// "<>,:" are replaced by underscored. For example for std::pair<edm::Vertex,int> the file name is
2518 /// pair_edm__Vertex_int_.h
2519 ///
2520 /// In the generated classes, map, multimap when the first template parameter is a class
2521 /// are replaced by a vector of pair. set and multiset when the tempalte parameter
2522 /// is a class are replaced by a vector. This is required since we do not have the
2523 /// code needed to order and/or compare the object of the classes.
2524 /// This is a quick explanation of the options available:
2525 /// Option | Details
2526 /// -------|--------
2527 /// new (default) | A new directory dirname is created. If dirname already exist, an error message is printed and the function returns.
2528 /// recreate | If dirname does not exist, it is created (like in "new"). If dirname already exist, all existing files in dirname are deleted before creating the new files.
2529 /// update | New classes are added to the existing directory. Existing classes with the same name are replaced by the new definition. If the directory dirname doest not exist, same effect as "new".
2530 /// genreflex | Use genreflex rather than rootcint to generate the dictionary.
2531 /// par | Create a PAR file with the minimal set of code needed to read the content of the ROOT file. The name of the PAR file is basename(dirname), with extension '.par' enforced; the PAR file will be created at dirname(dirname).
2532 ///
2533 /// If, in addition to one of the 3 above options, the option "+" is specified,
2534 /// the function will generate:
2535 /// - a script called MAKEP to build the shared lib
2536 /// - a dirnameLinkDef.h file
2537 /// - rootcint will be run to generate a dirnameProjectDict.cxx file
2538 /// - dirnameProjectDict.cxx will be compiled with the current options in compiledata.h
2539 /// - a shared lib dirname.so will be created.
2540 /// If the option "++" is specified, the generated shared lib is dynamically
2541 /// linked with the current executable module.
2542 /// If the option "+" and "nocompile" are specified, the utility files are generated
2543 /// as in the option "+" but they are not executed.
2544 /// Example:
2545 /// file.MakeProject("demo","*","recreate++");
2546 /// - creates a new directory demo unless it already exist
2547 /// - clear the previous directory content
2548 /// - generate the xxx.h files for all classes xxx found in this file
2549 /// and not yet known to the CINT dictionary.
2550 /// - creates the build script MAKEP
2551 /// - creates a LinkDef.h file
2552 /// - runs rootcint generating demoProjectDict.cxx
2553 /// - compiles demoProjectDict.cxx into demoProjectDict.o
2554 /// - generates a shared lib demo.so
2555 /// - dynamically links the shared lib demo.so to the executable
2556 /// If only the option "+" had been specified, one can still link the
2557 /// shared lib to the current executable module with:
2558 ///
2559 /// gSystem->load("demo/demo.so");
2560 ///
2561 /// The following feature is not yet enabled:
2562 /// One can restrict the list of classes to be generated by using expressions like:
2563 ///
2564 /// classes = "Ali*" generate code only for classes starting with Ali
2565 /// classes = "myClass" generate code for class MyClass only.
2566 ///
2567 
2568 void TFile::MakeProject(const char *dirname, const char * /*classes*/,
2569  Option_t *option)
2570 {
2571  TString opt = option;
2572  opt.ToLower();
2573  Bool_t makepar = kFALSE;
2574  TString parname, pardir;
2575  if (opt.Contains("par")) {
2576  // Create a PAR file
2577  parname = gSystem->BaseName(dirname);
2578  if (parname.EndsWith(".par")) parname.ReplaceAll(".par","");
2579  pardir = gSystem->DirName(dirname);
2580  // Cleanup or prepare the dirs
2581  TString path, filepath;
2582  void *dir = gSystem->OpenDirectory(pardir);
2583  if (dir) {
2584  path.Form("%s/%s", pardir.Data(), parname.Data());
2585  void *dirp = gSystem->OpenDirectory(path);
2586  if (dirp) {
2587  path += "/PROOF-INF";
2588  void *dirinf = gSystem->OpenDirectory(path);
2589  const char *afile = 0;
2590  if (dirinf) {
2591  while ((afile = gSystem->GetDirEntry(dirinf))) {
2592  if (strcmp(afile,".") == 0) continue;
2593  if (strcmp(afile,"..") == 0) continue;
2594  filepath.Form("%s/%s", path.Data(), afile);
2595  if (gSystem->Unlink(filepath))
2596  Warning("MakeProject", "1: problems unlinking '%s' ('%s', '%s')", filepath.Data(), path.Data(), afile);
2597  }
2598  gSystem->FreeDirectory(dirinf);
2599  }
2600  gSystem->Unlink(path);
2601  path.Form("%s/%s", pardir.Data(), parname.Data());
2602  while ((afile = gSystem->GetDirEntry(dirp))) {
2603  if (strcmp(afile,".") == 0) continue;
2604  if (strcmp(afile,"..") == 0) continue;
2605  filepath.Form("%s/%s", path.Data(), afile);
2606  if (gSystem->Unlink(filepath))
2607  Warning("MakeProject", "2: problems unlinking '%s' ('%s', '%s')", filepath.Data(), path.Data(), afile);
2608  }
2609  gSystem->FreeDirectory(dirp);
2610  if (gSystem->Unlink(path))
2611  Warning("MakeProject", "problems unlinking '%s'", path.Data());
2612  }
2613  }
2614  // Make sure that the relevant dirs exists: this is mandatory, so we fail if unsuccessful
2615  path.Form("%s/%s/PROOF-INF", pardir.Data(), parname.Data());
2616  if (gSystem->mkdir(path, kTRUE)) {
2617  Error("MakeProject", "problems creating '%s'", path.Data());
2618  return;
2619  }
2620  makepar = kTRUE;
2621 
2622  } else {
2623  void *dir = gSystem->OpenDirectory(dirname);
2624  TString dirpath;
2625 
2626  if (opt.Contains("update")) {
2627  // check that directory exist, if not create it
2628  if (!dir) {
2629  gSystem->mkdir(dirname);
2630  }
2631 
2632  } else if (opt.Contains("recreate")) {
2633  // check that directory exist, if not create it
2634  if (!dir) {
2635  if (gSystem->mkdir(dirname) < 0) {
2636  Error("MakeProject","cannot create directory '%s'",dirname);
2637  return;
2638  }
2639  }
2640  // clear directory
2641  while (dir) {
2642  const char *afile = gSystem->GetDirEntry(dir);
2643  if (!afile) break;
2644  if (strcmp(afile,".") == 0) continue;
2645  if (strcmp(afile,"..") == 0) continue;
2646  dirpath.Form("%s/%s",dirname,afile);
2647  gSystem->Unlink(dirpath);
2648  }
2649 
2650  } else {
2651  // new is assumed
2652  // if directory already exist, print error message and return
2653  if (dir) {
2654  Error("MakeProject","cannot create directory %s, already existing",dirname);
2655  gSystem->FreeDirectory(dir);
2656  return;
2657  }
2658  if (gSystem->mkdir(dirname) < 0) {
2659  Error("MakeProject","cannot create directory '%s'",dirname);
2660  return;
2661  }
2662  }
2663  if (dir) {
2664  gSystem->FreeDirectory(dir);
2665  }
2666  }
2667  Bool_t genreflex = opt.Contains("genreflex");
2668 
2669  // we are now ready to generate the classes
2670  // loop on all TStreamerInfo
2671  TList *filelist = (TList*)GetStreamerInfoCache();
2672  if (filelist) filelist = (TList*)filelist->Clone();
2673  if (!filelist) {
2674  Error("MakeProject","file %s has no StreamerInfo", GetName());
2675  return;
2676  }
2677 
2678  TString clean_dirname(dirname);
2679  if (makepar) clean_dirname.Form("%s/%s", pardir.Data(), parname.Data());
2680  if (clean_dirname[clean_dirname.Length()-1]=='/') {
2681  clean_dirname.Remove(clean_dirname.Length()-1);
2682  } else if (clean_dirname[clean_dirname.Length()-1]=='\\') {
2683  clean_dirname.Remove(clean_dirname.Length()-1);
2684  if (clean_dirname[clean_dirname.Length()-1]=='\\') {
2685  clean_dirname.Remove(clean_dirname.Length()-1);
2686  }
2687  }
2688  TString subdirname( gSystem->BaseName(clean_dirname) );
2689  if (makepar) subdirname = parname;
2690  if (subdirname == "") {
2691  Error("MakeProject","Directory name must not be empty.");
2692  return;
2693  }
2694 
2695  // Start the source file
2696  TString spath; spath.Form("%s/%sProjectSource.cxx",clean_dirname.Data(),subdirname.Data());
2697  FILE *sfp = fopen(spath.Data(),"w");
2698  if (!sfp) {
2699  Error("MakeProject","Unable to create the source file %s.",spath.Data());
2700  return;
2701  }
2702  fprintf(sfp, "namespace std {}\nusing namespace std;\n");
2703  fprintf(sfp, "#include \"%sProjectHeaders.h\"\n\n",subdirname.Data() );
2704  if (!genreflex) fprintf(sfp, "#include \"%sLinkDef.h\"\n\n",subdirname.Data() );
2705  fprintf(sfp, "#include \"%sProjectDict.cxx\"\n\n",subdirname.Data() );
2706  fprintf(sfp, "struct DeleteObjectFunctor {\n");
2707  fprintf(sfp, " template <typename T>\n");
2708  fprintf(sfp, " void operator()(const T *ptr) const {\n");
2709  fprintf(sfp, " delete ptr;\n");
2710  fprintf(sfp, " }\n");
2711  fprintf(sfp, " template <typename T, typename Q>\n");
2712  fprintf(sfp, " void operator()(const std::pair<T,Q> &) const {\n");
2713  fprintf(sfp, " // Do nothing\n");
2714  fprintf(sfp, " }\n");
2715  fprintf(sfp, " template <typename T, typename Q>\n");
2716  fprintf(sfp, " void operator()(const std::pair<T,Q*> &ptr) const {\n");
2717  fprintf(sfp, " delete ptr.second;\n");
2718  fprintf(sfp, " }\n");
2719  fprintf(sfp, " template <typename T, typename Q>\n");
2720  fprintf(sfp, " void operator()(const std::pair<T*,Q> &ptr) const {\n");
2721  fprintf(sfp, " delete ptr.first;\n");
2722  fprintf(sfp, " }\n");
2723  fprintf(sfp, " template <typename T, typename Q>\n");
2724  fprintf(sfp, " void operator()(const std::pair<T*,Q*> &ptr) const {\n");
2725  fprintf(sfp, " delete ptr.first;\n");
2726  fprintf(sfp, " delete ptr.second;\n");
2727  fprintf(sfp, " }\n");
2728  fprintf(sfp, "};\n\n");
2729  fclose( sfp );
2730 
2731  // loop on all TStreamerInfo classes to check for empty classes
2732  // and enums listed either as data member or template parameters,
2733  // and filter out 'duplicates' classes/streamerInfos.
2734  TStreamerInfo *info;
2735  TIter flnext(filelist);
2736  TList extrainfos;
2737  TList *list = new TList();
2738  while ((info = (TStreamerInfo*)flnext())) {
2739  if (info->IsA() != TStreamerInfo::Class()) {
2740  continue;
2741  }
2742  if (strstr(info->GetName(),"@@")) {
2743  // Skip schema evolution support streamerInfo
2744  continue;
2745  }
2746  TClass *cl = TClass::GetClass(info->GetName());
2747  if (cl) {
2748  if (cl->HasInterpreterInfo()) continue; // skip known classes
2749  }
2750  // Find and use the proper rules for the TStreamerInfos.
2751  TMakeProject::GenerateMissingStreamerInfos( &extrainfos, info->GetName() );
2752  TIter enext( info->GetElements() );
2753  TStreamerElement *el;
2754  ROOT::Detail::TSchemaRuleSet::TMatches rules;
2755  if (cl && cl->GetSchemaRules()) {
2756  rules = cl->GetSchemaRules()->FindRules(cl->GetName(), info->GetClassVersion());
2757  }
2758  while( (el=(TStreamerElement*)enext()) ) {
2759  for(auto rule : rules) {
2760  if( rule->IsRenameRule() || rule->IsAliasRule() )
2761  continue;
2762  // Check whether this is an 'attribute' rule.
2763  if ( rule->HasTarget( el->GetName()) && rule->GetAttributes()[0] != 0 ) {
2764  TString attr( rule->GetAttributes() );
2765  attr.ToLower();
2766  if (attr.Contains("owner")) {
2767  if (attr.Contains("notowner")) {
2768  el->SetBit(TStreamerElement::kDoNotDelete);
2769  } else {
2770  el->ResetBit(TStreamerElement::kDoNotDelete);
2771  }
2772  }
2773  }
2774  }
2775  TMakeProject::GenerateMissingStreamerInfos(&extrainfos, el);
2776  }
2777  TVirtualStreamerInfo *alternate = (TVirtualStreamerInfo*)list->FindObject(info->GetName());
2778  if (alternate) {
2779  if ((info->GetClass() && info->GetClassVersion() == info->GetClass()->GetClassVersion())
2780  || (info->GetClassVersion() > alternate->GetClassVersion()) ) {
2781  list->AddAfter(alternate, info);
2782  list->Remove(alternate);
2783  } // otherwise ignore this info as not being the official one.
2784  } else {
2785  list->Add(info);
2786  }
2787  }
2788  // Now transfer the new StreamerInfo onto the main list and
2789  // to the owning list.
2790  TIter nextextra(&extrainfos);
2791  while ((info = (TStreamerInfo*)nextextra())) {
2792  list->Add(info);
2793  filelist->Add(info);
2794  }
2795 
2796  // loop on all TStreamerInfo classes
2797  TIter next(list);
2798  Int_t ngener = 0;
2799  while ((info = (TStreamerInfo*)next())) {
2800  if (info->IsA() != TStreamerInfo::Class()) {
2801  continue;
2802  }
2803  if (info->GetClassVersion()==-4) continue; // Skip outer level namespace
2804  TIter subnext(list);
2805  TStreamerInfo *subinfo;
2806  TList subClasses;
2807  Int_t len = strlen(info->GetName());
2808  while ((subinfo = (TStreamerInfo*)subnext())) {
2809  if (subinfo->IsA() != TStreamerInfo::Class()) {
2810  continue;
2811  }
2812  if (strncmp(info->GetName(),subinfo->GetName(),len)==0) {
2813  // The 'sub' StreamerInfo start with the main StreamerInfo name,
2814  // it subinfo is likely to be a nested class.
2815  const Int_t sublen = strlen(subinfo->GetName());
2816  if ( (sublen > len) && subinfo->GetName()[len+1]==':'
2817  && !subClasses.FindObject(subinfo->GetName()) /* We need to insure uniqueness */)
2818  {
2819  subClasses.Add(subinfo);
2820  }
2821  }
2822  }
2823  ngener += info->GenerateHeaderFile(clean_dirname.Data(),&subClasses,&extrainfos);
2824  subClasses.Clear("nodelete");
2825  }
2826  extrainfos.Clear("nodelete"); // We are done with this list.
2827 
2828  TString path;
2829  path.Form("%s/%sProjectHeaders.h",clean_dirname.Data(),subdirname.Data());
2830  FILE *allfp = fopen(path,"a");
2831  if (!allfp) {
2832  Error("MakeProject","Cannot open output file:%s\n",path.Data());
2833  } else {
2834  fprintf(allfp,"#include \"%sProjectInstances.h\"\n", subdirname.Data());
2835  fclose(allfp);
2836  }
2837 
2838  printf("MakeProject has generated %d classes in %s\n",ngener,clean_dirname.Data());
2839 
2840  // generate the shared lib
2841  if (!opt.Contains("+") && !makepar) {
2842  delete list;
2843  filelist->Delete();
2844  delete filelist;
2845  return;
2846  }
2847 
2848  // Makefiles files
2849  FILE *fpMAKE = nullptr;
2850  if (!makepar) {
2851  // Create the MAKEP file by looping on all *.h files
2852  // delete MAKEP if it already exists
2853 #ifdef WIN32
2854  path.Form("%s/makep.cmd",clean_dirname.Data());
2855 #else
2856  path.Form("%s/MAKEP",clean_dirname.Data());
2857 #endif
2858 #ifdef R__WINGCC
2859  fpMAKE = fopen(path,"wb");
2860 #else
2861  fpMAKE = fopen(path,"w");
2862 #endif
2863  if (!fpMAKE) {
2864  Error("MakeProject", "cannot open file %s", path.Data());
2865  delete list;
2866  filelist->Delete();
2867  delete filelist;
2868  return;
2869  }
2870  }
2871 
2872  // Add rootcint/genreflex statement generating ProjectDict.cxx
2873  FILE *ifp = nullptr;
2874  path.Form("%s/%sProjectInstances.h",clean_dirname.Data(),subdirname.Data());
2875 #ifdef R__WINGCC
2876  ifp = fopen(path,"wb");
2877 #else
2878  ifp = fopen(path,"w");
2879 #endif
2880  if (!ifp) {
2881  Error("MakeProject", "cannot open path file %s", path.Data());
2882  delete list;
2883  filelist->Delete();
2884  delete filelist;
2885  fclose(fpMAKE);
2886  return;
2887  }
2888 
2889  if (!makepar) {
2890  if (genreflex) {
2891  fprintf(fpMAKE,"genreflex %sProjectHeaders.h -o %sProjectDict.cxx --comments --iocomments %s ",subdirname.Data(),subdirname.Data(),gSystem->GetIncludePath());
2892  path.Form("%s/%sSelection.xml",clean_dirname.Data(),subdirname.Data());
2893  } else {
2894  fprintf(fpMAKE,"rootcint -v1 -f %sProjectDict.cxx %s ", subdirname.Data(), gSystem->GetIncludePath());
2895  path.Form("%s/%sLinkDef.h",clean_dirname.Data(),subdirname.Data());
2896  }
2897  } else {
2898  path.Form("%s/%sLinkDef.h",clean_dirname.Data(),subdirname.Data());
2899  }
2900 
2901  // Create the LinkDef.h or xml selection file by looping on all *.h files
2902  // replace any existing file.
2903 #ifdef R__WINGCC
2904  FILE *fp = fopen(path,"wb");
2905 #else
2906  FILE *fp = fopen(path,"w");
2907 #endif
2908  if (!fp) {
2909  Error("MakeProject", "cannot open path file %s", path.Data());
2910  delete list;
2911  filelist->Delete();
2912  delete filelist;
2913  fclose(fpMAKE);
2914  fclose(ifp);
2915  return;
2916  }
2917  if (genreflex) {
2918  fprintf(fp,"<lcgdict>\n");
2919  fprintf(fp,"\n");
2920  } else {
2921  fprintf(fp,"#ifdef __CINT__\n");
2922  fprintf(fp,"\n");
2923  }
2924 
2925  TString tmp;
2926  TString instances;
2927  TString selections;
2928  next.Reset();
2929  while ((info = (TStreamerInfo*)next())) {
2930  if (info->IsA() != TStreamerInfo::Class()) {
2931  continue;
2932  }
2933  if (strncmp(info->GetName(), "auto_ptr<", strlen("auto_ptr<")) == 0) {
2934  continue;
2935  }
2936  TClass *cl = TClass::GetClass(info->GetName());
2937  if (cl) {
2938  if (cl->HasInterpreterInfo()) continue; // skip known classes
2939  if (cl->GetSchemaRules()) {
2940  auto rules = cl->GetSchemaRules()->FindRules(cl->GetName(), info->GetClassVersion());
2941  TString strrule;
2942  for(auto rule : rules) {
2943  strrule.Clear();
2944  if (genreflex) {
2945  rule->AsString(strrule,"x");
2946  strrule.Append("\n");
2947  if ( selections.Index(strrule) == kNPOS ) {
2948  selections.Append(strrule);
2949  }
2950  } else {
2951  rule->AsString(strrule);
2952  if (strncmp(strrule.Data(),"type=",5)==0) {
2953  strrule.Remove(0,5);
2954  }
2955  fprintf(fp,"#pragma %s;\n",strrule.Data());
2956  }
2957  }
2958  }
2959 
2960  }
2961  if ((info->GetClass() && info->GetClass()->GetCollectionType()) || TClassEdit::IsSTLCont(info->GetName())) {
2962  std::vector<std::string> inside;
2963  int nestedLoc;
2964  TClassEdit::GetSplit( info->GetName(), inside, nestedLoc, TClassEdit::kLong64 );
2965  Int_t stlkind = TClassEdit::STLKind(inside[0]);
2966  TClass *key = TClass::GetClass(inside[1].c_str());
2967  if (key) {
2968  TString what;
2969  switch ( stlkind ) {
2970  case ROOT::kSTLmap:
2971  case ROOT::kSTLmultimap:
2972  if (TClass::GetClass(inside[1].c_str())) {
2973  what = "std::pair<";
2974  what += TMakeProject::UpdateAssociativeToVector( inside[1].c_str() );
2975  what += ",";
2976  what += TMakeProject::UpdateAssociativeToVector( inside[2].c_str() );
2977  if (what[what.Length()-1]=='>') {
2978  what += " >";
2979  } else {
2980  what += ">";
2981  }
2982  if (genreflex) {
2983  tmp.Form("<class name=\"%s\" />\n",what.Data());
2984  if ( selections.Index(tmp) == kNPOS ) {
2985  selections.Append(tmp);
2986  }
2987  tmp.Form("template class %s;\n",what.Data());
2988  if ( instances.Index(tmp) == kNPOS ) {
2989  instances.Append(tmp);
2990  }
2991  } else {
2992  what.ReplaceAll("std::","");
2993  TClass *paircl = TClass::GetClass(what.Data());
2994  if (!paircl || !paircl->HasInterpreterInfo()) {
2995  fprintf(fp,"#pragma link C++ class %s+;\n",what.Data());
2996  }
2997  }
2998  break;
2999  }
3000  default:
3001  if (strncmp(key->GetName(),"pair<",strlen("pair<"))==0) {
3002  if (genreflex) {
3003  tmp.Form("<class name=\"%s\" />\n",key->GetName());
3004  if ( selections.Index(tmp) == kNPOS ) {
3005  selections.Append(tmp);
3006  }
3007  tmp.Form("template class %s;\n",key->GetName());
3008  if ( instances.Index(tmp) == kNPOS ) {
3009  instances.Append(tmp);
3010  }
3011  } else {
3012  what.ReplaceAll("std::","");
3013  fprintf(fp,"#pragma link C++ class %s+;\n",key->GetName());
3014  }
3015  }
3016  break;
3017  }
3018  }
3019  continue;
3020  }
3021  {
3022  TString what(TMakeProject::UpdateAssociativeToVector(info->GetName()).Data());
3023  if (genreflex) {
3024  tmp.Form("<class name=\"%s\" />\n",what.Data());
3025  if ( selections.Index(tmp) == kNPOS ) {
3026  selections.Append(tmp);
3027  }
3028  if (what[what.Length()-1] == '>') {
3029  tmp.Form("template class %s;\n",what.Data());
3030  if ( instances.Index(tmp) == kNPOS ) {
3031  instances.Append(tmp);
3032  }
3033  }
3034  } else {
3035  what.ReplaceAll("std::","");
3036  fprintf(fp,"#pragma link C++ class %s+;\n",what.Data());
3037  }
3038  }
3039  if (genreflex) {
3040  // Also request the dictionary for the STL container used as members ...
3041  TIter eliter( info->GetElements() );
3042  TStreamerElement *element;
3043  while( (element = (TStreamerElement*)eliter() ) ) {
3044  if (element->GetClass() && !element->GetClass()->IsLoaded() && element->GetClass()->GetCollectionProxy()) {
3045  TString what( TMakeProject::UpdateAssociativeToVector(element->GetClass()->GetName()) );
3046  tmp.Form("<class name=\"%s\" />\n",what.Data());
3047  if ( selections.Index(tmp) == kNPOS ) {
3048  selections.Append(tmp);
3049  }
3050  tmp.Form("template class %s;\n",what.Data());
3051  if ( instances.Index(tmp) == kNPOS ) {
3052  instances.Append(tmp);
3053  }
3054  }
3055  }
3056  }
3057  }
3058  if (genreflex) {
3059  fprintf(ifp,"#ifndef PROJECT_INSTANCES_H\n");
3060  fprintf(ifp,"#define PROJECT_INSTANCES_H\n");
3061  fprintf(ifp,"%s",instances.Data());
3062  fprintf(ifp,"#endif\n");
3063  fprintf(fp,"%s",selections.Data());
3064  fprintf(fp,"</lcgdict>\n");
3065  } else {
3066  fprintf(fp,"#endif\n");
3067  }
3068  fclose(fp);
3069  fclose(ifp);
3070 
3071  if (!makepar) {
3072  // add compilation line
3073  TString sdirname(subdirname);
3074 
3075  TString cmd = gSystem->GetMakeSharedLib();
3076  TString sources = TString::Format("%sProjectSource.cxx ", sdirname.Data());
3077  cmd.ReplaceAll("$SourceFiles",sources.Data());
3078  TString object = TString::Format("%sProjectSource.", sdirname.Data());
3079  object.Append( gSystem->GetObjExt() );
3080  cmd.ReplaceAll("$ObjectFiles", object.Data());
3081  cmd.ReplaceAll("$IncludePath",TString(gSystem->GetIncludePath()) + " -I" + clean_dirname.Data());
3082  cmd.ReplaceAll("$SharedLib",sdirname+"."+gSystem->GetSoExt());
3083  cmd.ReplaceAll("$LinkedLibs",gSystem->GetLibraries("","SDL"));
3084  cmd.ReplaceAll("$LibName",sdirname);
3085  cmd.ReplaceAll("$BuildDir",".");
3086  TString sOpt;
3087  TString rootbuild = ROOTBUILD;
3088  if (rootbuild.Index("debug",0,TString::kIgnoreCase)==kNPOS) {
3089  sOpt = gSystem->GetFlagsOpt();
3090  } else {
3091  sOpt = gSystem->GetFlagsDebug();
3092  }
3093  cmd.ReplaceAll("$Opt", sOpt);
3094 
3095  if (genreflex) {
3096  fprintf(fpMAKE,"-s %sSelection.xml \n",subdirname.Data());
3097  } else {
3098  fprintf(fpMAKE,"%sProjectHeaders.h ",subdirname.Data());
3099  fprintf(fpMAKE,"%sLinkDef.h \n",subdirname.Data());
3100  }
3101 
3102  fprintf(fpMAKE,"%s\n",cmd.Data());
3103 
3104  printf("%s/MAKEP file has been generated\n", clean_dirname.Data());
3105 
3106  fclose(fpMAKE);
3107 
3108  } else {
3109 
3110  // Create the Makefile
3111  TString filemake = TString::Format("%s/Makefile", clean_dirname.Data());
3112  if (MakeProjectParMake(parname, filemake.Data()) != 0) {
3113  Error("MakeProject", "problems creating PAR make file '%s'", filemake.Data());
3114  delete list;
3115  filelist->Delete();
3116  delete filelist;
3117  return;
3118  }
3119  // Get Makefile.arch
3120  TString mkarchsrc = TString::Format("%s/Makefile.arch", TROOT::GetEtcDir().Data());
3121  if (gSystem->ExpandPathName(mkarchsrc))
3122  Warning("MakeProject", "problems expanding '%s'", mkarchsrc.Data());
3123  TString mkarchdst = TString::Format("%s/Makefile.arch", clean_dirname.Data());
3124  if (gSystem->CopyFile(mkarchsrc.Data(), mkarchdst.Data(), kTRUE) != 0) {
3125  Error("MakeProject", "problems retrieving '%s' to '%s'", mkarchsrc.Data(), mkarchdst.Data());
3126  delete list;
3127  filelist->Delete();
3128  delete filelist;
3129  return;
3130  }
3131  // Create the Makefile
3132  TString proofinf = TString::Format("%s/PROOF-INF", clean_dirname.Data());
3133  if (MakeProjectParProofInf(parname, proofinf.Data()) != 0) {
3134  Error("MakeProject", "problems creating BUILD.sh and/or SETUP.C under '%s'", proofinf.Data());
3135  delete list;
3136  filelist->Delete();
3137  delete filelist;
3138  return;
3139  }
3140 
3141  // Make sure BUILD.sh is executable and create SETUP.C
3142  TString cmod = TString::Format("chmod +x %s/PROOF-INF/BUILD.sh", clean_dirname.Data());
3143 #ifndef WIN32
3144  gSystem->Exec(cmod.Data());
3145 #else
3146  // not really needed for Windows but it would work both both Unix and NT
3147  chmod(cmod.Data(), 00700);
3148 #endif
3149  Printf("Files Makefile, Makefile.arch, PROOF-INF/BUILD.sh and"
3150  " PROOF-INF/SETUP.C have been generated under '%s'", clean_dirname.Data());
3151 
3152  // Generate the PAR file, if not Windows
3153 #ifndef WIN32
3154  TString curdir = gSystem->WorkingDirectory();
3155  if (gSystem->ChangeDirectory(pardir)) {
3156  TString cmd = TString::Format("tar czvf %s.par %s", parname.Data(), parname.Data());
3157  gSystem->Exec(cmd.Data());
3158  if (gSystem->ChangeDirectory(curdir)) {
3159  Info("MakeProject", "PAR file %s.par generated", clean_dirname.Data());
3160  } else {
3161  Warning("MakeProject", "problems changing directory back to '%s'", curdir.Data());
3162  }
3163  } else {
3164  Error("MakeProject", "problems changing directory to '%s' - skipping PAR file generation", pardir.Data());
3165  }
3166 #else
3167  Warning("MakeProject", "on Windows systems the PAR file cannot be generated out of the package directory!");
3168 #endif
3169  }
3170 
3171 
3172  if (!makepar && !opt.Contains("nocompilation")) {
3173  // now execute the generated script compiling and generating the shared lib
3174  path = gSystem->WorkingDirectory();
3175  gSystem->ChangeDirectory(clean_dirname.Data());
3176 #ifndef WIN32
3177  gSystem->Exec("chmod +x MAKEP");
3178  int res = !gSystem->Exec("./MAKEP");
3179 #else
3180  // not really needed for Windows but it would work both both Unix and NT
3181  chmod("makep.cmd",00700);
3182  int res = !gSystem->Exec("MAKEP");
3183 #endif
3184  gSystem->ChangeDirectory(path);
3185  path.Form("%s/%s.%s",clean_dirname.Data(),subdirname.Data(),gSystem->GetSoExt());
3186  if (res) printf("Shared lib %s has been generated\n",path.Data());
3187 
3188  //dynamically link the generated shared lib
3189  if (opt.Contains("++")) {
3190  res = !gSystem->Load(path);
3191  if (res) printf("Shared lib %s has been dynamically linked\n",path.Data());
3192  }
3193  }
3194 
3195  delete list;
3196  filelist->Delete();
3197  delete filelist;
3198 }
3199 
3200 ////////////////////////////////////////////////////////////////////////////////
3201 /// Create makefile at 'filemake' for PAR package 'pack'.
3202 ///
3203 /// Called by MakeProject when option 'par' is given.
3204 /// Return 0 on success, -1 on error.
3205 
3206 Int_t TFile::MakeProjectParMake(const char *pack, const char *filemake)
3207 {
3208  // Output file path must be defined
3209  if (!filemake || (filemake && strlen(filemake) <= 0)) {
3210  Error("MakeProjectParMake", "path for output file undefined!");
3211  return -1;
3212  }
3213 
3214  // Package name must be defined
3215  if (!pack || (pack && strlen(pack) <= 0)) {
3216  Error("MakeProjectParMake", "package name undefined!");
3217  return -1;
3218  }
3219 
3220 #ifdef R__WINGCC
3221  FILE *fmk = fopen(filemake, "wb");
3222 #else
3223  FILE *fmk = fopen(filemake, "w");
3224 #endif
3225  if (!fmk) {
3226  Error("MakeProjectParMake", "cannot create file '%s' (errno: %d)", filemake, TSystem::GetErrno());
3227  return -1;
3228  }
3229 
3230  // Fill the file now
3231  fprintf(fmk, "# Makefile for the ROOT test programs.\n");
3232  fprintf(fmk, "# This Makefile shows how to compile and link applications\n");
3233  fprintf(fmk, "# using the ROOT libraries on all supported platforms.\n");
3234  fprintf(fmk, "#\n");
3235  fprintf(fmk, "# Copyright (c) 2000 Rene Brun and Fons Rademakers\n");
3236  fprintf(fmk, "#\n");
3237  fprintf(fmk, "# Author: this makefile has been automatically generated via TFile::MakeProject\n");
3238  fprintf(fmk, "\n");
3239  fprintf(fmk, "include Makefile.arch\n");
3240  fprintf(fmk, "\n");
3241  fprintf(fmk, "#------------------------------------------------------------------------------\n");
3242  fprintf(fmk, "\n");
3243  fprintf(fmk, "PACKO = %sProjectSource.$(ObjSuf)\n", pack);
3244  fprintf(fmk, "PACKS = %sProjectSource.$(SrcSuf) %sProjectDict.$(SrcSuf)\n", pack, pack);
3245  fprintf(fmk, "PACKSO = lib%s.$(DllSuf)\n", pack);
3246  fprintf(fmk, "\n");
3247  fprintf(fmk, "ifeq ($(PLATFORM),win32)\n");
3248  fprintf(fmk, "PACKLIB = lib%s.lib\n", pack);
3249  fprintf(fmk, "else\n");
3250  fprintf(fmk, "PACKLIB = $(PACKSO)\n");
3251  fprintf(fmk, "endif\n");
3252  fprintf(fmk, "\n");
3253  fprintf(fmk, "OBJS = $(PACKO)\n");
3254  fprintf(fmk, "\n");
3255  fprintf(fmk, "PROGRAMS =\n");
3256  fprintf(fmk, "\n");
3257  fprintf(fmk, "#------------------------------------------------------------------------------\n");
3258  fprintf(fmk, "\n");
3259  fprintf(fmk, ".SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(DllSuf)\n");
3260  fprintf(fmk, "\n");
3261  fprintf(fmk, "all: $(PACKLIB)\n");
3262  fprintf(fmk, "\n");
3263  fprintf(fmk, "$(PACKSO): $(PACKO)\n");
3264  fprintf(fmk, "ifeq ($(ARCH),aix)\n");
3265  fprintf(fmk, "\t\t/usr/ibmcxx/bin/makeC++SharedLib $(OutPutOpt) $@ $(LIBS) -p 0 $^\n");
3266  fprintf(fmk, "else\n");
3267  fprintf(fmk, "ifeq ($(ARCH),aix5)\n");
3268  fprintf(fmk, "\t\t/usr/vacpp/bin/makeC++SharedLib $(OutPutOpt) $@ $(LIBS) -p 0 $^\n");
3269  fprintf(fmk, "else\n");
3270  fprintf(fmk, "ifeq ($(PLATFORM),macosx)\n");
3271  fprintf(fmk, "# We need to make both the .dylib and the .so\n");
3272  fprintf(fmk, "\t\t$(LD) $(SOFLAGS)$@ $(LDFLAGS) $^ $(OutPutOpt) $@ $(LIBS)\n");
3273  fprintf(fmk, "ifneq ($(subst $(MACOSX_MINOR),,1234),1234)\n");
3274  fprintf(fmk, "ifeq ($(MACOSX_MINOR),4)\n");
3275  fprintf(fmk, "\t\tln -sf $@ $(subst .$(DllSuf),.so,$@)\n");
3276  fprintf(fmk, "else\n");
3277  fprintf(fmk, "\t\t$(LD) -bundle -undefined $(UNDEFOPT) $(LDFLAGS) $^ \\\n");
3278  fprintf(fmk, "\t\t $(OutPutOpt) $(subst .$(DllSuf),.so,$@)\n");
3279  fprintf(fmk, "endif\n");
3280  fprintf(fmk, "endif\n");
3281  fprintf(fmk, "else\n");
3282  fprintf(fmk, "ifeq ($(PLATFORM),win32)\n");
3283  fprintf(fmk, "\t\tbindexplib $* $^ > $*.def\n");
3284  fprintf(fmk, "\t\tlib -nologo -MACHINE:IX86 $^ -def:$*.def \\\n");
3285  fprintf(fmk, "\t\t $(OutPutOpt)$(PACKLIB)\n");
3286  fprintf(fmk, "\t\t$(LD) $(SOFLAGS) $(LDFLAGS) $^ $*.exp $(LIBS) \\\n");
3287  fprintf(fmk, "\t\t $(OutPutOpt)$@\n");
3288  fprintf(fmk, "else\n");
3289  fprintf(fmk, "\t\t$(LD) $(SOFLAGS) $(LDFLAGS) $^ $(OutPutOpt) $@ $(LIBS) $(EXPLLINKLIBS)\n");
3290  fprintf(fmk, "endif\n");
3291  fprintf(fmk, "endif\n");
3292  fprintf(fmk, "endif\n");
3293  fprintf(fmk, "endif\n");
3294  fprintf(fmk, "\t\t@echo \"$@ done\"\n");
3295  fprintf(fmk, "\n");
3296  fprintf(fmk, "clean:\n");
3297  fprintf(fmk, "\t\t@rm -f $(OBJS) core\n");
3298  fprintf(fmk, "\n");
3299  fprintf(fmk, "distclean: clean\n");
3300  fprintf(fmk, "\t\t@rm -f $(PROGRAMS) $(PACKSO) $(PACKLIB) *Dict.* *.def *.exp \\\n");
3301  fprintf(fmk, "\t\t *.so *.lib *.dll *.d *.log .def so_locations\n");
3302  fprintf(fmk, "\t\t@rm -rf cxx_repository\n");
3303  fprintf(fmk, "\n");
3304  fprintf(fmk, "# Dependencies\n");
3305  fprintf(fmk, "\n");
3306  fprintf(fmk, "%sProjectSource.$(ObjSuf): %sProjectHeaders.h %sLinkDef.h %sProjectDict.$(SrcSuf)\n", pack, pack, pack, pack);
3307  fprintf(fmk, "\n");
3308  fprintf(fmk, "%sProjectDict.$(SrcSuf): %sProjectHeaders.h %sLinkDef.h\n", pack, pack, pack);
3309  fprintf(fmk, "\t\t@echo \"Generating dictionary $@...\"\n");
3310  fprintf(fmk, "\t\t@rootcint -f $@ $^\n");
3311  fprintf(fmk, "\n");
3312  fprintf(fmk, ".$(SrcSuf).$(ObjSuf):\n");
3313  fprintf(fmk, "\t\t$(CXX) $(CXXFLAGS) -c $<\n");
3314  fprintf(fmk, "\n");
3315 
3316  // Close the file
3317  fclose(fmk);
3318 
3319  // Done
3320  return 0;
3321 }
3322 
3323 ////////////////////////////////////////////////////////////////////////////////
3324 /// Create BUILD.sh and SETUP.C under 'proofinf' for PAR package 'pack'.
3325 /// Called by MakeProject when option 'par' is given.
3326 /// Return 0 on success, -1 on error.
3327 
3328 Int_t TFile::MakeProjectParProofInf(const char *pack, const char *proofinf)
3329 {
3330  // Output directory path must be defined ...
3331  if (!proofinf || (proofinf && strlen(proofinf) <= 0)) {
3332  Error("MakeProjectParProofInf", "directory path undefined!");
3333  return -1;
3334  }
3335 
3336  // ... and exist and be a directory
3337  Int_t rcst = 0;
3338  FileStat_t st;
3339  if ((rcst = gSystem->GetPathInfo(proofinf, st)) != 0 || !R_ISDIR(st.fMode)) {
3340  Error("MakeProjectParProofInf", "path '%s' %s", proofinf,
3341  ((rcst == 0) ? "is not a directory" : "does not exist"));
3342  return -1;
3343  }
3344 
3345  // Package name must be defined
3346  if (!pack || (pack && strlen(pack) <= 0)) {
3347  Error("MakeProjectParProofInf", "package name undefined!");
3348  return -1;
3349  }
3350 
3351  TString path;
3352 
3353  // The BUILD.sh first
3354  path.Form("%s/BUILD.sh", proofinf);
3355 #ifdef R__WINGCC
3356  FILE *f = fopen(path.Data(), "wb");
3357 #else
3358  FILE *f = fopen(path.Data(), "w");
3359 #endif
3360  if (!f) {
3361  Error("MakeProjectParProofInf", "cannot create file '%s' (errno: %d)",
3362  path.Data(), TSystem::GetErrno());
3363  return -1;
3364  }
3365 
3366  fprintf(f, "#! /bin/sh\n");
3367  fprintf(f, "# Build libEvent library.\n");
3368  fprintf(f, "\n");
3369  fprintf(f, "#\n");
3370  fprintf(f, "# The environment variables ROOTPROOFLITE and ROOTPROOFCLIENT can be used to\n");
3371  fprintf(f, "# adapt the script to the calling environment\n");
3372  fprintf(f, "#\n");
3373  fprintf(f, "# if test ! \"x$ROOTPROOFLITE\" = \"x\"; then\n");
3374  fprintf(f, "# echo \"event-BUILD: PROOF-Lite node (session has $ROOTPROOFLITE workers)\"\n");
3375  fprintf(f, "# elif test ! \"x$ROOTPROOFCLIENT\" = \"x\"; then\n");
3376  fprintf(f, "# echo \"event-BUILD: PROOF client\"\n");
3377  fprintf(f, "# else\n");
3378  fprintf(f, "# echo \"event-BUILD: standard PROOF node\"\n");
3379  fprintf(f, "# fi\n");
3380  fprintf(f, "\n");
3381  fprintf(f, "if [ \"\" = \"clean\" ]; then\n");
3382  fprintf(f, " make distclean\n");
3383  fprintf(f, " exit 0\n");
3384  fprintf(f, "fi\n");
3385  fprintf(f, "\n");
3386  fprintf(f, "make\n");
3387  fprintf(f, "rc=$?\n");
3388  fprintf(f, "echo \"rc=$?\"\n");
3389  fprintf(f, "if [ $? != \"0\" ] ; then\n");
3390  fprintf(f, " exit 1\n");
3391  fprintf(f, "fi\n");
3392  fprintf(f, "exit 0\n");
3393 
3394  // Close the file
3395  fclose(f);
3396 
3397  // Then SETUP.C
3398  path.Form("%s/SETUP.C", proofinf);
3399 #ifdef R__WINGCC
3400  f = fopen(path.Data(), "wb");
3401 #else
3402  f = fopen(path.Data(), "w");
3403 #endif
3404  if (!f) {
3405  Error("MakeProjectParProofInf", "cannot create file '%s' (errno: %d)",
3406  path.Data(), TSystem::GetErrno());
3407  return -1;
3408  }
3409 
3410  fprintf(f, "Int_t SETUP()\n");
3411  fprintf(f, "{\n");
3412  fprintf(f, "\n");
3413  fprintf(f, "//\n");
3414  fprintf(f, "// The environment variables ROOTPROOFLITE and ROOTPROOFCLIENT can be used to\n");
3415  fprintf(f, "// adapt the macro to the calling environment\n");
3416  fprintf(f, "//\n");
3417  fprintf(f, "// if (gSystem->Getenv(\"ROOTPROOFLITE\")) {\n");
3418  fprintf(f, "// Printf(\"event-SETUP: PROOF-Lite node (session has %%s workers)\",\n");
3419  fprintf(f, "// gSystem->Getenv(\"ROOTPROOFLITE\"));\n");
3420  fprintf(f, "// } else if (gSystem->Getenv(\"ROOTPROOFCLIENT\")) {\n");
3421  fprintf(f, "// Printf(\"event-SETUP: PROOF client\");\n");
3422  fprintf(f, "// } else {\n");
3423  fprintf(f, "// Printf(\"event-SETUP: standard PROOF node\");\n");
3424  fprintf(f, "// }\n");
3425  fprintf(f, "\n");
3426  fprintf(f, " if (gSystem->Load(\"lib%s\") == -1)\n", pack);
3427  fprintf(f, " return -1;\n");
3428  fprintf(f, " return 0;\n");
3429  fprintf(f, "}\n");
3430  fprintf(f, "\n");
3431 
3432  // Close the file
3433  fclose(f);
3434 
3435  // Done
3436  return 0;
3437 }
3438 
3439 ////////////////////////////////////////////////////////////////////////////////
3440 /// Read the list of StreamerInfo from this file.
3441 ///
3442 /// The key with name holding the list of TStreamerInfo objects is read.
3443 /// The corresponding TClass objects are updated.
3444 /// Note that this function is not called if the static member fgReadInfo is false.
3445 /// (see TFile::SetReadStreamerInfo)
3446 
3447 void TFile::ReadStreamerInfo()
3448 {
3449  auto listRetcode = GetStreamerInfoListImpl(/*lookupSICache*/ true); // NOLINT: silence clang-tidy warnings
3450  TList *list = listRetcode.fList;
3451  auto retcode = listRetcode.fReturnCode;
3452  if (!list) {
3453  if (retcode) MakeZombie();
3454  return;
3455  }
3456 
3457  list->SetOwner(kFALSE);
3458 
3459  if (gDebug > 0) Info("ReadStreamerInfo", "called for file %s",GetName());
3460 
3461  TStreamerInfo *info;
3462 
3463  Int_t version = fVersion;
3464  if (version > 1000000) version -= 1000000;
3465  if (version < 53419 || (59900 < version && version < 59907)) {
3466  // We need to update the fCheckSum field of the TStreamerBase.
3467 
3468  // loop on all TStreamerInfo classes
3469  TObjLink *lnk = list->FirstLink();
3470  while (lnk) {
3471  info = (TStreamerInfo*)lnk->GetObject();
3472  if (!info || info->IsA() != TStreamerInfo::Class()) {
3473  lnk = lnk->Next();
3474  continue;
3475  }
3476  TIter next(info->GetElements());
3477  TStreamerElement *element;
3478  while ((element = (TStreamerElement*) next())) {
3479  TStreamerBase *base = dynamic_cast<TStreamerBase*>(element);
3480  if (!base) continue;
3481  if (base->GetBaseCheckSum() != 0) continue;
3482  TStreamerInfo *baseinfo = (TStreamerInfo*)list->FindObject(base->GetName());
3483  if (baseinfo) {
3484  base->SetBaseCheckSum(baseinfo->GetCheckSum());
3485  }
3486  }
3487  lnk = lnk->Next();
3488  }
3489  }
3490 
3491  // loop on all TStreamerInfo classes
3492  for (int mode=0;mode<2; ++mode) {
3493  // In order for the collection proxy to be initialized properly, we need
3494  // to setup the TStreamerInfo for non-stl class before the stl classes.
3495  TObjLink *lnk = list->FirstLink();
3496  while (lnk) {
3497  info = (TStreamerInfo*)lnk->GetObject();
3498  if (!info) {
3499  lnk = lnk->Next();
3500  continue;
3501  }
3502  if (info->IsA() != TStreamerInfo::Class()) {
3503  if (mode==1) {
3504  TObject *obj = (TObject*)info;
3505  if (strcmp(obj->GetName(),"listOfRules")==0) {
3506 #if 0
3507  // Completely ignore the rules for now.
3508  TList *listOfRules = (TList*)obj;
3509  TObjLink *rulelnk = listOfRules->FirstLink();
3510  while (rulelnk) {
3511  TObjString *rule = (TObjString*)rulelnk->GetObject();
3512  TClass::AddRule( rule->String().Data() );
3513  rulelnk = rulelnk->Next();
3514  }
3515 #endif
3516  } else {
3517  Warning("ReadStreamerInfo","%s has a %s in the list of TStreamerInfo.", GetName(), info->IsA()->GetName());
3518  }
3519  info->SetBit(kCanDelete);
3520  }
3521  lnk = lnk->Next();
3522  continue;
3523  }
3524  // This is a quick way (instead of parsing the name) to see if this is
3525  // the description of an STL container.
3526  if (info->GetElements()==0) {
3527  Warning("ReadStreamerInfo","The StreamerInfo for %s does not have a list of elements.",info->GetName());
3528  lnk = lnk->Next();
3529  continue;
3530  }
3531  TObject *element = info->GetElements()->UncheckedAt(0);
3532  Bool_t isstl = element && strcmp("This",element->GetName())==0;
3533 
3534  if ( (!isstl && mode ==0) || (isstl && mode ==1) ) {
3535  // Skip the STL container the first time around
3536  // Skip the regular classes the second time around;
3537  info->BuildCheck(this);
3538  Int_t uid = info->GetNumber();
3539  Int_t asize = fClassIndex->GetSize();
3540  if (uid >= asize && uid <100000) fClassIndex->Set(2*asize);
3541  if (uid >= 0 && uid < fClassIndex->GetSize()) fClassIndex->fArray[uid] = 1;
3542  else if (!isstl) {
3543  printf("ReadStreamerInfo, class:%s, illegal uid=%d\n",info->GetName(),uid);
3544  }
3545  if (gDebug > 0) printf(" -class: %s version: %d info read at slot %d\n",info->GetName(), info->GetClassVersion(),uid);
3546  }
3547  lnk = lnk->Next();
3548  }
3549  }
3550  fClassIndex->fArray[0] = 0;
3551  list->Clear(); //this will delete all TStreamerInfo objects with kCanDelete bit set
3552  delete list;
3553 
3554 #ifdef R__USE_IMT
3555  // We are done processing the record, let future calls and other threads that it
3556  // has been done.
3557  fgTsSIHashes.Insert(listRetcode.fHash);
3558 #endif
3559 }
3560 
3561 ////////////////////////////////////////////////////////////////////////////////
3562 /// Specify if the streamerinfos must be read at file opening.
3563 ///
3564 /// If fgReadInfo is true (default) TFile::ReadStreamerInfo is called
3565 /// when opening the file.
3566 /// It may be interesting to set fgReadInfo to false to speedup the file
3567 /// opening time or in case libraries containing classes referenced
3568 /// by the file have not yet been loaded.
3569 /// if fgReadInfo is false, one can still read the StreamerInfo with
3570 /// myfile.ReadStreamerInfo();
3571 
3572 void TFile::SetReadStreamerInfo(Bool_t readinfo)
3573 {
3574  fgReadInfo = readinfo;
3575 }
3576 
3577 ////////////////////////////////////////////////////////////////////////////////
3578 /// If the streamerinfos are to be read at file opening.
3579 ///
3580 /// See TFile::SetReadStreamerInfo for more documentation.
3581 
3582 Bool_t TFile::GetReadStreamerInfo()
3583 {
3584  return fgReadInfo;
3585 }
3586 
3587 ////////////////////////////////////////////////////////////////////////////////
3588 /// Show the StreamerInfo of all classes written to this file.
3589 
3590 void TFile::ShowStreamerInfo()
3591 {
3592  TList *list = GetStreamerInfoList();
3593  if (!list) return;
3594 
3595  list->ls();
3596  delete list;
3597 }
3598 
3599 ////////////////////////////////////////////////////////////////////////////////
3600 /// Check if the ProcessID pidd is already in the file,
3601 /// if not, add it and return the index number in the local file list.
3602 
3603 UShort_t TFile::WriteProcessID(TProcessID *pidd)
3604 {
3605  TProcessID *pid = pidd;
3606  if (!pid) pid = TProcessID::GetPID();
3607  TObjArray *pids = GetListOfProcessIDs();
3608  Int_t npids = GetNProcessIDs();
3609  for (Int_t i=0;i<npids;i++) {
3610  if (pids->At(i) == pid) return (UShort_t)i;
3611  }
3612 
3613  this->SetBit(TFile::kHasReferences);
3614  pids->AddAtAndExpand(pid,npids);
3615  pid->IncrementCount();
3616  char name[32];
3617  snprintf(name,32,"ProcessID%d",npids);
3618  this->WriteTObject(pid,name);
3619  this->IncrementProcessIDs();
3620  if (gDebug > 0) {
3621  Info("WriteProcessID", "name=%s, file=%s", name, GetName());
3622  }
3623  return (UShort_t)npids;
3624 }
3625 
3626 
3627 ////////////////////////////////////////////////////////////////////////////////
3628 /// Write the list of TStreamerInfo as a single object in this file
3629 /// The class Streamer description for all classes written to this file
3630 /// is saved. See class TStreamerInfo.
3631 
3632 void TFile::WriteStreamerInfo()
3633 {
3634  //if (!gFile) return;
3635  if (!fWritable) return;
3636  if (!fClassIndex) return;
3637  if (fIsPcmFile) return; // No schema evolution for ROOT PCM files.
3638  if (fClassIndex->fArray[0] == 0
3639  && fSeekInfo != 0) {
3640  // No need to update the index if no new classes added to the file
3641  // but write once an empty StreamerInfo list to mark that there is no need
3642  // for StreamerInfos in this file.
3643  return;
3644  }
3645  if (gDebug > 0) Info("WriteStreamerInfo", "called for file %s",GetName());
3646 
3647  SafeDelete(fInfoCache);
3648 
3649  // build a temporary list with the marked files
3650  TIter next(gROOT->GetListOfStreamerInfo());
3651  TStreamerInfo *info;
3652  TList list;
3653  TList listOfRules;
3654  listOfRules.SetOwner(kTRUE);
3655  listOfRules.SetName("listOfRules");
3656  std::set<TClass*> classSet;
3657 
3658 
3659  while ((info = (TStreamerInfo*)next())) {
3660  Int_t uid = info->GetNumber();
3661  if (fClassIndex->fArray[uid]) {
3662  list.Add(info);
3663  if (gDebug > 0) printf(" -class: %s info number %d saved\n",info->GetName(),uid);
3664 
3665  // Add the IO customization rules to the list to be saved for the underlying
3666  // class but make sure to add them only once.
3667  TClass *clinfo = info->GetClass();
3668  if (clinfo && clinfo->GetSchemaRules()) {
3669  if ( classSet.find( clinfo ) == classSet.end() ) {
3670  if (gDebug > 0) printf(" -class: %s stored the I/O customization rules\n",info->GetName());
3671 
3672  TObjArrayIter it( clinfo->GetSchemaRules()->GetRules() );
3673  ROOT::TSchemaRule *rule;
3674  while( (rule = (ROOT::TSchemaRule*)it.Next()) ) {
3675  TObjString *obj = new TObjString();
3676  rule->AsString(obj->String());
3677  listOfRules.Add(obj);
3678  }
3679  classSet.insert(clinfo);
3680  }
3681  }
3682  }
3683  }
3684 
3685  // Write the StreamerInfo list even if it is empty.
3686  fClassIndex->fArray[0] = 2; //to prevent adding classes in TStreamerInfo::TagFile
3687 
3688  if (listOfRules.GetEntries()) {
3689  // Only add the list of rules if we have something to say.
3690  list.Add(&listOfRules);
3691  }
3692 
3693  //free previous StreamerInfo record
3694  if (fSeekInfo) MakeFree(fSeekInfo,fSeekInfo+fNbytesInfo-1);
3695  //Create new key
3696  TKey key(&list,"StreamerInfo",GetBestBuffer(), this);
3697  fKeys->Remove(&key);
3698  fSeekInfo = key.GetSeekKey();
3699  fNbytesInfo = key.GetNbytes();
3700  SumBuffer(key.GetObjlen());
3701  key.WriteFile(0);
3702 
3703  fClassIndex->fArray[0] = 0;
3704 
3705  list.RemoveLast(); // remove the listOfRules.
3706 }
3707 
3708 ////////////////////////////////////////////////////////////////////////////////
3709 /// Open a file for reading through the file cache.
3710 ///
3711 /// The file will be downloaded to the cache and opened from there.
3712 /// If the download fails, it will be opened remotely.
3713 /// The file will be downloaded to the directory specified by SetCacheFileDir().
3714 
3715 TFile *TFile::OpenFromCache(const char *name, Option_t *, const char *ftitle,
3716  Int_t compress, Int_t netopt)
3717 {
3718  TFile *f = nullptr;
3719 
3720  if (fgCacheFileDir == "") {
3721  ::Warning("TFile::OpenFromCache",
3722  "you want to read through a cache, but you have no valid cache "
3723  "directory set - reading remotely");
3724  ::Info("TFile::OpenFromCache", "set cache directory using TFile::SetCacheFileDir()");
3725  } else {
3726  TUrl fileurl(name);
3727  TUrl tagurl;
3728 
3729  if ((!strcmp(fileurl.GetProtocol(), "file"))) {
3730  // it makes no sense to read local files through a file cache
3731  if (!fgCacheFileForce)
3732  ::Warning("TFile::OpenFromCache",
3733  "you want to read through a cache, but you are reading "
3734  "local files - CACHEREAD disabled");
3735  } else {
3736  // this is a remote file and worthwhile to be put into the local cache
3737  // now create cachepath to put it
3738  TString cachefilepath;
3739  TString cachefilepathbasedir;
3740  cachefilepath = fgCacheFileDir;
3741  cachefilepath += fileurl.GetFile();
3742  cachefilepathbasedir = gSystem->DirName(cachefilepath);
3743  if ((gSystem->mkdir(cachefilepathbasedir, kTRUE) < 0) &&
3744  (gSystem->AccessPathName(cachefilepathbasedir, kFileExists))) {
3745  ::Warning("TFile::OpenFromCache","you want to read through a cache, but I "
3746  "cannot create the directory %s - CACHEREAD disabled",
3747  cachefilepathbasedir.Data());
3748  } else {
3749  // check if this should be a zip file
3750  if (strlen(fileurl.GetAnchor())) {
3751  // remove the anchor and change the target name
3752  cachefilepath += "__";
3753  cachefilepath += fileurl.GetAnchor();
3754  fileurl.SetAnchor("");
3755  }
3756  if (strstr(name,"zip=")) {
3757  // filter out this option and change the target cache name
3758  TString urloptions = fileurl.GetOptions();
3759  TString newoptions;
3760  TObjArray *objOptions = urloptions.Tokenize("&");
3761  Int_t optioncount = 0;
3762  TString zipname;
3763  for (Int_t n = 0; n < objOptions->GetEntries(); n++) {
3764  TString loption = ((TObjString*)objOptions->At(n))->GetName();
3765  TObjArray *objTags = loption.Tokenize("=");
3766  if (objTags->GetEntries() == 2) {
3767  TString key = ((TObjString*)objTags->At(0))->GetName();
3768  TString value = ((TObjString*)objTags->At(1))->GetName();
3769  if (key.CompareTo("zip", TString::kIgnoreCase)) {
3770  if (optioncount!=0) {
3771  newoptions += "&";
3772  }
3773  newoptions += key;
3774  newoptions += "=";
3775  newoptions += value;
3776  ++optioncount;
3777  } else {
3778  zipname = value;
3779  }
3780  }
3781  delete objTags;
3782  }
3783  delete objOptions;
3784  fileurl.SetOptions(newoptions.Data());
3785  cachefilepath += "__";
3786  cachefilepath += zipname;
3787  fileurl.SetAnchor("");
3788  }
3789 
3790  Bool_t need2copy = kFALSE;
3791 
3792  // check if file is in the cache
3793  Long_t id;
3794  Long64_t size;
3795  Long_t flags;
3796  Long_t modtime;
3797  if (!gSystem->GetPathInfo(cachefilepath, &id, &size, &flags, &modtime)) {
3798  // file is in the cache
3799  if (!fgCacheFileDisconnected) {
3800  char cacheblock[256];
3801  char remotblock[256];
3802  // check the remote file for it's size and compare some magic bytes
3803  TString cfurl;
3804  cfurl = cachefilepath;
3805  cfurl += "?filetype=raw";
3806  TUrl rurl(name);
3807  TString ropt = rurl.GetOptions();
3808  ropt += "&filetype=raw";
3809  rurl.SetOptions(ropt);
3810 
3811  Bool_t forcedcache = fgCacheFileForce;
3812  fgCacheFileForce = kFALSE;
3813 
3814  TFile *cachefile = TFile::Open(cfurl, "READ");
3815  TFile *remotfile = TFile::Open(rurl.GetUrl(), "READ");
3816 
3817  fgCacheFileForce = forcedcache;
3818 
3819  if (!cachefile) {
3820  need2copy = kTRUE;
3821  ::Error("TFile::OpenFromCache",
3822  "cannot open the cache file to check cache consistency");
3823  return nullptr;
3824  }
3825 
3826  if (!remotfile) {
3827  ::Error("TFile::OpenFromCache",
3828  "cannot open the remote file to check cache consistency");
3829  return nullptr;
3830  }
3831 
3832  cachefile->Seek(0);
3833  remotfile->Seek(0);
3834 
3835  if ((!cachefile->ReadBuffer(cacheblock,256)) &&
3836  (!remotfile->ReadBuffer(remotblock,256))) {
3837  if (memcmp(cacheblock, remotblock, 256)) {
3838  ::Warning("TFile::OpenFromCache", "the header of the cache file "
3839  "differs from the remote file - forcing an update");
3840  need2copy = kTRUE;
3841  }
3842  } else {
3843  ::Warning("TFile::OpenFromCache", "the header of the cache and/or "
3844  "remote file are not readable - forcing an update");
3845  need2copy = kTRUE;
3846  }
3847 
3848  delete remotfile;
3849  delete cachefile;
3850  }
3851  } else {
3852  need2copy = kTRUE;
3853  }
3854 
3855  // try to fetch the file (disable now the forced caching)
3856  Bool_t forcedcache = fgCacheFileForce;
3857  fgCacheFileForce = kFALSE;
3858  if (need2copy && !TFile::Cp(name, cachefilepath)) {
3859  ::Warning("TFile::OpenFromCache", "you want to read through a cache, but I "
3860  "cannot make a cache copy of %s - CACHEREAD disabled",
3861  cachefilepathbasedir.Data());
3862  fgCacheFileForce = forcedcache;
3863  if (fgOpenTimeout != 0)
3864  return nullptr;
3865  } else {
3866  fgCacheFileForce = forcedcache;
3867  ::Info("TFile::OpenFromCache", "using local cache copy of %s [%s]",
3868  name, cachefilepath.Data());
3869  // finally we have the file and can open it locally
3870  fileurl.SetProtocol("file");
3871  fileurl.SetFile(cachefilepath);
3872 
3873  tagurl = fileurl;
3874  TString tagfile;
3875  tagfile = cachefilepath;
3876  tagfile += ".ROOT.cachefile";
3877  tagurl.SetFile(tagfile);
3878  // we symlink this file as a ROOT cached file
3879  gSystem->Symlink(gSystem->BaseName(cachefilepath), tagfile);
3880  return TFile::Open(fileurl.GetUrl(), "READ", ftitle, compress, netopt);
3881  }
3882  }
3883  }
3884  }
3885 
3886  // Failed
3887  return f;
3888 }
3889 
3890 ////////////////////////////////////////////////////////////////////////////////
3891 /// Create / open a file
3892 ///
3893 /// The type of the file can be either a
3894 /// TFile, TNetFile, TWebFile or any TFile derived class for which an
3895 /// plugin library handler has been registered with the plugin manager
3896 /// (for the plugin manager see the TPluginManager class). The returned
3897 /// type of TFile depends on the file name specified by 'url'.
3898 /// If 'url' is a '|'-separated list of file URLs, the 'URLs' are tried
3899 /// sequentially in the specified order until a successful open.
3900 /// If the file starts with "root:", "roots:" or "rootk:" a TNetFile object
3901 /// will be returned, with "http:" a TWebFile, with "file:" a local TFile,
3902 /// etc. (see the list of TFile plugin handlers in $ROOTSYS/etc/system.rootrc
3903 /// for regular expressions that will be checked) and as last a local file will
3904 /// be tried.
3905 /// Before opening a file via TNetFile a check is made to see if the URL
3906 /// specifies a local file. If that is the case the file will be opened
3907 /// via a normal TFile. To force the opening of a local file via a
3908 /// TNetFile use either TNetFile directly or specify as host "localhost".
3909 /// The netopt argument is only used by TNetFile. For the meaning of the
3910 /// options and other arguments see the constructors of the individual
3911 /// file classes. In case of error returns 0.
3912 ///
3913 /// For TFile implementations supporting asynchronous file open, see
3914 /// TFile::AsyncOpen(...), it is possible to request a timeout with the
3915 /// option <b>TIMEOUT=<secs></b>: the timeout must be specified in seconds and
3916 /// it will be internally checked with granularity of one millisec.
3917 /// For remote files there is the option: <b>CACHEREAD</b> opens an existing
3918 /// file for reading through the file cache. The file will be downloaded to
3919 /// the cache and opened from there. If the download fails, it will be opened remotely.
3920 /// The file will be downloaded to the directory specified by SetCacheFileDir().
3921 ///
3922 /// *The caller is responsible for deleting the pointer.*
3923 
3924 TFile *TFile::Open(const char *url, Option_t *options, const char *ftitle,
3925  Int_t compress, Int_t netopt)
3926 {
3927  TPluginHandler *h;
3928  TFile *f = nullptr;
3929  EFileType type = kFile;
3930 
3931  // Check input
3932  if (!url || strlen(url) <= 0) {
3933  ::Error("TFile::Open", "no url specified");
3934  return f;
3935  }
3936 
3937  TString expandedUrl(url);
3938  gSystem->ExpandPathName(expandedUrl);
3939 
3940  // If a timeout has been specified extract the value and try to apply it (it requires
3941  // support for asynchronous open, though; the following is completely transparent if
3942  // such support if not available for the required protocol)
3943  TString opts(options);
3944  Int_t ito = opts.Index("TIMEOUT=");
3945  if (ito != kNPOS) {
3946  TString sto = opts(ito + strlen("TIMEOUT="), opts.Length());
3947  while (!(sto.IsDigit()) && !(sto.IsNull())) { sto.Remove(sto.Length()-1,1); }
3948  if (!(sto.IsNull())) {
3949  // Timeout in millisecs
3950  Int_t toms = sto.Atoi() * 1000;
3951  if (gDebug > 0) ::Info("TFile::Open", "timeout of %d millisec requested", toms);
3952  // Remove from the options field
3953  sto.Insert(0, "TIMEOUT=");
3954  opts.ReplaceAll(sto, "");
3955  // Asynchrounous open
3956  TFileOpenHandle *fh = TFile::AsyncOpen(expandedUrl, opts, ftitle, compress, netopt);
3957  // Check the result in steps of 1 millisec
3958  TFile::EAsyncOpenStatus aos = TFile::kAOSNotAsync;
3959  aos = TFile::GetAsyncOpenStatus(fh);
3960  Int_t xtms = toms;
3961  while (aos == TFile::kAOSInProgress && xtms > 0) {
3962  gSystem->Sleep(1);
3963  xtms -= 1;
3964  aos = TFile::GetAsyncOpenStatus(fh);
3965  }
3966  if (aos == TFile::kAOSNotAsync || aos == TFile::kAOSSuccess) {
3967  // Do open the file now
3968  f = TFile::Open(fh);
3969  if (gDebug > 0) {
3970  if (aos == TFile::kAOSSuccess)
3971  ::Info("TFile::Open", "waited %d millisec for asynchronous open", toms - xtms);
3972  else
3973  ::Info("TFile::Open", "timeout option not supported (requires asynchronous"
3974  " open support)");
3975  }
3976  } else {
3977  if (xtms <= 0)
3978  ::Error("TFile::Open", "timeout expired while opening '%s'", expandedUrl.Data());
3979  // Cleanup the request
3980  SafeDelete(fh);
3981  }
3982  // Done
3983  return f;
3984  } else {
3985  ::Warning("TFile::Open", "incomplete 'TIMEOUT=' option specification - ignored");
3986  opts.ReplaceAll("TIMEOUT=", "");
3987  }
3988  }
3989 
3990  // We will use this from now on
3991  const char *option = opts;
3992 
3993  // Many URLs? Redirect output and print errors in case of global failure
3994  TString namelist(expandedUrl);
3995  Ssiz_t ip = namelist.Index("|");
3996  Bool_t rediroutput = (ip != kNPOS &&
3997  ip != namelist.Length()-1 && gDebug <= 0) ? kTRUE : kFALSE;
3998  RedirectHandle_t rh;
3999  if (rediroutput) {
4000  TString outf = ".TFileOpen_";
4001  FILE *fout = gSystem->TempFileName(outf);
4002  if (fout) {
4003  fclose(fout);
4004  gSystem->RedirectOutput(outf, "w", &rh);
4005  }
4006  }
4007 
4008  // Try sequentially all names in 'names'
4009  TString name, n;
4010  Ssiz_t from = 0;
4011  while (namelist.Tokenize(n, from, "|") && !f) {
4012 
4013  // check if we read through a file cache
4014  if (!strcasecmp(option, "CACHEREAD") ||
4015  ((!strcasecmp(option,"READ") || !option[0]) && fgCacheFileForce)) {
4016  // Try opening the file from the cache
4017  if ((f = TFile::OpenFromCache(n, option, ftitle, compress, netopt)))
4018  return f;
4019  }
4020 
4021  IncrementFileCounter();
4022 
4023  // change names to be recognized by the plugin manager
4024  // e.g. /protocol/path/to/file.root -> protocol:/path/to/file.root
4025  TUrl urlname(n, kTRUE);
4026  name = urlname.GetUrl();
4027  // Check first if a pending async open request matches this one
4028  if (fgAsyncOpenRequests && (fgAsyncOpenRequests->GetSize() > 0)) {
4029  TIter nxr(fgAsyncOpenRequests);
4030  TFileOpenHandle *fh = nullptr;
4031  while ((fh = (TFileOpenHandle *)nxr()))
4032  if (fh->Matches(name))
4033  return TFile::Open(fh);
4034  }
4035 
4036  TString urlOptions(urlname.GetOptions());
4037  if (urlOptions.BeginsWith("pmerge") || urlOptions.Contains("&pmerge") || urlOptions.Contains(" pmerge")) {
4038  type = kMerge;
4039 
4040  // Pass the full name including the url options:
4041  f = (TFile*) gROOT->ProcessLineFast(TString::Format("new TParallelMergingFile(\"%s\",\"%s\",\"%s\",%d)",n.Data(),option,ftitle,compress));
4042 
4043  } else {
4044  // Resolve the file type; this also adjusts names
4045  TString lfname = gEnv->GetValue("Path.Localroot", "");
4046  type = GetType(name, option, &lfname);
4047 
4048  if (type == kLocal) {
4049 
4050  // Local files
4051  if (lfname.IsNull()) {
4052  urlname.SetHost("");
4053  urlname.SetProtocol("file");
4054  lfname = urlname.GetUrl();
4055  }
4056  f = new TFile(lfname.Data(), option, ftitle, compress);
4057 
4058  } else if (type == kNet) {
4059 
4060  // Network files
4061  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name))) {
4062  if (h->LoadPlugin() == -1)
4063  return nullptr;
4064  f = (TFile*) h->ExecPlugin(5, name.Data(), option, ftitle, compress, netopt);
4065  }
4066 
4067  } else if (type == kWeb) {
4068 
4069  // Web files
4070  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name))) {
4071  if (h->LoadPlugin() == -1)
4072  return nullptr;
4073  f = (TFile*) h->ExecPlugin(2, name.Data(), option);
4074  }
4075 
4076  } else if (type == kFile) {
4077 
4078  // 'file:' protocol
4079  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name)) &&
4080  h->LoadPlugin() == 0) {
4081  name.ReplaceAll("file:", "");
4082  f = (TFile*) h->ExecPlugin(4, name.Data(), option, ftitle, compress);
4083  } else
4084  f = new TFile(name.Data(), option, ftitle, compress);
4085 
4086  } else {
4087 
4088  // no recognized specification: try the plugin manager
4089  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name.Data()))) {
4090  if (h->LoadPlugin() == -1)
4091  return nullptr;
4092  TClass *cl = TClass::GetClass(h->GetClass());
4093  if (cl && cl->InheritsFrom("TNetFile"))
4094  f = (TFile*) h->ExecPlugin(5, name.Data(), option, ftitle, compress, netopt);
4095  else
4096  f = (TFile*) h->ExecPlugin(4, name.Data(), option, ftitle, compress);
4097  } else {
4098  // Just try to open it locally but via TFile::Open, so that we pick-up the correct
4099  // plug-in in the case file name contains information about a special backend (e.g.
4100  f = TFile::Open(urlname.GetFileAndOptions(), option, ftitle, compress);
4101  }
4102  }
4103  }
4104 
4105  if (f && f->IsZombie()) {
4106  TString newUrl = f->GetNewUrl();
4107  delete f;
4108  if( newUrl.Length() && gEnv->GetValue("TFile.CrossProtocolRedirects", 1) )
4109  f = TFile::Open( newUrl, option, ftitle, compress );
4110  else
4111  f = nullptr;
4112  }
4113  }
4114 
4115  if (rediroutput) {
4116  // Restore output to stdout
4117  gSystem->RedirectOutput(0, "", &rh);
4118  // If we failed print error messages
4119  if (!f)
4120  gSystem->ShowOutput(&rh);
4121  // Remove the file
4122  gSystem->Unlink(rh.fFile);
4123  }
4124 
4125  // if the file is writable, non local, and not opened in raw mode
4126  // we create a default write cache of 512 KBytes
4127  if (type != kLocal && type != kFile &&
4128  f && f->IsWritable() && !f->IsRaw()) {
4129  new TFileCacheWrite(f, 1);
4130  }
4131 
4132  return f;
4133 }
4134 
4135 ////////////////////////////////////////////////////////////////////////////////
4136 /// Submit an asynchronous open request.
4137 
4138 /// See TFile::Open(const char *, ...) for an
4139 /// explanation of the arguments. A handler is returned which is to be passed
4140 /// to TFile::Open(TFileOpenHandle *) to get the real TFile instance once
4141 /// the file is open.
4142 /// This call never blocks and it is provided to allow parallel submission
4143 /// of file opening operations expected to take a long time.
4144 /// TFile::Open(TFileOpenHandle *) may block if the file is not yet ready.
4145 /// The sequence
4146 ///
4147 /// TFile::Open(TFile::AsyncOpen(const char *, ...))
4148 ///
4149 /// is equivalent to
4150 ///
4151 /// TFile::Open(const char *, ...)
4152 ///
4153 /// To be effective, the underlying TFile implementation must be able to
4154 /// support asynchronous open functionality. Currently, only TXNetFile
4155 /// supports it. If the functionality is not implemented, this call acts
4156 /// transparently by returning an handle with the arguments for the
4157 /// standard synchronous open run by TFile::Open(TFileOpenHandle *).
4158 /// The retuned handle will be adopted by TFile after opening completion
4159 /// in TFile::Open(TFileOpenHandle *); if opening is not finalized the
4160 /// handle must be deleted by the caller.
4161 
4162 TFileOpenHandle *TFile::AsyncOpen(const char *url, Option_t *option,
4163  const char *ftitle, Int_t compress,
4164  Int_t netopt)
4165 {
4166  TFileOpenHandle *fh = nullptr;
4167  TFile *f = nullptr;
4168  Bool_t notfound = kTRUE;
4169 
4170  // Check input
4171  if (!url || strlen(url) <= 0) {
4172  ::Error("TFile::AsyncOpen", "no url specified");
4173  return fh;
4174  }
4175 
4176  // Many URLs? Redirect output and print errors in case of global failure
4177  TString namelist(url);
4178  gSystem->ExpandPathName(namelist);
4179  Ssiz_t ip = namelist.Index("|");
4180  Bool_t rediroutput = (ip != kNPOS &&
4181  ip != namelist.Length()-1 && gDebug <= 0) ? kTRUE : kFALSE;
4182  RedirectHandle_t rh;
4183  if (rediroutput) {
4184  TString outf = ".TFileAsyncOpen_";
4185  FILE *fout = gSystem->TempFileName(outf);
4186  if (fout) {
4187  fclose(fout);
4188  gSystem->RedirectOutput(outf, "w", &rh);
4189  }
4190  }
4191 
4192  // Try sequentially all names in 'names'
4193  TString name, n;
4194  Ssiz_t from = 0;
4195  while (namelist.Tokenize(n, from, "|") && !f) {
4196 
4197  // change names to be recognized by the plugin manager
4198  // e.g. /protocol/path/to/file.root -> protocol:/path/to/file.root
4199  TUrl urlname(n, kTRUE);
4200  name = urlname.GetUrl();
4201 
4202  // Resolve the file type; this also adjusts names
4203  EFileType type = GetType(name, option);
4204 
4205  TPluginHandler *h = nullptr;
4206 
4207  // Here we send the asynchronous request if the functionality is implemented
4208  if (type == kNet) {
4209  // Network files
4210  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name)) &&
4211  (!strcmp(h->GetClass(),"TXNetFile") || !strcmp(h->GetClass(),"TNetXNGFile"))
4212  && h->LoadPlugin() == 0) {
4213  f = (TFile*) h->ExecPlugin(6, name.Data(), option, ftitle, compress, netopt, kTRUE);
4214  notfound = kFALSE;
4215  }
4216  }
4217  if ((h = gROOT->GetPluginManager()->FindHandler("TFile", name)) &&
4218  !strcmp(h->GetClass(),"TAlienFile") && h->LoadPlugin() == 0) {
4219  f = (TFile*) h->ExecPlugin(5, name.Data(), option, ftitle, compress, kTRUE);
4220  notfound = kFALSE;
4221  }
4222 
4223  }
4224 
4225  if (rediroutput) {
4226  // Restore output to stdout
4227  gSystem->RedirectOutput(0, "", &rh);
4228  // If we failed print error messages
4229  if (!notfound && !f)
4230  gSystem->ShowOutput(&rh);
4231  // Remove the file
4232  gSystem->Unlink(rh.fFile);
4233  }
4234 
4235  // Make sure that no error occurred
4236  if (notfound) {
4237  SafeDelete(f);
4238  // Save the arguments in the handler, so that a standard open can be
4239  // attempted later on
4240  fh = new TFileOpenHandle(name, option, ftitle, compress, netopt);
4241  } else if (f) {
4242  // Fill the opaque handler to be use to attach the file later on
4243  fh = new TFileOpenHandle(f);
4244  }
4245 
4246  // Record this request
4247  if (fh) {
4248  // Create the lst, if not done already
4249  if (!fgAsyncOpenRequests)
4250  fgAsyncOpenRequests = new TList;
4251  fgAsyncOpenRequests->Add(fh);
4252  }
4253 
4254  // We are done
4255  return fh;
4256 }
4257 
4258 ////////////////////////////////////////////////////////////////////////////////
4259 /// Waits for the completion of an asynchronous open request.
4260 ///
4261 /// Returns the pointer to the associated TFile, transferring ownership of the
4262 /// handle to the TFile instance.
4263 
4264 TFile *TFile::Open(TFileOpenHandle *fh)
4265 {
4266  TFile *f = nullptr;
4267 
4268  // Note that the request may have failed
4269  if (fh && fgAsyncOpenRequests) {
4270  // Remove it from the pending list: we need to do it at this level to avoid
4271  // recursive calls in the standard TFile::Open
4272  fgAsyncOpenRequests->Remove(fh);
4273  // Was asynchronous open functionality implemented?
4274  if ((f = fh->GetFile()) && !(f->IsZombie())) {
4275  // Yes: wait for the completion of the open phase, if needed
4276  Bool_t cr = (!strcmp(f->GetOption(),"CREATE") ||
4277  !strcmp(f->GetOption(),"RECREATE") ||
4278  !strcmp(f->GetOption(),"NEW")) ? kTRUE : kFALSE;
4279  f->Init(cr);
4280  } else {
4281  // No: process a standard open
4282  f = TFile::Open(fh->GetName(), fh->GetOpt(), fh->GetTitle(),
4283  fh->GetCompress(), fh->GetNetOpt());
4284  }
4285 
4286  // Adopt the handle instance in the TFile instance so that it gets
4287  // automatically cleaned up
4288  if (f) f->fAsyncHandle = fh;
4289  }
4290 
4291  // We are done
4292  return f;
4293 }
4294 
4295 ////////////////////////////////////////////////////////////////////////////////
4296 /// Interface to system open. All arguments like in POSIX open().
4297 
4298 Int_t TFile::SysOpen(const char *pathname, Int_t flags, UInt_t mode)
4299 {
4300 #if defined(R__WINGCC)
4301  // ALWAYS use binary mode - even cygwin text should be in unix format
4302  // although this is posix default it has to be set explicitly
4303  return ::open(pathname, flags | O_BINARY, mode);
4304 #elif defined(R__SEEK64)
4305  return ::open64(pathname, flags, mode);
4306 #else
4307  return ::open(pathname, flags, mode);
4308 #endif
4309 }
4310 
4311 ////////////////////////////////////////////////////////////////////////////////
4312 /// Interface to system close. All arguments like in POSIX close().
4313 
4314 Int_t TFile::SysClose(Int_t fd)
4315 {
4316  if (fd < 0) return 0;
4317  return ::close(fd);
4318 }
4319 
4320 ////////////////////////////////////////////////////////////////////////////////
4321 /// Interface to system read. All arguments like in POSIX read().
4322 
4323 Int_t TFile::SysRead(Int_t fd, void *buf, Int_t len)
4324 {
4325  return ::read(fd, buf, len);
4326 }
4327 
4328 ////////////////////////////////////////////////////////////////////////////////
4329 /// Interface to system write. All arguments like in POSIX write().
4330 
4331 Int_t TFile::SysWrite(Int_t fd, const void *buf, Int_t len)
4332 {
4333  return ::write(fd, buf, len);
4334 }
4335 ////////////////////////////////////////////////////////////////////////////////
4336 /// Interface to system lseek.
4337 ///
4338 /// All arguments like in POSIX lseek()
4339 /// except that the offset and return value are of a type which are
4340 /// able to handle 64 bit file systems.
4341 
4342 Long64_t TFile::SysSeek(Int_t fd, Long64_t offset, Int_t whence)
4343 {
4344 #if defined (R__SEEK64)
4345  return ::lseek64(fd, offset, whence);
4346 #elif defined(WIN32)
4347  return ::_lseeki64(fd, offset, whence);
4348 #else
4349  return ::lseek(fd, offset, whence);
4350 #endif
4351 }
4352 
4353 ////////////////////////////////////////////////////////////////////////////////
4354 /// Return file stat information.
4355 ///
4356 /// The interface and return value is
4357 /// identical to TSystem::GetPathInfo(). The function returns 0 in
4358 /// case of success and 1 if the file could not be stat'ed.
4359 
4360 Int_t TFile::SysStat(Int_t, Long_t *id, Long64_t *size, Long_t *flags,
4361  Long_t *modtime)
4362 {
4363  return gSystem->GetPathInfo(fRealName, id, size, flags, modtime);
4364 }
4365 
4366 ////////////////////////////////////////////////////////////////////////////////
4367 /// Interface to system fsync. All arguments like in POSIX fsync().
4368 
4369 Int_t TFile::SysSync(Int_t fd)
4370 {
4371  if (TestBit(kDevNull)) return 0;
4372 
4373 #ifndef WIN32
4374  return ::fsync(fd);
4375 #else
4376  return ::_commit(fd);
4377 #endif
4378 }
4379 
4380 ////////////////////////////////////////////////////////////////////////////////
4381 /// Return the total number of bytes written so far to the file.
4382 
4383 Long64_t TFile::GetBytesWritten() const
4384 {
4385  return fCacheWrite ? fCacheWrite->GetBytesInCache() + fBytesWrite : fBytesWrite;
4386 }
4387 
4388 ////////////////////////////////////////////////////////////////////////////////
4389 /// Static function returning the total number of bytes read from all files.
4390 
4391 Long64_t TFile::GetFileBytesRead()
4392 {
4393  return fgBytesRead;
4394 }
4395 
4396 ////////////////////////////////////////////////////////////////////////////////
4397 /// Static function returning the total number of bytes written to all files.
4398 /// Does not take into account what might still be in the write caches.
4399 
4400 Long64_t TFile::GetFileBytesWritten()
4401 {
4402  return fgBytesWrite;
4403 }
4404 
4405 ////////////////////////////////////////////////////////////////////////////////
4406 /// Static function returning the total number of read calls from all files.
4407 
4408 Int_t TFile::GetFileReadCalls()
4409 {
4410  return fgReadCalls;
4411 }
4412 
4413 ////////////////////////////////////////////////////////////////////////////////
4414 /// Static function returning the readahead buffer size.
4415 
4416 Int_t TFile::GetReadaheadSize()
4417 {
4418  return fgReadaheadSize;
4419 }
4420 
4421 //______________________________________________________________________________
4422 void TFile::SetReadaheadSize(Int_t bytes) { fgReadaheadSize = bytes; }
4423 
4424 //______________________________________________________________________________
4425 void TFile::SetFileBytesRead(Long64_t bytes) { fgBytesRead = bytes; }
4426 
4427 //______________________________________________________________________________
4428 void TFile::SetFileBytesWritten(Long64_t bytes) { fgBytesWrite = bytes; }
4429 
4430 //______________________________________________________________________________
4431 void TFile::SetFileReadCalls(Int_t readcalls) { fgReadCalls = readcalls; }
4432 
4433 //______________________________________________________________________________
4434 Long64_t TFile::GetFileCounter() { return fgFileCounter; }
4435 
4436 //______________________________________________________________________________
4437 void TFile::IncrementFileCounter() { fgFileCounter++; }
4438 
4439 ////////////////////////////////////////////////////////////////////////////////
4440 /// Sets the directory where to locally stage/cache remote files.
4441 /// If the directory is not writable by us return kFALSE.
4442 
4443 Bool_t TFile::SetCacheFileDir(std::string_view cachedir, Bool_t operatedisconnected,
4444  Bool_t forcecacheread )
4445 {
4446  TString cached{cachedir};
4447  if (!cached.EndsWith("/"))
4448  cached += "/";
4449 
4450  if (gSystem->AccessPathName(cached, kFileExists)) {
4451  // try to create it
4452  gSystem->mkdir(cached, kTRUE);
4453  if (gSystem->AccessPathName(cached, kFileExists)) {
4454  ::Error("TFile::SetCacheFileDir", "no sufficient permissions on cache directory %s or cannot create it", TString(cachedir).Data());
4455  fgCacheFileDir = "";
4456  return kFALSE;
4457  }
4458  gSystem->Chmod(cached, 0700);
4459  }
4460  if (gSystem->AccessPathName(cached, kWritePermission))
4461  gSystem->Chmod(cached, 0700);
4462  fgCacheFileDir = cached;
4463  fgCacheFileDisconnected = operatedisconnected;
4464  fgCacheFileForce = forcecacheread;
4465  return kTRUE;
4466 }
4467 
4468 ////////////////////////////////////////////////////////////////////////////////
4469 /// Get the directory where to locally stage/cache remote files.
4470 
4471 const char *TFile::GetCacheFileDir()
4472 {
4473  return fgCacheFileDir;
4474 }
4475 
4476 ////////////////////////////////////////////////////////////////////////////////
4477 /// Try to shrink the cache to the desired size.
4478 ///
4479 /// With the clenupinterval you can specify the minimum amount of time after
4480 /// the previous cleanup before the cleanup operation is repeated in
4481 /// the cache directory
4482 
4483 Bool_t TFile::ShrinkCacheFileDir(Long64_t shrinksize, Long_t cleanupinterval)
4484 {
4485  if (fgCacheFileDir == "") {
4486  return kFALSE;
4487  }
4488 
4489  // check the last clean-up in the cache
4490  Long_t id;
4491  Long64_t size;
4492  Long_t flags;
4493  Long_t modtime;
4494 
4495  TString cachetagfile = fgCacheFileDir;
4496  cachetagfile += ".tag.ROOT.cache";
4497  if (!gSystem->GetPathInfo(cachetagfile, &id, &size, &flags, &modtime)) {
4498  // check the time passed since last cache cleanup
4499  Long_t lastcleanuptime = ((Long_t)time(0) - modtime);
4500  if (lastcleanuptime < cleanupinterval) {
4501  ::Info("TFile::ShrinkCacheFileDir", "clean-up is skipped - last cleanup %lu seconds ago - you requested %lu", lastcleanuptime, cleanupinterval);
4502  return kTRUE;
4503  }
4504  }
4505 
4506  // (re-)create the cache tag file
4507  cachetagfile += "?filetype=raw";
4508  TFile *tagfile = nullptr;
4509 
4510  if (!(tagfile = TFile::Open(cachetagfile, "RECREATE"))) {
4511  ::Error("TFile::ShrinkCacheFileDir", "cannot create the cache tag file %s", cachetagfile.Data());
4512  return kFALSE;
4513  }
4514 
4515  // the shortest garbage collector in the world - one long line of PERL - unlinks files only,
4516  // if there is a symbolic link with '.ROOT.cachefile' for safety ;-)
4517 
4518  TString cmd;
4519 #if defined(R__WIN32)
4520  cmd = "echo <TFile::ShrinkCacheFileDir>: cleanup to be implemented";
4521 #elif defined(R__MACOSX)
4522  cmd.Format("perl -e 'my $cachepath = \"%s\"; my $cachesize = %lld;my $findcommand=\"find $cachepath -type f -exec stat -f \\\"\\%%a::\\%%N::\\%%z\\\" \\{\\} \\\\\\;\";my $totalsize=0;open FIND, \"$findcommand | sort -k 1 |\";while (<FIND>) { my ($accesstime, $filename, $filesize) = split \"::\",$_; $totalsize += $filesize;if ($totalsize > $cachesize) {if ( ( -e \"${filename}.ROOT.cachefile\" ) && ( -e \"${filename}\" ) ) {unlink \"$filename.ROOT.cachefile\";unlink \"$filename\";}}}close FIND;' ", fgCacheFileDir.Data(),shrinksize);
4523 #else
4524  cmd.Format("perl -e 'my $cachepath = \"%s\"; my $cachesize = %lld;my $findcommand=\"find $cachepath -type f -exec stat -c \\\"\\%%x::\\%%n::\\%%s\\\" \\{\\} \\\\\\;\";my $totalsize=0;open FIND, \"$findcommand | sort -k 1 |\";while (<FIND>) { my ($accesstime, $filename, $filesize) = split \"::\",$_; $totalsize += $filesize;if ($totalsize > $cachesize) {if ( ( -e \"${filename}.ROOT.cachefile\" ) && ( -e \"${filename}\" ) ) {unlink \"$filename.ROOT.cachefile\";unlink \"$filename\";}}}close FIND;' ", fgCacheFileDir.Data(),shrinksize);
4525 #endif
4526 
4527  tagfile->WriteBuffer(cmd, 4096);
4528  delete tagfile;
4529 
4530  if ((gSystem->Exec(cmd)) != 0) {
4531  ::Error("TFile::ShrinkCacheFileDir", "error executing clean-up script");
4532  return kFALSE;
4533  }
4534 
4535  return kTRUE;
4536 }
4537 
4538 ////////////////////////////////////////////////////////////////////////////////
4539 /// Sets open timeout time (in ms). Returns previous timeout value.
4540 
4541 UInt_t TFile::SetOpenTimeout(UInt_t timeout)
4542 {
4543  UInt_t to = fgOpenTimeout;
4544  fgOpenTimeout = timeout;
4545  return to;
4546 }
4547 
4548 ////////////////////////////////////////////////////////////////////////////////
4549 /// Returns open timeout (in ms).
4550 
4551 UInt_t TFile::GetOpenTimeout()
4552 {
4553  return fgOpenTimeout;
4554 }
4555 
4556 ////////////////////////////////////////////////////////////////////////////////
4557 /// Sets only staged flag. Returns previous value of flag.
4558 /// When true we check before opening the file if it is staged, if not,
4559 /// the open fails.
4560 
4561 Bool_t TFile::SetOnlyStaged(Bool_t onlystaged)
4562 {
4563  Bool_t f = fgOnlyStaged;
4564  fgOnlyStaged = onlystaged;
4565  return f;
4566 }
4567 
4568 ////////////////////////////////////////////////////////////////////////////////
4569 /// Returns staged only flag.
4570 
4571 Bool_t TFile::GetOnlyStaged()
4572 {
4573  return fgOnlyStaged;
4574 }
4575 
4576 ////////////////////////////////////////////////////////////////////////////////
4577 /// Return kTRUE if 'url' matches the coordinates of this file.
4578 ///
4579 /// The check is implementation dependent and may need to be overload
4580 /// by each TFile implementation relying on this check.
4581 /// The default implementation checks the file name only.
4582 
4583 Bool_t TFile::Matches(const char *url)
4584 {
4585  // Check the full URL, including port and FQDN.
4586  TUrl u(url);
4587 
4588  // Check
4589  if (!strcmp(u.GetFile(), fUrl.GetFile())) {
4590  // Check ports
4591  if (u.GetPort() == fUrl.GetPort()) {
4592  if (!strcmp(u.GetHostFQDN(), fUrl.GetHostFQDN())) {
4593  // Ok, coordinates match
4594  return kTRUE;
4595  }
4596  }
4597  }
4598 
4599  // Default is not matching
4600  return kFALSE;
4601 }
4602 
4603 ////////////////////////////////////////////////////////////////////////////////
4604 /// Return kTRUE if this async request matches the open request
4605 /// specified by 'url'
4606 
4607 Bool_t TFileOpenHandle::Matches(const char *url)
4608 {
4609  if (fFile) {
4610  return fFile->Matches(url);
4611  } else if (fName.Length() > 0){
4612  // Deep check of URLs
4613  TUrl u(url);
4614  TUrl uref(fName);
4615  if (!strcmp(u.GetFile(), uref.GetFile())) {
4616  // Check ports
4617  if (u.GetPort() == uref.GetPort()) {
4618  // Check also the host name
4619  if (!strcmp(u.GetHostFQDN(), uref.GetHostFQDN())) {
4620  // Ok, coordinates match
4621  return kTRUE;
4622  }
4623  }
4624  }
4625  }
4626 
4627  // Default is not matching
4628  return kFALSE;
4629 }
4630 
4631 ////////////////////////////////////////////////////////////////////////////////
4632 /// Resolve the file type as a function of the protocol field in 'name'
4633 ///
4634 /// If defined, the string 'prefix' is added when testing the locality of
4635 /// a 'name' with network-like structure (i.e. root://host//path); if the file
4636 /// is local, on return 'prefix' will contain the actual local path of the file.
4637 
4638 TFile::EFileType TFile::GetType(const char *name, Option_t *option, TString *prefix)
4639 {
4640  EFileType type = kDefault;
4641 
4642  TPMERegexp re("^(root|xroot).*", "i");
4643  if (re.Match(name)) {
4644  //
4645  // Should be a network file ...
4646  type = kNet;
4647  // ... but make sure that is not local or that a remote-like connection
4648  // is forced. Treat it as local if:
4649  // i) the url points to the localhost, the file will be opened in
4650  // readonly mode and the current user has read access;
4651  // ii) the specified user is equal to the current user then open local
4652  // TFile.
4653  Bool_t localFile = kFALSE;
4654  TUrl url(name);
4655  //
4656  // Check whether we should try to optimize for local files
4657  Bool_t forceRemote = gEnv->GetValue("Path.ForceRemote", 0);
4658  forceRemote = (forceRemote) ? kTRUE : gEnv->GetValue("TFile.ForceRemote", 0);
4659  TString opts = url.GetOptions();
4660  if (opts.Contains("remote=1"))
4661  forceRemote = kTRUE;
4662  else if (opts.Contains("remote=0"))
4663  forceRemote = kFALSE;
4664  if (!forceRemote) {
4665  // Generic locality test
4666  localFile = gSystem->IsPathLocal(name);
4667  if (localFile) {
4668  // Local path including the prefix
4669  const char *fname = url.GetFileAndOptions();
4670  TString lfname;
4671  if (fname[0] == '/') {
4672  if (prefix)
4673  lfname.Form("%s%s", prefix->Data(), fname);
4674  else
4675  lfname = fname;
4676  } else if (fname[0] == '~' || fname[0] == '$') {
4677  lfname = fname;
4678  } else {
4679  lfname.Form("%s/%s", gSystem->HomeDirectory(), fname);
4680  }
4681  // If option "READ" test existence and access
4682  TString opt = option;
4683  Bool_t read = (opt.IsNull() ||
4684  !opt.CompareTo("READ", TString::kIgnoreCase)) ? kTRUE : kFALSE;
4685  if (read) {
4686  char *fn;
4687  if ((fn = gSystem->ExpandPathName(TUrl(lfname).GetFile()))) {
4688  if (gSystem->AccessPathName(fn, kReadPermission))
4689  localFile = kFALSE;
4690  delete [] fn;
4691  }
4692  }
4693  // Return full local path if requested (and if the case)
4694  if (localFile && prefix)
4695  *prefix = lfname;
4696  }
4697  }
4698  //
4699  // Adjust the type according to findings
4700  type = (localFile) ? kLocal : type;
4701  } else if (TPMERegexp("^(http[s]?|s3http[s]?|[a]?s3|gs|gshttp[s]?){1}:", "i").Match(name)) {
4702  //
4703  // Web file
4704  type = kWeb;
4705  } else if (!strncmp(name, "file:", 5)) {
4706  //
4707  // 'file' protocol
4708  type = kFile;
4709  }
4710  // We are done
4711  return type;
4712 }
4713 
4714 ////////////////////////////////////////////////////////////////////////////////
4715 /// Get status of the async open request related to 'name'.
4716 
4717 TFile::EAsyncOpenStatus TFile::GetAsyncOpenStatus(const char* name)
4718 {
4719  // Check the list of pending async open requests
4720  if (fgAsyncOpenRequests && (fgAsyncOpenRequests->GetSize() > 0)) {
4721  TIter nxr(fgAsyncOpenRequests);
4722  TFileOpenHandle *fh = nullptr;
4723  while ((fh = (TFileOpenHandle *)nxr()))
4724  if (fh->Matches(name))
4725  return TFile::GetAsyncOpenStatus(fh);
4726  }
4727 
4728  // Check also the list of files open
4729  R__LOCKGUARD(gROOTMutex);
4730  TSeqCollection *of = gROOT->GetListOfFiles();
4731  if (of && (of->GetSize() > 0)) {
4732  TIter nxf(of);
4733  TFile *f = nullptr;
4734  while ((f = (TFile *)nxf()))
4735  if (f->Matches(name))
4736  return f->GetAsyncOpenStatus();
4737  }
4738 
4739  // Default is synchronous mode
4740  return kAOSNotAsync;
4741 }
4742 
4743 ////////////////////////////////////////////////////////////////////////////////
4744 /// Get status of the async open request related to 'handle'.
4745 
4746 TFile::EAsyncOpenStatus TFile::GetAsyncOpenStatus(TFileOpenHandle *handle)
4747 {
4748  if (handle && handle->fFile) {
4749  if (!handle->fFile->IsZombie())
4750  return handle->fFile->GetAsyncOpenStatus();
4751  else
4752  return TFile::kAOSFailure;
4753  }
4754 
4755  // Default is synchronous mode
4756  return TFile::kAOSNotAsync;
4757 }
4758 
4759 ////////////////////////////////////////////////////////////////////////////////
4760 /// Get final URL for file being opened asynchronously.
4761 /// Returns 0 is the information is not yet available.
4762 
4763 const TUrl *TFile::GetEndpointUrl(const char* name)
4764 {
4765  // Check the list of pending async open requests
4766  if (fgAsyncOpenRequests && (fgAsyncOpenRequests->GetSize() > 0)) {
4767  TIter nxr(fgAsyncOpenRequests);
4768  TFileOpenHandle *fh = nullptr;
4769  while ((fh = (TFileOpenHandle *)nxr()))
4770  if (fh->Matches(name))
4771  if (fh->fFile)
4772  return fh->fFile->GetEndpointUrl();
4773  }
4774 
4775  // Check also the list of files open
4776  R__LOCKGUARD(gROOTMutex);
4777  TSeqCollection *of = gROOT->GetListOfFiles();
4778  if (of && (of->GetSize() > 0)) {
4779  TIter nxf(of);
4780  TFile *f = nullptr;
4781  while ((f = (TFile *)nxf()))
4782  if (f->Matches(name))
4783  return f->GetEndpointUrl();
4784  }
4785 
4786  // Information not yet available
4787  return (const TUrl *)nullptr;
4788 }
4789 
4790 ////////////////////////////////////////////////////////////////////////////////
4791 /// Print file copy progress.
4792 
4793 void TFile::CpProgress(Long64_t bytesread, Long64_t size, TStopwatch &watch)
4794 {
4795  fprintf(stderr, "[TFile::Cp] Total %.02f MB\t|", (Double_t)size/1048576);
4796 
4797  for (int l = 0; l < 20; l++) {
4798  if (size > 0) {
4799  if (l < 20*bytesread/size)
4800  fprintf(stderr, "=");
4801  else if (l == 20*bytesread/size)
4802  fprintf(stderr, ">");
4803  else if (l > 20*bytesread/size)
4804  fprintf(stderr, ".");
4805  } else
4806  fprintf(stderr, "=");
4807  }
4808  // Allow to update the GUI while uploading files
4809  gSystem->ProcessEvents();
4810  watch.Stop();
4811  Double_t lCopy_time = watch.RealTime();
4812  fprintf(stderr, "| %.02f %% [%.01f MB/s]\r",
4813  100.0*(size?(bytesread/((float)size)):1), (lCopy_time>0.)?bytesread/lCopy_time/1048576.:0.);
4814  watch.Continue();
4815 }
4816 
4817 ////////////////////////////////////////////////////////////////////////////////
4818 /// Allows to copy this file to the dst URL. Returns kTRUE in case of success,
4819 /// kFALSE otherwise.
4820 
4821 Bool_t TFile::Cp(const char *dst, Bool_t progressbar, UInt_t buffersize)
4822 {
4823  Bool_t rmdestiferror = kFALSE;
4824  TStopwatch watch;
4825  Bool_t success = kFALSE;
4826 
4827  TUrl dURL(dst, kTRUE);
4828 
4829  TString oopt = "RECREATE";
4830  TString ourl = dURL.GetUrl();
4831 
4832  // Files will be open in RAW mode
4833  TString raw = "filetype=raw";
4834 
4835  // Set optimization options for the destination file
4836  TString opt = dURL.GetOptions();
4837  if (opt != "") opt += "&";
4838  opt += raw;
4839 
4840  // AliEn files need to know where the source file is
4841  if (!strcmp(dURL.GetProtocol(), "alien"))
4842  opt += TString::Format("&source=%s", GetName());
4843 
4844  dURL.SetOptions(opt);
4845 
4846  char *copybuffer = nullptr;
4847 
4848  TFile *sfile = this;
4849  TFile *dfile = nullptr;
4850 
4851  // "RECREATE" does not work always well with XROOTD
4852  // namely when some pieces of the path are missing;
4853  // we force "NEW" in such a case
4854  if (TFile::GetType(ourl, "") == TFile::kNet) {
4855  if (gSystem->AccessPathName(ourl)) {
4856  oopt = "NEW";
4857  // Force creation of the missing parts of the path
4858  opt += "&mkpath=1";
4859  dURL.SetOptions(opt);
4860  }
4861  }
4862 
4863  // Open destination file
4864  if (!(dfile = TFile::Open(dURL.GetUrl(), oopt))) {
4865  ::Error("TFile::Cp", "cannot open destination file %s", dst);
4866  goto copyout;
4867  }
4868 
4869  // Probably we created a new file
4870  // We have to remove it in case of errors
4871  rmdestiferror = kTRUE;
4872 
4873  sfile->Seek(0);
4874  dfile->Seek(0);
4875 
4876  copybuffer = new char[buffersize];
4877  if (!copybuffer) {
4878  ::Error("TFile::Cp", "cannot allocate the copy buffer");
4879  goto copyout;
4880  }
4881 
4882  Bool_t readop, writeop;
4883  Long64_t read, written, totalread, filesize, b00;
4884 
4885  totalread = 0;
4886  filesize = sfile->GetSize();
4887 
4888  watch.Start();
4889 
4890  b00 = sfile->GetBytesRead();
4891 
4892  do {
4893  if (progressbar) CpProgress(totalread, filesize,watch);
4894 
4895  Long64_t b1 = sfile->GetBytesRead() - b00;
4896 
4897  Long64_t readsize;
4898  if (filesize - b1 > (Long64_t)buffersize) {
4899  readsize = buffersize;
4900  } else {
4901  readsize = filesize - b1;
4902  }
4903 
4904  if (readsize == 0) break;
4905 
4906  Long64_t b0 = sfile->GetBytesRead();
4907  sfile->Seek(totalread,TFile::kBeg);
4908  readop = sfile->ReadBuffer(copybuffer, (Int_t)readsize);
4909  read = sfile->GetBytesRead() - b0;
4910  if ((read <= 0) || readop) {
4911  ::Error("TFile::Cp", "cannot read from source file %s. readsize=%lld read=%lld readop=%d",
4912  sfile->GetName(), readsize, read, readop);
4913  goto copyout;
4914  }
4915 
4916  Long64_t w0 = dfile->GetBytesWritten();
4917  writeop = dfile->WriteBuffer(copybuffer, (Int_t)read);
4918  written = dfile->GetBytesWritten() - w0;
4919  if ((written != read) || writeop) {
4920  ::Error("TFile::Cp", "cannot write %lld bytes to destination file %s", read, dst);
4921  goto copyout;
4922  }
4923  totalread += read;
4924  } while (read == (Long64_t)buffersize);
4925 
4926  if (progressbar) {
4927  CpProgress(totalread, filesize,watch);
4928  fprintf(stderr, "\n");
4929  }
4930 
4931  success = kTRUE;
4932 
4933 copyout:
4934  if (dfile) dfile->Close();
4935 
4936  if (dfile) delete dfile;
4937  if (copybuffer) delete[] copybuffer;
4938 
4939  if (rmdestiferror && (success != kTRUE))
4940  gSystem->Unlink(dst);
4941 
4942  watch.Stop();
4943  watch.Reset();
4944 
4945  return success;
4946 }
4947 
4948 ////////////////////////////////////////////////////////////////////////////////
4949 /// Allows to copy file from src to dst URL. Returns kTRUE in case of success,
4950 /// kFALSE otherwise.
4951 
4952 Bool_t TFile::Cp(const char *src, const char *dst, Bool_t progressbar,
4953  UInt_t buffersize)
4954 {
4955  TUrl sURL(src, kTRUE);
4956 
4957  // Files will be open in RAW mode
4958  TString raw = "filetype=raw";
4959 
4960  // Set optimization options for the source file
4961  TString opt = sURL.GetOptions();
4962  if (opt != "") opt += "&";
4963  opt += raw;
4964  // Netx-related options:
4965  // cachesz = 4*buffersize -> 4 buffers as peak mem usage
4966  // readaheadsz = 2*buffersize -> Keep at max 4*buffersize bytes outstanding when reading
4967  // rmpolicy = 1 -> Remove from the cache the blk with the least offset
4968  opt += TString::Format("&cachesz=%d&readaheadsz=%d&rmpolicy=1", 4*buffersize, 2*buffersize);
4969  sURL.SetOptions(opt);
4970 
4971  TFile *sfile = nullptr;
4972 
4973  Bool_t success = kFALSE;
4974 
4975  // Open source file
4976  if (!(sfile = TFile::Open(sURL.GetUrl(), "READ"))) {
4977  ::Error("TFile::Cp", "cannot open source file %s", src);
4978  } else {
4979  success = sfile->Cp(dst, progressbar, buffersize);
4980  }
4981 
4982  if (sfile) {
4983  sfile->Close();
4984  delete sfile;
4985  }
4986 
4987  return success;
4988 }
4989 
4990 //______________________________________________________________________________
4991 //The next statement is not active anymore on Linux.
4992 //Using posix_fadvise introduces a performance penalty (10 %) on optimized files
4993 //and in addition it destroys the information of TTreePerfStats
4994 #if defined(R__neverLINUX) && !defined(R__WINGCC)
4995 Bool_t TFile::ReadBufferAsync(Long64_t offset, Int_t len)
4996 {
4997  // Read specified byte range asynchronously. Actually we tell the kernel
4998  // which blocks we are going to read so it can start loading these blocks
4999  // in the buffer cache.
5000 
5001  // Shortcut to avoid having to implement dummy ReadBufferAsync() in all
5002  // I/O plugins. Override ReadBufferAsync() in plugins if async is supported.
5003  if (IsA() != TFile::Class())
5004  return kTRUE;
5005 
5006  int advice = POSIX_FADV_WILLNEED;
5007  if (len == 0) {
5008  // according POSIX spec if len is zero, all data following offset
5009  // is specified. Nevertheless ROOT uses zero to probe readahead
5010  // capabilities.
5011  advice = POSIX_FADV_NORMAL;
5012  }
5013  Double_t start = 0;
5014  if (gPerfStats) start = TTimeStamp();
5015 #if defined(R__SEEK64)
5016  Int_t result = posix_fadvise64(fD, offset, len, advice);
5017 #else
5018  Int_t result = posix_fadvise(fD, offset, len, advice);
5019 #endif
5020  if (gPerfStats) {
5021  gPerfStats->FileReadEvent(this, len, start);
5022  }
5023  return (result != 0);
5024 }
5025 #else
5026 Bool_t TFile::ReadBufferAsync(Long64_t, Int_t)
5027 {
5028  // Not supported yet on non Linux systems.
5029 
5030  return kTRUE;
5031 }
5032 #endif
5033 
5034 ////////////////////////////////////////////////////////////////////////////////
5035 /// Max number of bytes to prefetch.
5036 ///
5037 /// By default this is 75% of the
5038 /// read cache size. But specific TFile implementations may need to change it
5039 
5040 Int_t TFile::GetBytesToPrefetch() const
5041 {
5042  TFileCacheRead *cr = nullptr;
5043  if ((cr = GetCacheRead())) {
5044  Int_t bytes = cr->GetBufferSize() / 4 * 3;
5045  return ((bytes < 0) ? 0 : bytes);
5046  }
5047  return 0;
5048 }