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
24QT_BEGIN_NAMESPACE
25
26using namespace Qt::StringLiterals;
27
28static_assert(std::is_nothrow_move_constructible_v<QTemporaryDir>);
29static_assert(std::is_nothrow_move_assignable_v<QTemporaryDir>);
30
31//************* QTemporaryDirPrivate
32class QTemporaryDirPrivate
33{
34public:
35 QTemporaryDirPrivate();
36 ~QTemporaryDirPrivate();
37
38 void create(const QString &templateName);
39
40 QString pathOrError;
41 bool autoRemove;
42 bool success;
43};
44
45QTemporaryDirPrivate::QTemporaryDirPrivate()
46 : autoRemove(true),
47 success(false)
48{
49}
50
51QTemporaryDirPrivate::~QTemporaryDirPrivate()
52{
53}
54
55static 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
67void 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*/
141QTemporaryDir::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*/
160QTemporaryDir::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*/
209QTemporaryDir::~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*/
222bool 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*/
234QString 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*/
251QString 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*/
267QString 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*/
297bool 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*/
309void 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*/
319bool 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
335QT_END_NAMESPACE
336
337#endif // QT_CONFIG(temporaryfile)
338

source code of qtbase/src/corelib/io/qtemporarydir.cpp