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