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