1 | // Copyright (C) 2017 The Qt Company Ltd. |
2 | // Copyright (C) 2017 Intel Corporation. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #include "qtemporarydir.h" |
6 | |
7 | #if QT_CONFIG(temporaryfile) |
8 | |
9 | #include "qdebug.h" |
10 | #include "qplatformdefs.h" |
11 | #include "qrandom.h" |
12 | #include "private/qtemporaryfile_p.h" |
13 | |
14 | #if defined(QT_BUILD_CORE_LIB) |
15 | #include "qcoreapplication.h" |
16 | #endif |
17 | |
18 | #if !defined(Q_OS_WIN) |
19 | #include <errno.h> |
20 | #endif |
21 | |
22 | #include <type_traits> |
23 | |
24 | QT_BEGIN_NAMESPACE |
25 | |
26 | using namespace Qt::StringLiterals; |
27 | |
28 | static_assert(std::is_nothrow_move_constructible_v<QTemporaryDir>); |
29 | static_assert(std::is_nothrow_move_assignable_v<QTemporaryDir>); |
30 | |
31 | //************* QTemporaryDirPrivate |
32 | class QTemporaryDirPrivate |
33 | { |
34 | public: |
35 | QTemporaryDirPrivate(); |
36 | ~QTemporaryDirPrivate(); |
37 | |
38 | void create(const QString &templateName); |
39 | |
40 | QString pathOrError; |
41 | bool autoRemove; |
42 | bool success; |
43 | }; |
44 | |
45 | QTemporaryDirPrivate::QTemporaryDirPrivate() |
46 | : autoRemove(true), |
47 | success(false) |
48 | { |
49 | } |
50 | |
51 | QTemporaryDirPrivate::~QTemporaryDirPrivate() |
52 | { |
53 | } |
54 | |
55 | static QString defaultTemplateName() |
56 | { |
57 | QString baseName; |
58 | #if defined(QT_BUILD_CORE_LIB) |
59 | baseName = QCoreApplication::applicationName(); |
60 | if (baseName.isEmpty()) |
61 | #endif |
62 | baseName = "qt_temp"_L1 ; |
63 | |
64 | return QDir::tempPath() + u'/' + baseName + "-XXXXXX"_L1 ; |
65 | } |
66 | |
67 | void QTemporaryDirPrivate::create(const QString &templateName) |
68 | { |
69 | QTemporaryFileName tfn(templateName); |
70 | for (int i = 0; i < 256; ++i) { |
71 | tfn.generateNext(); |
72 | QFileSystemEntry fileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath()); |
73 | if (QFileSystemEngine::createDirectory(entry: fileSystemEntry, createParents: false, |
74 | permissions: QFile::ReadOwner | QFile::WriteOwner |
75 | | QFile::ExeOwner)) { |
76 | success = true; |
77 | pathOrError = fileSystemEntry.filePath(); |
78 | return; |
79 | } |
80 | # ifdef Q_OS_WIN |
81 | const int exists = ERROR_ALREADY_EXISTS; |
82 | int code = GetLastError(); |
83 | # else |
84 | const int exists = EEXIST; |
85 | int code = errno; |
86 | # endif |
87 | if (code != exists) |
88 | break; |
89 | } |
90 | pathOrError = qt_error_string(); |
91 | success = false; |
92 | } |
93 | |
94 | //************* QTemporaryDir |
95 | |
96 | /*! |
97 | \class QTemporaryDir |
98 | \inmodule QtCore |
99 | \reentrant |
100 | \brief The QTemporaryDir class creates a unique directory for temporary use. |
101 | |
102 | \ingroup io |
103 | |
104 | |
105 | QTemporaryDir is used to create unique temporary directories safely. |
106 | The directory itself is created by the constructor. The name of the |
107 | temporary directory is guaranteed to be unique (i.e., you are |
108 | guaranteed to not overwrite an existing directory), and the directory will |
109 | subsequently be removed upon destruction of the QTemporaryDir |
110 | object. The directory name is either auto-generated, or created based |
111 | on a template, which is passed to QTemporaryDir's constructor. |
112 | |
113 | Example: |
114 | |
115 | \snippet code/src_corelib_io_qtemporarydir.cpp 0 |
116 | |
117 | It is very important to test that the temporary directory could be |
118 | created, using isValid(). Do not use \l {QDir::exists()}{exists()}, since a default-constructed |
119 | QDir represents the current directory, which exists. |
120 | |
121 | The path to the temporary directory can be found by calling path(). |
122 | |
123 | A temporary directory will have some static part of the name and some |
124 | part that is calculated to be unique. The default path will be |
125 | determined from QCoreApplication::applicationName() (otherwise \c qt_temp) and will |
126 | be placed into the temporary path as returned by QDir::tempPath(). |
127 | If you specify your own path, a relative path will not be placed in the |
128 | temporary directory by default, but be relative to the current working directory. |
129 | In all cases, a random string will be appended to the path in order to make it unique. |
130 | |
131 | \sa QDir::tempPath(), QDir, QTemporaryFile |
132 | */ |
133 | |
134 | /*! |
135 | Constructs a QTemporaryDir using as template the application name |
136 | returned by QCoreApplication::applicationName() (otherwise \c qt_temp). |
137 | The directory is stored in the system's temporary directory, QDir::tempPath(). |
138 | |
139 | \sa QDir::tempPath() |
140 | */ |
141 | QTemporaryDir::QTemporaryDir() |
142 | : d_ptr(new QTemporaryDirPrivate) |
143 | { |
144 | d_ptr->create(templateName: defaultTemplateName()); |
145 | } |
146 | |
147 | /*! |
148 | Constructs a QTemporaryDir with a template of \a templatePath. |
149 | |
150 | If \a templatePath is a relative path, the path will be relative to the |
151 | current working directory. You can use QDir::tempPath() to construct \a |
152 | templatePath if you want use the system's temporary directory. |
153 | |
154 | If the \a templatePath ends with XXXXXX it will be used as the dynamic portion |
155 | of the directory name, otherwise it will be appended. |
156 | Unlike QTemporaryFile, XXXXXX in the middle of the template string is not supported. |
157 | |
158 | \sa QDir::tempPath() |
159 | */ |
160 | QTemporaryDir::QTemporaryDir(const QString &templatePath) |
161 | : d_ptr(new QTemporaryDirPrivate) |
162 | { |
163 | if (templatePath.isEmpty()) |
164 | d_ptr->create(templateName: defaultTemplateName()); |
165 | else |
166 | d_ptr->create(templateName: templatePath); |
167 | } |
168 | |
169 | /*! |
170 | \fn QTemporaryDir::QTemporaryDir(QTemporaryDir &&other) |
171 | |
172 | Move-constructs a new QTemporaryDir from \a other. |
173 | |
174 | \note The moved-from object \a other is placed in a |
175 | partially-formed state, in which the only valid operations are |
176 | destruction and assignment of a new value. |
177 | |
178 | \since 6.4 |
179 | */ |
180 | |
181 | /*! |
182 | \fn QTemporaryDir &QTemporaryDir::operator=(QTemporaryDir&& other) |
183 | |
184 | Move-assigns \a other to this QTemporaryDir instance. |
185 | |
186 | \note The moved-from object \a other is placed in a |
187 | partially-formed state, in which the only valid operations are |
188 | destruction and assignment of a new value. |
189 | |
190 | \since 6.4 |
191 | */ |
192 | |
193 | /*! |
194 | \fn void QTemporaryDir::swap(QTemporaryDir &other) |
195 | |
196 | Swaps temporary-dir \a other with this temporary-dir. This operation is |
197 | very fast and never fails. |
198 | |
199 | \since 6.4 |
200 | */ |
201 | |
202 | /*! |
203 | Destroys the temporary directory object. |
204 | If auto remove mode was set, it will automatically delete the directory |
205 | including all its contents. |
206 | |
207 | \sa autoRemove() |
208 | */ |
209 | QTemporaryDir::~QTemporaryDir() |
210 | { |
211 | if (d_ptr) { |
212 | if (d_ptr->autoRemove) |
213 | remove(); |
214 | |
215 | delete d_ptr; |
216 | } |
217 | } |
218 | |
219 | /*! |
220 | Returns \c true if the QTemporaryDir was created successfully. |
221 | */ |
222 | bool QTemporaryDir::isValid() const |
223 | { |
224 | return d_ptr->success; |
225 | } |
226 | |
227 | /*! |
228 | \since 5.6 |
229 | |
230 | If isValid() returns \c false, this function returns the error string that |
231 | explains why the creation of the temporary directory failed. Otherwise, this |
232 | function return an empty string. |
233 | */ |
234 | QString QTemporaryDir::errorString() const |
235 | { |
236 | return d_ptr->success ? QString() : d_ptr->pathOrError; |
237 | } |
238 | |
239 | /*! |
240 | Returns the path to the temporary directory. |
241 | Empty if the QTemporaryDir could not be created. |
242 | |
243 | //! [relative-or-absolute-path] |
244 | The returned path will be relative or absolulte depending on whether |
245 | QTemporaryDir was constructed with a relative or absolute path, |
246 | respectively. |
247 | //! [relative-or-absolute-path] |
248 | |
249 | |
250 | */ |
251 | QString QTemporaryDir::path() const |
252 | { |
253 | return d_ptr->success ? d_ptr->pathOrError : QString(); |
254 | } |
255 | |
256 | /*! |
257 | \since 5.9 |
258 | |
259 | Returns the path name of a file in the temporary directory. |
260 | Does \e not check if the file actually exists in the directory. |
261 | Redundant multiple separators or "." and ".." directories in |
262 | \a fileName are not removed (see QDir::cleanPath()). Absolute |
263 | paths are not allowed. |
264 | |
265 | \include qtemporarydir.cpp relative-or-absolute-path |
266 | */ |
267 | QString QTemporaryDir::filePath(const QString &fileName) const |
268 | { |
269 | if (QDir::isAbsolutePath(path: fileName)) { |
270 | qWarning(msg: "QTemporaryDir::filePath: Absolute paths are not allowed: %s" , qUtf8Printable(fileName)); |
271 | return QString(); |
272 | } |
273 | |
274 | if (!d_ptr->success) |
275 | return QString(); |
276 | |
277 | QString ret = d_ptr->pathOrError; |
278 | if (!fileName.isEmpty()) { |
279 | ret += u'/'; |
280 | ret += fileName; |
281 | } |
282 | return ret; |
283 | } |
284 | |
285 | /*! |
286 | Returns \c true if the QTemporaryDir is in auto remove |
287 | mode. Auto-remove mode will automatically delete the directory from |
288 | disk upon destruction. This makes it very easy to create your |
289 | QTemporaryDir object on the stack, fill it with files, do something with |
290 | the files, and finally on function return it will automatically clean up |
291 | after itself. |
292 | |
293 | Auto-remove is on by default. |
294 | |
295 | \sa setAutoRemove(), remove() |
296 | */ |
297 | bool QTemporaryDir::autoRemove() const |
298 | { |
299 | return d_ptr->autoRemove; |
300 | } |
301 | |
302 | /*! |
303 | Sets the QTemporaryDir into auto-remove mode if \a b is true. |
304 | |
305 | Auto-remove is on by default. |
306 | |
307 | \sa autoRemove(), remove() |
308 | */ |
309 | void QTemporaryDir::setAutoRemove(bool b) |
310 | { |
311 | d_ptr->autoRemove = b; |
312 | } |
313 | |
314 | /*! |
315 | Removes the temporary directory, including all its contents. |
316 | |
317 | Returns \c true if removing was successful. |
318 | */ |
319 | bool QTemporaryDir::remove() |
320 | { |
321 | if (!d_ptr->success) |
322 | return false; |
323 | Q_ASSERT(!path().isEmpty()); |
324 | Q_ASSERT(path() != "."_L1 ); |
325 | |
326 | const bool result = QDir(path()).removeRecursively(); |
327 | if (!result) { |
328 | qWarning() << "QTemporaryDir: Unable to remove" |
329 | << QDir::toNativeSeparators(pathName: path()) |
330 | << "most likely due to the presence of read-only files." ; |
331 | } |
332 | return result; |
333 | } |
334 | |
335 | QT_END_NAMESPACE |
336 | |
337 | #endif // QT_CONFIG(temporaryfile) |
338 | |