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