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