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

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