1 //
2 // Copyright 2017 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_TF_SAFE_OUTPUT_FILE_H
25 #define PXR_BASE_TF_SAFE_OUTPUT_FILE_H
26 
27 /// \file tf/safeOutputFile.h
28 /// Safe file writer with FILE * interface.
29 
30 #include "pxr/pxr.h"
31 #include "pxr/base/tf/api.h"
32 
33 #include <cstdio>
34 #include <memory>
35 #include <string>
36 
37 PXR_NAMESPACE_OPEN_SCOPE
38 
39 /// \class TfSafeOutputFile
40 ///
41 /// Opens a file for output, either for update "r+" or to completely replace
42 /// "w+".  In the case of complete replacement, create a sibling temporary file
43 /// to write to instead.  When writing is complete, rename the temporary file
44 /// over the target file.  This provides some safety to other processes reading
45 /// the existing file (at least on unix-like OSs).  They will continue to see
46 /// the existing contents of the old file.  If we overwrote the file itself,
47 /// then those other processes would see undefined, possibly partially updated
48 /// content.
49 class TfSafeOutputFile
50 {
51     TfSafeOutputFile(TfSafeOutputFile const &) = delete;
52     TfSafeOutputFile &operator=(TfSafeOutputFile const &) = delete;
53 public:
54     TfSafeOutputFile() = default;
55 
TfSafeOutputFile(TfSafeOutputFile && other)56     TfSafeOutputFile(TfSafeOutputFile &&other)
57         : _file(other._file)
58         , _targetFileName(std::move(other._targetFileName))
59         , _tempFileName(std::move(other._tempFileName))
60         { other._file = nullptr; }
61 
62     TfSafeOutputFile &operator=(TfSafeOutputFile &&other) {
63         _file = other._file;
64         _targetFileName = std::move(other._targetFileName);
65         _tempFileName = std::move(other._tempFileName);
66         other._file = nullptr;
67         return *this;
68     }
69 
70     /// Destructor invokes Close().
71     TF_API ~TfSafeOutputFile();
72 
73     /// Open \p fileName for update ("r+").
74     TF_API static TfSafeOutputFile Update(std::string const &fileName);
75 
76     /// Arrange for \p fileName to be replaced.  Create a sibling temporary file
77     /// and open that for writing.  When Close() is called (or the destructor is
78     /// run) close the temporary file and rename it over \p fileName.
79     TF_API static TfSafeOutputFile Replace(std::string const &fileName);
80 
81     /// Close the file.  If the file was opened with Replace(), rename the
82     /// temporary file over the target file to replace it.
83     TF_API void Close();
84 
85     /// Close the file.  If the file was opened with Replace(), the temporary
86     /// file is removed and not renamed over the target file.  It is an error
87     /// to call this for files opened for Update.
88     TF_API void Discard();
89 
90     /// Return the opened FILE *.
Get()91     FILE *Get() const { return _file; }
92 
93     /// If the underlying file was opened by Update(), return it.  The caller
94     /// takes responsibility for closing the file later.  It is an error to call
95     /// this for files opened for Replace.
96     TF_API FILE *ReleaseUpdatedFile();
97 
98     /// Return true if this TfSafeOutputFile was created by a call to Update(),
99     /// false otherwise.
100     TF_API bool IsOpenForUpdate() const;
101 
102 private:
103     FILE *_file = nullptr;
104     std::string _targetFileName;
105     std::string _tempFileName;
106 };
107 
108 PXR_NAMESPACE_CLOSE_SCOPE
109 
110 #endif // PXR_BASE_TF_SAFE_OUTPUT_FILE_H
111