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