fileOperationBroadcast.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2022-2023 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "fileOperation.H"
29 #include "Pstream.H"
30 #include "OSspecific.H"
31 #include <fstream>
32 #include <cinttypes>
33 
34 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38 
39 // Implementation for broadcasting an individual file
40 static void broadcastFile_single
41 (
42  const label comm,
43  const bool writeOnProc,
44  const fileName& srcName,
45  const fileName& dstName,
46  DynamicList<char>& buffer
47 )
48 {
50  {
52  << "Reading file " << srcName
53  << " on master processor and copying to " << dstName
54  << endl;
55  }
56 
57  // Read file on master, broadcast to all but write on IOranks only.
58  // This is a lot easier / possibly quicker?
59  // than -allocating our own IO communicator
60  // -send to all IO subProcs -releasing communicator
61 
62  // The file length uses uint64_t instead of off_t
63  // since MINGW has something odd for off_t:
64  //
65  // ... error: call of overloaded 'min(off_t&, off_t&)' is ambiguous
66 
67  // Tuple2<off_t, mode_t> lengthAndMode(0, 0);
68  Tuple2<uint64_t, mode_t> lengthAndMode(0, 0);
69 
70  std::unique_ptr<std::ifstream> srcStream;
71  std::unique_ptr<std::ofstream> dstStream;
72 
73  if (UPstream::master(comm))
74  {
75  // Read (see newIFstream)
76  auto fileLen = Foam::fileSize(srcName);
77  if (fileLen > 0)
78  {
79  lengthAndMode.first() = uint64_t(fileLen);
80  }
81  lengthAndMode.second() = Foam::mode(srcName);
82 
83  srcStream.reset
84  (
85  new std::ifstream
86  (
87  srcName,
88  std::ios_base::in | std::ios_base::binary
89  )
90  );
91  if (!srcStream->good())
92  {
93  FatalIOErrorInFunction(srcName)
94  << "Could not open file for reading!"
95  << exit(FatalIOError);
96  }
97  }
98 
99  if
100  (
101  writeOnProc
102  &&
103  (
104  UPstream::master(comm)
105  ? (srcName != dstName)
106  : UPstream::is_subrank(comm)
107  )
108  )
109  {
110  // Make sure the destination directory exists.
111  // - will fail itself if not possible
112  // - no-op if directory already exists
113  Foam::mkDir(dstName.path());
114 
115  dstStream.reset
116  (
117  new std::ofstream
118  (
119  dstName,
120  std::ios_base::out | std::ios_base::binary
121  )
122  );
123 
124  if (!dstStream->good())
125  {
126  // Fail noisily or silently?
127  if (!dstStream->good())
128  {
129  dstStream.reset(nullptr);
130  }
131  }
132 
133  // Adjust mode?
134  // Foam::chMode(dstName, fileMode);
135  }
136 
137  // Broadcast size and mode (contiguous data)
139  (
140  reinterpret_cast<char*>(&lengthAndMode),
141  sizeof(lengthAndMode),
142  comm
143  );
144 
145  uint64_t fileLength = lengthAndMode.first();
146 
147  const uint64_t maxChunkSize =
148  (
150  ? uint64_t(UPstream::maxCommsSize)
151  : uint64_t(pTraits<int>::max)
152  );
153 
154 
155  while (fileLength > 0)
156  {
157  const uint64_t sendSize = min(fileLength, maxChunkSize);
158  fileLength -= sendSize;
159 
160  // Read file contents into a character buffer
161  buffer.resize_nocopy(static_cast<label>(sendSize));
162 
163  if (srcStream)
164  {
165  srcStream->read(buffer.data_bytes(), buffer.size_bytes());
166  }
167 
168  UPstream::broadcast(buffer.data_bytes(), buffer.size_bytes(), comm);
169 
170  if (dstStream)
171  {
172  dstStream->write(buffer.data_bytes(), buffer.size_bytes());
173  }
174  }
175 }
176 
177 
178 // Implementation for broadcasting directory contents or an individual file
179 static bool broadcastFile_recursive
180 (
181  const label comm,
182  const bool writeOnProc,
183  const fileName& src,
184  const fileName& dst,
185  // const bool followLink
186  DynamicList<char>& buffer
187 )
188 {
189  // Read file on master, broadcast to all but write on procs with doWrite
190  // only. This is a lot easier / possibly quicker?
191  // than -allocating our own IO communicator
192  // -send to all IO subProcs -releasing communicator
193 
195  {
197  << "Reading " << src
198  << " on master processor and writing a copy to " << dst
199  << endl;
200  }
201 
202  fileName::Type srcType;
203  if (UPstream::master(comm))
204  {
205  srcType = src.type(false); // followLink = false, gzip = false
206  }
207 
209  (
210  reinterpret_cast<char*>(&srcType), // contiguous data
211  sizeof(srcType),
212  comm
213  );
214 
215  // Check type of source file.
216  if (srcType == fileName::FILE)
217  {
218  broadcastFile_single(comm, writeOnProc, src, dst, buffer);
219  }
220  else if (srcType == fileName::SYMLINK)
221  {
222  WarningInFunction<< "Copying symbolic links not supported" << endl;
223 
224  // Read the link target
225  fileName linkTarget;
226  if (UPstream::master(comm))
227  {
228  linkTarget = Foam::readLink(src);
229  }
230  Pstream::broadcast(linkTarget, comm);
231 
232  if
233  (
234  writeOnProc
235  &&
236  (
237  UPstream::master(comm)
238  ? (src != dst)
239  : UPstream::is_subrank(comm)
240  )
241  )
242  {
243  // Recreate softlink on remote processor
244  return Foam::ln(linkTarget, dst);
245  }
246  }
247  else if (srcType == fileName::DIRECTORY)
248  {
249  // Copy files
250  {
251  fileNameList files;
252  if (UPstream::master(comm))
253  {
254  files = Foam::readDir
255  (
256  src,
258  false // Never trim '.gz' ending
259  //followLink
260  );
261  }
262  Pstream::broadcast(files, comm);
263 
264  for (const fileName& item : files)
265  {
266  // File to file
268  (
269  comm,
270  writeOnProc,
271  src/item,
272  dst/item,
273  //followLink
274  buffer
275  );
276  }
277  }
278 
279  // Copy softlinks
280  {
281  fileNameList files;
282  if (UPstream::master(comm))
283  {
284  files = Foam::readDir
285  (
286  src,
288  false // Never trim '.gz' ending
289  //followLink
290  );
291  }
292  Pstream::broadcast(files, comm);
293 
294  for (const fileName& item : files)
295  {
296  // Softlink to softlink
298  (
299  comm,
300  writeOnProc,
301  src/item,
302  dst/item,
303  //followLink
304  buffer
305  );
306  }
307  }
308 
309 
310  // Copy sub directories
311  {
312  fileNameList dirs;
313  if (UPstream::master(comm))
314  {
315  dirs = Foam::readDir
316  (
317  src,
319  false // Never trim '.gz' ending
320  //followLink
321  );
322  }
323  Pstream::broadcast(dirs, comm);
324 
325  for (const fileName& item : dirs)
326  {
327  // Dir to Dir
329  (
330  comm,
331  writeOnProc,
332  src/item,
333  dst/item,
334  //followLink
335  buffer
336  );
337  }
338  }
339  }
340  else if (srcType == fileName::UNDEFINED)
341  {
342  WarningInFunction<< "No known file type: " << src << endl;
343  return false;
344  }
345  else
346  {
347  return false;
348  }
349 
350  return true;
351 }
353 } // End namespace Foam
354 
355 
356 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
357 
359 (
360  const label comm,
361  const bool writeOnProc,
362  const fileName& srcPath,
363  const fileName& dstPath
364  //const bool followLink
365 ) const
366 {
367  DynamicList<char> fileContents;
368 
369 
370  // FUTURE:
371  // Handling with broadcast of file -> directory etc
372  // as per Foam::cp()
373  //
374  // However, this adds extra communicattion and logic etc...
375 
376 #if 0
377  const fileName::Type srcType = src.type(followLink);
378 
379  // Check type of source file.
380  if (srcType == fileName::FILE)
381  {
382  // If dest is a directory, create the destination file name.
383  if (destFile.type() == fileName::DIRECTORY)
384  {
385  destFile = destFile/src.name();
386  }
387  }
388  else if (srcType == fileName::DIRECTORY)
389  {
390  if (destFile.type() == fileName::DIRECTORY)
391  {
392  // Both are directories. Could mean copy contents or copy
393  // recursively. Don't actually know what the user wants,
394  // but assume that if names are identical == copy contents.
395  //
396  // So: "path1/foo" "path2/foo" copy contents
397  // So: "path1/foo" "path2/bar" copy directory
398 
399  const word srcDirName = src.name();
400  if (destFile.name() != srcDirName)
401  {
402  destFile /= srcDirName;
403  }
404  }
405  }
406 #endif
407 
409  (
410  comm,
411  writeOnProc,
412  srcPath,
413  (dstPath.empty() ? srcPath : dstPath),
414  fileContents
415  );
416 }
417 
418 
419 // ************************************************************************* //
A symbolic link.
Definition: fileName.H:86
A class for handling file names.
Definition: fileName.H:72
off_t fileSize(const fileName &name, const bool followLink=true)
Return size of file or -1 on failure (normally follows symbolic links).
Definition: POSIX.C:905
fileName readLink(const fileName &link)
Return the contents (target) of a symlink.
Definition: POSIX.C:1290
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
A 2-tuple for storing two objects of dissimilar types. The container is similar in purpose to std::pa...
Definition: stringOps.H:54
static int maxCommsSize
Optional maximum message size (bytes)
Definition: UPstream.H:390
Undefined type.
Definition: fileName.H:83
static bool broadcastFile_recursive(const label comm, const bool writeOnProc, const fileName &src, const fileName &dst, DynamicList< char > &buffer)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
A traits class, which is primarily used for primitives and vector-space.
Definition: pTraits.H:75
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:169
char * data_bytes() noexcept
Return pointer to the underlying array serving as data storage,.
Definition: UListI.H:286
static void broadcast(Type &value, const label comm=UPstream::worldComm)
Broadcast content (contiguous or non-contiguous) to all communicator ranks. Does nothing in non-paral...
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:614
virtual bool broadcastCopy(const label comm, const bool writeOnProc, const fileName &src, const fileName &dst) const
Read dir/file (recursively if necessary) on master of the communicator, send and write contents to al...
A regular file.
Definition: fileName.H:84
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
bool ln(const fileName &src, const fileName &dst)
Create a softlink. dst should not exist. Returns true if successful.
Definition: POSIX.C:1237
int debug
Static debugging option.
static bool is_subrank(const label communicator=worldComm)
True if process corresponds to a sub-rank in the given communicator.
Definition: UPstream.H:1099
static void broadcastFile_single(const label comm, const bool writeOnProc, const fileName &srcName, const fileName &dstName, DynamicList< char > &buffer)
#define WarningInFunction
Report a warning using Foam::Warning.
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:627
static bool broadcast(char *buf, const std::streamsize bufSize, const label communicator, const int rootProcNo=masterNo())
Broadcast buffer contents to all processes in given communicator. The sizes must match on all process...
label comm() const noexcept
Communicator to use.
const T2 & second() const noexcept
Access the second element.
Definition: Tuple2.H:142
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
void resize_nocopy(const label len)
Alter addressable list size, allocating new space if required without necessarily recovering old cont...
Definition: DynamicListI.H:375
const T1 & first() const noexcept
Access the first element.
Definition: Tuple2.H:132
List< fileName > fileNameList
List of fileName.
Definition: fileNameList.H:32
fileNameList readDir(const fileName &directory, const fileName::Type type=fileName::Type::FILE, const bool filtergz=true, const bool followLink=true)
Read a directory and return the entries as a fileName List.
Definition: POSIX.C:963
mode_t mode(const fileName &name, const bool followLink=true)
Return the file mode, normally following symbolic links.
Definition: POSIX.C:773
std::streamsize size_bytes() const noexcept
Number of contiguous bytes for the List data.
Definition: UListI.H:293
Type
Enumerations to handle directory entry types.
Definition: fileName.H:81
Namespace for OpenFOAM.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...
#define InfoInFunction
Report an information message using Foam::Info.