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