xref: /aosp_15_r20/external/boringssl/src/crypto/test/file_util.h (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
1 /* Copyright (c) 2023, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #ifndef OPENSSL_HEADER_CRYPTO_TEST_FILE_UTIL_H
16 #define OPENSSL_HEADER_CRYPTO_TEST_FILE_UTIL_H
17 
18 #include <stdio.h>
19 
20 #include <memory>
21 #include <set>
22 #include <string>
23 #include <utility>
24 
25 #include <openssl/span.h>
26 
27 #if defined(OPENSSL_WINDOWS)
28 #include <io.h>
29 #else
30 #include <unistd.h>
31 #endif
32 
33 
34 BSSL_NAMESPACE_BEGIN
35 
36 struct FileDeleter {
operatorFileDeleter37   void operator()(FILE *f) const {
38     if (f != nullptr) {
39       fclose(f);
40     }
41   }
42 };
43 
44 using ScopedFILE = std::unique_ptr<FILE, FileDeleter>;
45 
46 class ScopedFD {
47  public:
48   ScopedFD() = default;
ScopedFD(int fd)49   explicit ScopedFD(int fd) : fd_(fd) {}
~ScopedFD()50   ~ScopedFD() { reset(); }
51 
ScopedFD(ScopedFD && other)52   ScopedFD(ScopedFD &&other) { *this = std::move(other); }
53   ScopedFD &operator=(ScopedFD other) {
54     reset(other.release());
55     return *this;
56   }
57 
is_valid()58   bool is_valid() const { return fd_ >= 0; }
get()59   int get() const { return fd_; }
60 
release()61   int release() { return std::exchange(fd_, -1); }
62   void reset(int fd = -1) {
63     if (is_valid()) {
64 #if defined(OPENSSL_WINDOWS)
65       _close(fd_);
66 #else
67       close(fd_);
68 #endif
69     }
70     fd_ = fd;
71   }
72 
73  private:
74   int fd_ = -1;
75 };
76 
77 // SkipTempFileTests returns true and prints a warning if tests involving
78 // temporary files should be skipped because of platform issues.
79 bool SkipTempFileTests();
80 
81 // TemporaryFile manages a temporary file for testing.
82 class TemporaryFile {
83  public:
84   TemporaryFile() = default;
85   ~TemporaryFile();
86 
TemporaryFile(TemporaryFile & other)87   TemporaryFile(TemporaryFile &other) { *this = std::move(other); }
88   TemporaryFile& operator=(TemporaryFile&&other) {
89     // Ensure |path_| is empty so it doesn't try to delete the File.
90     path_ = std::exchange(other.path_, {});
91     return *this;
92   }
93 
94   // Init initializes the temporary file with the specified content. It returns
95   // true on success and false on error. On error, callers should call
96   // |IgnoreTempFileErrors| to determine whether to ignore the error.
97   bool Init(bssl::Span<const uint8_t> content = {});
Init(const std::string & content)98   bool Init(const std::string &content) {
99     return Init(bssl::MakeConstSpan(
100         reinterpret_cast<const uint8_t *>(content.data()), content.size()));
101   }
102 
103   // Open opens the file as a |FILE| with the specified mode.
104   ScopedFILE Open(const char *mode) const;
105 
106   // Open opens the file as a file descriptor with the specified flags.
107   ScopedFD OpenFD(int flags) const;
108 
109   // path returns the path to the temporary file.
path()110   const std::string &path() const { return path_; }
111 
112  private:
113   std::string path_;
114 };
115 
116 // TemporaryDirectory manages a temporary directory for testing.
117 class TemporaryDirectory {
118  public:
119   TemporaryDirectory() = default;
120   ~TemporaryDirectory();
121 
TemporaryDirectory(TemporaryDirectory & other)122   TemporaryDirectory(TemporaryDirectory &other) { *this = std::move(other); }
123   TemporaryDirectory& operator=(TemporaryDirectory&&other) {
124     // Ensure |other_| is empty so it doesn't try to delete the directory.
125     path_ = std::exchange(other.path_, {});
126     files_ = std::exchange(other.files_, {});
127     return *this;
128   }
129 
130   // Init initializes the temporary directory. It returns true on success and
131   // false on error. On error, callers should call |IgnoreTempFileErrors| to
132   // determine whether to ignore the error.
133   bool Init();
134 
135   // path returns the path to the temporary directory.
path()136   const std::string &path() const { return path_; }
137 
138   // AddFile adds a file to the temporary directory with the specified content.
139   // It returns true on success and false on error. Subdirectories in the
140   // temporary directory are not currently supported.
141   bool AddFile(const std::string &filename, bssl::Span<const uint8_t> content);
AddFile(const std::string & filename,const std::string & content)142   bool AddFile(const std::string &filename, const std::string &content) {
143     return AddFile(
144         filename,
145         bssl::MakeConstSpan(reinterpret_cast<const uint8_t *>(content.data()),
146                             content.size()));
147   }
148 
149   // GetFilePath returns the path to the speciifed file within the temporary
150   // directory.
GetFilePath(const std::string & filename)151   std::string GetFilePath(const std::string &filename) {
152 #if defined(OPENSSL_WINDOWS)
153     return path_ + '\\' + filename;
154 #else
155     return path_ + '/' + filename;
156 #endif
157   }
158 
159  private:
160   std::string path_;
161   std::set<std::string> files_;
162 };
163 
164 BSSL_NAMESPACE_END
165 
166 #endif  // OPENSSL_HEADER_CRYPTO_TEST_FILE_UTIL_H
167