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