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