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