1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Copyright (C) 2017 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qtemporarydir.h"
42
43#ifndef QT_NO_TEMPORARYFILE
44
45#include "qdebug.h"
46#include "qdiriterator.h"
47#include "qpair.h"
48#include "qplatformdefs.h"
49#include "qrandom.h"
50#include "private/qtemporaryfile_p.h"
51
52#if defined(QT_BUILD_CORE_LIB)
53#include "qcoreapplication.h"
54#endif
55
56#if !defined(Q_OS_WIN)
57#include <errno.h>
58#endif
59
60QT_BEGIN_NAMESPACE
61
62//************* QTemporaryDirPrivate
63class QTemporaryDirPrivate
64{
65public:
66 QTemporaryDirPrivate();
67 ~QTemporaryDirPrivate();
68
69 void create(const QString &templateName);
70
71 QString pathOrError;
72 bool autoRemove;
73 bool success;
74};
75
76QTemporaryDirPrivate::QTemporaryDirPrivate()
77 : autoRemove(true),
78 success(false)
79{
80}
81
82QTemporaryDirPrivate::~QTemporaryDirPrivate()
83{
84}
85
86static QString defaultTemplateName()
87{
88 QString baseName;
89#if defined(QT_BUILD_CORE_LIB)
90 baseName = QCoreApplication::applicationName();
91 if (baseName.isEmpty())
92#endif
93 baseName = QLatin1String("qt_temp");
94
95 return QDir::tempPath() + QLatin1Char('/') + baseName + QLatin1String("-XXXXXX");
96}
97
98void QTemporaryDirPrivate::create(const QString &templateName)
99{
100 QTemporaryFileName tfn(templateName);
101 for (int i = 0; i < 256; ++i) {
102 tfn.generateNext();
103 QFileSystemEntry fileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
104 if (QFileSystemEngine::createDirectory(fileSystemEntry, false)) {
105 QSystemError error;
106 QFileSystemEngine::setPermissions(fileSystemEntry,
107 QFile::ReadOwner |
108 QFile::WriteOwner |
109 QFile::ExeOwner, error);
110 if (error.error() != 0) {
111 if (!QFileSystemEngine::removeDirectory(fileSystemEntry, false))
112 qWarning() << "Unable to remove unused directory" << templateName;
113 continue;
114 }
115 success = true;
116 pathOrError = fileSystemEntry.filePath();
117 return;
118 }
119# ifdef Q_OS_WIN
120 const int exists = ERROR_ALREADY_EXISTS;
121 int code = GetLastError();
122# else
123 const int exists = EEXIST;
124 int code = errno;
125# endif
126 if (code != exists)
127 break;
128 }
129 pathOrError = qt_error_string();
130 success = false;
131}
132
133//************* QTemporaryDir
134
135/*!
136 \class QTemporaryDir
137 \inmodule QtCore
138 \reentrant
139 \brief The QTemporaryDir class creates a unique directory for temporary use.
140
141 \ingroup io
142
143
144 QTemporaryDir is used to create unique temporary directories safely.
145 The directory itself is created by the constructor. The name of the
146 temporary directory is guaranteed to be unique (i.e., you are
147 guaranteed to not overwrite an existing directory), and the directory will
148 subsequently be removed upon destruction of the QTemporaryDir
149 object. The directory name is either auto-generated, or created based
150 on a template, which is passed to QTemporaryDir's constructor.
151
152 Example:
153
154 \snippet code/src_corelib_io_qtemporarydir.cpp 0
155
156 It is very important to test that the temporary directory could be
157 created, using isValid(). Do not use \l {QDir::exists()}{exists()}, since a default-constructed
158 QDir represents the current directory, which exists.
159
160 The path to the temporary directory can be found by calling path().
161
162 A temporary directory will have some static part of the name and some
163 part that is calculated to be unique. The default path will be
164 determined from QCoreApplication::applicationName() (otherwise \c qt_temp) and will
165 be placed into the temporary path as returned by QDir::tempPath().
166 If you specify your own path, a relative path will not be placed in the
167 temporary directory by default, but be relative to the current working directory.
168 In all cases, a random string will be appended to the path in order to make it unique.
169
170 \sa QDir::tempPath(), QDir, QTemporaryFile
171*/
172
173/*!
174 Constructs a QTemporaryDir using as template the application name
175 returned by QCoreApplication::applicationName() (otherwise \c qt_temp).
176 The directory is stored in the system's temporary directory, QDir::tempPath().
177
178 \sa QDir::tempPath()
179*/
180QTemporaryDir::QTemporaryDir()
181 : d_ptr(new QTemporaryDirPrivate)
182{
183 d_ptr->create(defaultTemplateName());
184}
185
186/*!
187 Constructs a QTemporaryDir with a template of \a templatePath.
188
189 If \a templatePath is a relative path, the path will be relative to the
190 current working directory. You can use QDir::tempPath() to construct \a
191 templatePath if you want use the system's temporary directory.
192
193 If the \a templatePath ends with XXXXXX it will be used as the dynamic portion
194 of the directory name, otherwise it will be appended.
195 Unlike QTemporaryFile, XXXXXX in the middle of the template string is not supported.
196
197 \sa QDir::tempPath()
198*/
199QTemporaryDir::QTemporaryDir(const QString &templatePath)
200 : d_ptr(new QTemporaryDirPrivate)
201{
202 if (templatePath.isEmpty())
203 d_ptr->create(defaultTemplateName());
204 else
205 d_ptr->create(templatePath);
206}
207
208/*!
209 Destroys the temporary directory object.
210 If auto remove mode was set, it will automatically delete the directory
211 including all its contents.
212
213 \sa autoRemove()
214*/
215QTemporaryDir::~QTemporaryDir()
216{
217 if (d_ptr->autoRemove)
218 remove();
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(fileName)) {
262 qWarning("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 += QLatin1Char('/');
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() != QLatin1String("."));
317
318 const bool result = QDir(path()).removeRecursively();
319 if (!result) {
320 qWarning() << "QTemporaryDir: Unable to remove"
321 << QDir::toNativeSeparators(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