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