1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2020 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 "qplatformdefs.h" |
42 | #include "qdebug.h" |
43 | #include "qfile.h" |
44 | #include "qfsfileengine_p.h" |
45 | #include "qtemporaryfile.h" |
46 | #include "qtemporaryfile_p.h" |
47 | #include "qlist.h" |
48 | #include "qfileinfo.h" |
49 | #include "private/qiodevice_p.h" |
50 | #include "private/qfile_p.h" |
51 | #include "private/qfilesystemengine_p.h" |
52 | #include "private/qsystemerror_p.h" |
53 | #include "private/qtemporaryfile_p.h" |
54 | #if defined(QT_BUILD_CORE_LIB) |
55 | # include "qcoreapplication.h" |
56 | #endif |
57 | |
58 | #include <private/qmemory_p.h> |
59 | |
60 | #ifdef QT_NO_QOBJECT |
61 | #define tr(X) QString::fromLatin1(X) |
62 | #endif |
63 | |
64 | QT_BEGIN_NAMESPACE |
65 | |
66 | Q_DECL_COLD_FUNCTION |
67 | static bool file_already_open(QFile &file, const char *where = nullptr) |
68 | { |
69 | qWarning("QFile::%s: File (%ls) already open" , where ? where : "open" , qUtf16Printable(file.fileName())); |
70 | return false; |
71 | } |
72 | |
73 | //************* QFilePrivate |
74 | QFilePrivate::QFilePrivate() |
75 | { |
76 | } |
77 | |
78 | QFilePrivate::~QFilePrivate() |
79 | { |
80 | } |
81 | |
82 | bool |
83 | QFilePrivate::openExternalFile(int flags, int fd, QFile::FileHandleFlags handleFlags) |
84 | { |
85 | #ifdef QT_NO_FSFILEENGINE |
86 | Q_UNUSED(flags); |
87 | Q_UNUSED(fd); |
88 | return false; |
89 | #else |
90 | auto fs = qt_make_unique<QFSFileEngine>(); |
91 | auto fe = fs.get(); |
92 | fileEngine = std::move(fs); |
93 | return fe->open(QIODevice::OpenMode(flags), fd, handleFlags); |
94 | #endif |
95 | } |
96 | |
97 | bool |
98 | QFilePrivate::openExternalFile(int flags, FILE *fh, QFile::FileHandleFlags handleFlags) |
99 | { |
100 | #ifdef QT_NO_FSFILEENGINE |
101 | Q_UNUSED(flags); |
102 | Q_UNUSED(fh); |
103 | return false; |
104 | #else |
105 | auto fs = qt_make_unique<QFSFileEngine>(); |
106 | auto fe = fs.get(); |
107 | fileEngine = std::move(fs); |
108 | return fe->open(QIODevice::OpenMode(flags), fh, handleFlags); |
109 | #endif |
110 | } |
111 | |
112 | QAbstractFileEngine *QFilePrivate::engine() const |
113 | { |
114 | if (!fileEngine) |
115 | fileEngine.reset(QAbstractFileEngine::create(fileName)); |
116 | return fileEngine.get(); |
117 | } |
118 | |
119 | //************* QFile |
120 | |
121 | /*! |
122 | \class QFile |
123 | \inmodule QtCore |
124 | \brief The QFile class provides an interface for reading from and writing to files. |
125 | |
126 | \ingroup io |
127 | |
128 | \reentrant |
129 | |
130 | QFile is an I/O device for reading and writing text and binary |
131 | files and \l{The Qt Resource System}{resources}. A QFile may be |
132 | used by itself or, more conveniently, with a QTextStream or |
133 | QDataStream. |
134 | |
135 | The file name is usually passed in the constructor, but it can be |
136 | set at any time using setFileName(). QFile expects the file |
137 | separator to be '/' regardless of operating system. The use of |
138 | other separators (e.g., '\\') is not supported. |
139 | |
140 | You can check for a file's existence using exists(), and remove a |
141 | file using remove(). (More advanced file system related operations |
142 | are provided by QFileInfo and QDir.) |
143 | |
144 | The file is opened with open(), closed with close(), and flushed |
145 | with flush(). Data is usually read and written using QDataStream |
146 | or QTextStream, but you can also call the QIODevice-inherited |
147 | functions read(), readLine(), readAll(), write(). QFile also |
148 | inherits getChar(), putChar(), and ungetChar(), which work one |
149 | character at a time. |
150 | |
151 | The size of the file is returned by size(). You can get the |
152 | current file position using pos(), or move to a new file position |
153 | using seek(). If you've reached the end of the file, atEnd() |
154 | returns \c true. |
155 | |
156 | \section1 Reading Files Directly |
157 | |
158 | The following example reads a text file line by line: |
159 | |
160 | \snippet file/file.cpp 0 |
161 | |
162 | The QIODevice::Text flag passed to open() tells Qt to convert |
163 | Windows-style line terminators ("\\r\\n") into C++-style |
164 | terminators ("\\n"). By default, QFile assumes binary, i.e. it |
165 | doesn't perform any conversion on the bytes stored in the file. |
166 | |
167 | \section1 Using Streams to Read Files |
168 | |
169 | The next example uses QTextStream to read a text file |
170 | line by line: |
171 | |
172 | \snippet file/file.cpp 1 |
173 | |
174 | QTextStream takes care of converting the 8-bit data stored on |
175 | disk into a 16-bit Unicode QString. By default, it assumes that |
176 | the file is encoded in UTF-8. This can be changed using |
177 | \l QTextStream::setEncoding(). |
178 | |
179 | To write text, we can use operator<<(), which is overloaded to |
180 | take a QTextStream on the left and various data types (including |
181 | QString) on the right: |
182 | |
183 | \snippet file/file.cpp 2 |
184 | |
185 | QDataStream is similar, in that you can use operator<<() to write |
186 | data and operator>>() to read it back. See the class |
187 | documentation for details. |
188 | |
189 | When you use QFile, QFileInfo, and QDir to access the file system |
190 | with Qt, you can use Unicode file names. On Unix, these file |
191 | names are converted to an 8-bit encoding. If you want to use |
192 | standard C++ APIs (\c <cstdio> or \c <iostream>) or |
193 | platform-specific APIs to access files instead of QFile, you can |
194 | use the encodeName() and decodeName() functions to convert |
195 | between Unicode file names and 8-bit file names. |
196 | |
197 | On Unix, there are some special system files (e.g. in \c /proc) for which |
198 | size() will always return 0, yet you may still be able to read more data |
199 | from such a file; the data is generated in direct response to you calling |
200 | read(). In this case, however, you cannot use atEnd() to determine if |
201 | there is more data to read (since atEnd() will return true for a file that |
202 | claims to have size 0). Instead, you should either call readAll(), or call |
203 | read() or readLine() repeatedly until no more data can be read. The next |
204 | example uses QTextStream to read \c /proc/modules line by line: |
205 | |
206 | \snippet file/file.cpp 3 |
207 | |
208 | \section1 Signals |
209 | |
210 | Unlike other QIODevice implementations, such as QTcpSocket, QFile does not |
211 | emit the aboutToClose(), bytesWritten(), or readyRead() signals. This |
212 | implementation detail means that QFile is not suitable for reading and |
213 | writing certain types of files, such as device files on Unix platforms. |
214 | |
215 | \section1 Platform Specific Issues |
216 | |
217 | File permissions are handled differently on Unix-like systems and |
218 | Windows. In a non \l{QIODevice::isWritable()}{writable} |
219 | directory on Unix-like systems, files cannot be created. This is not always |
220 | the case on Windows, where, for instance, the 'My Documents' |
221 | directory usually is not writable, but it is still possible to |
222 | create files in it. |
223 | |
224 | Qt's understanding of file permissions is limited, which affects especially |
225 | the \l QFile::setPermissions() function. On Windows, Qt will set only the |
226 | legacy read-only flag, and that only when none of the Write* flags are |
227 | passed. Qt does not manipulate access control lists (ACLs), which makes this |
228 | function mostly useless for NTFS volumes. It may still be of use for USB |
229 | sticks that use VFAT file systems. POSIX ACLs are not manipulated, either. |
230 | |
231 | \sa QTextStream, QDataStream, QFileInfo, QDir, {The Qt Resource System} |
232 | */ |
233 | |
234 | #ifdef QT_NO_QOBJECT |
235 | QFile::QFile() |
236 | : QFileDevice(*new QFilePrivate) |
237 | { |
238 | } |
239 | QFile::QFile(const QString &name) |
240 | : QFileDevice(*new QFilePrivate) |
241 | { |
242 | d_func()->fileName = name; |
243 | } |
244 | QFile::QFile(QFilePrivate &dd) |
245 | : QFileDevice(dd) |
246 | { |
247 | } |
248 | #else |
249 | /*! |
250 | Constructs a QFile object. |
251 | */ |
252 | QFile::QFile() |
253 | : QFileDevice(*new QFilePrivate, nullptr) |
254 | { |
255 | } |
256 | /*! |
257 | Constructs a new file object with the given \a parent. |
258 | */ |
259 | QFile::QFile(QObject *parent) |
260 | : QFileDevice(*new QFilePrivate, parent) |
261 | { |
262 | } |
263 | /*! |
264 | Constructs a new file object to represent the file with the given \a name. |
265 | */ |
266 | QFile::QFile(const QString &name) |
267 | : QFileDevice(*new QFilePrivate, nullptr) |
268 | { |
269 | Q_D(QFile); |
270 | d->fileName = name; |
271 | } |
272 | /*! |
273 | Constructs a new file object with the given \a parent to represent the |
274 | file with the specified \a name. |
275 | */ |
276 | QFile::QFile(const QString &name, QObject *parent) |
277 | : QFileDevice(*new QFilePrivate, parent) |
278 | { |
279 | Q_D(QFile); |
280 | d->fileName = name; |
281 | } |
282 | /*! |
283 | \internal |
284 | */ |
285 | QFile::QFile(QFilePrivate &dd, QObject *parent) |
286 | : QFileDevice(dd, parent) |
287 | { |
288 | } |
289 | #endif |
290 | |
291 | /*! |
292 | Destroys the file object, closing it if necessary. |
293 | */ |
294 | QFile::~QFile() |
295 | { |
296 | } |
297 | |
298 | /*! |
299 | Returns the name set by setFileName() or to the QFile |
300 | constructors. |
301 | |
302 | \sa setFileName(), QFileInfo::fileName() |
303 | */ |
304 | QString QFile::fileName() const |
305 | { |
306 | Q_D(const QFile); |
307 | return d->engine()->fileName(QAbstractFileEngine::DefaultName); |
308 | } |
309 | |
310 | /*! |
311 | Sets the \a name of the file. The name can have no path, a |
312 | relative path, or an absolute path. |
313 | |
314 | Do not call this function if the file has already been opened. |
315 | |
316 | If the file name has no path or a relative path, the path used |
317 | will be the application's current directory path |
318 | \e{at the time of the open()} call. |
319 | |
320 | Example: |
321 | \snippet code/src_corelib_io_qfile.cpp 0 |
322 | |
323 | Note that the directory separator "/" works for all operating |
324 | systems supported by Qt. |
325 | |
326 | \sa fileName(), QFileInfo, QDir |
327 | */ |
328 | void |
329 | QFile::setFileName(const QString &name) |
330 | { |
331 | Q_D(QFile); |
332 | if (isOpen()) { |
333 | file_already_open(*this, "setFileName" ); |
334 | close(); |
335 | } |
336 | d->fileEngine.reset(); //get a new file engine later |
337 | d->fileName = name; |
338 | } |
339 | |
340 | /*! |
341 | \fn QString QFile::decodeName(const char *localFileName) |
342 | |
343 | \overload |
344 | |
345 | Returns the Unicode version of the given \a localFileName. See |
346 | encodeName() for details. |
347 | */ |
348 | |
349 | /*! |
350 | \fn QByteArray QFile::encodeName(const QString &fileName) |
351 | |
352 | Converts \a fileName to the local 8-bit |
353 | encoding determined by the user's locale. This is sufficient for |
354 | file names that the user chooses. File names hard-coded into the |
355 | application should only use 7-bit ASCII filename characters. |
356 | |
357 | \sa decodeName() |
358 | */ |
359 | |
360 | /*! |
361 | \fn QString QFile::decodeName(const QByteArray &localFileName) |
362 | |
363 | This does the reverse of QFile::encodeName() using \a localFileName. |
364 | |
365 | \sa encodeName() |
366 | */ |
367 | |
368 | /*! |
369 | \overload |
370 | |
371 | Returns \c true if the file specified by fileName() exists; otherwise |
372 | returns \c false. |
373 | |
374 | \sa fileName(), setFileName() |
375 | */ |
376 | |
377 | bool |
378 | QFile::exists() const |
379 | { |
380 | Q_D(const QFile); |
381 | // 0x1000000 = QAbstractFileEngine::Refresh, forcing an update |
382 | return (d->engine()->fileFlags(QAbstractFileEngine::FlagsMask |
383 | | QAbstractFileEngine::Refresh) & QAbstractFileEngine::ExistsFlag); |
384 | } |
385 | |
386 | /*! |
387 | Returns \c true if the file specified by \a fileName exists; otherwise |
388 | returns \c false. |
389 | |
390 | \note If \a fileName is a symlink that points to a non-existing |
391 | file, false is returned. |
392 | */ |
393 | |
394 | bool |
395 | QFile::exists(const QString &fileName) |
396 | { |
397 | return QFileInfo::exists(fileName); |
398 | } |
399 | |
400 | /*! |
401 | \fn QString QFile::symLinkTarget() const |
402 | \since 4.2 |
403 | \overload |
404 | |
405 | Returns the absolute path of the file or directory a symlink (or shortcut |
406 | on Windows) points to, or a an empty string if the object isn't a symbolic |
407 | link. |
408 | |
409 | This name may not represent an existing file; it is only a string. |
410 | QFile::exists() returns \c true if the symlink points to an existing file. |
411 | |
412 | \sa fileName(), setFileName() |
413 | */ |
414 | QString QFile::symLinkTarget() const |
415 | { |
416 | Q_D(const QFile); |
417 | return d->engine()->fileName(QAbstractFileEngine::LinkName); |
418 | } |
419 | |
420 | /*! |
421 | \fn static QString QFile::symLinkTarget(const QString &fileName) |
422 | \since 4.2 |
423 | |
424 | Returns the absolute path of the file or directory referred to by the |
425 | symlink (or shortcut on Windows) specified by \a fileName, or returns an |
426 | empty string if the \a fileName does not correspond to a symbolic link. |
427 | |
428 | This name may not represent an existing file; it is only a string. |
429 | QFile::exists() returns \c true if the symlink points to an existing file. |
430 | */ |
431 | QString QFile::symLinkTarget(const QString &fileName) |
432 | { |
433 | return QFileInfo(fileName).symLinkTarget(); |
434 | } |
435 | |
436 | /*! |
437 | Removes the file specified by fileName(). Returns \c true if successful; |
438 | otherwise returns \c false. |
439 | |
440 | The file is closed before it is removed. |
441 | |
442 | \sa setFileName() |
443 | */ |
444 | |
445 | bool |
446 | QFile::remove() |
447 | { |
448 | Q_D(QFile); |
449 | if (d->fileName.isEmpty() && |
450 | !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) { |
451 | qWarning("QFile::remove: Empty or null file name" ); |
452 | return false; |
453 | } |
454 | unsetError(); |
455 | close(); |
456 | if(error() == QFile::NoError) { |
457 | if (d->engine()->remove()) { |
458 | unsetError(); |
459 | return true; |
460 | } |
461 | d->setError(QFile::RemoveError, d->fileEngine->errorString()); |
462 | } |
463 | return false; |
464 | } |
465 | |
466 | /*! |
467 | \overload |
468 | |
469 | Removes the file specified by the \a fileName given. |
470 | |
471 | Returns \c true if successful; otherwise returns \c false. |
472 | |
473 | \sa remove() |
474 | */ |
475 | |
476 | bool |
477 | QFile::remove(const QString &fileName) |
478 | { |
479 | return QFile(fileName).remove(); |
480 | } |
481 | |
482 | /*! |
483 | \since 5.15 |
484 | |
485 | Moves the file specified by fileName() to the trash. Returns \c true if successful, |
486 | and sets the fileName() to the path at which the file can be found within the trash; |
487 | otherwise returns \c false. |
488 | |
489 | \note On systems where the system API doesn't report the location of the file in the |
490 | trash, fileName() will be set to the null string once the file has been moved. On |
491 | systems that don't have a trash can, this function always returns false. |
492 | */ |
493 | bool |
494 | QFile::moveToTrash() |
495 | { |
496 | Q_D(QFile); |
497 | if (d->fileName.isEmpty() && |
498 | !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) { |
499 | qWarning("QFile::remove: Empty or null file name" ); |
500 | return false; |
501 | } |
502 | unsetError(); |
503 | close(); |
504 | if (error() == QFile::NoError) { |
505 | QFileSystemEntry fileEntry(d->fileName); |
506 | QFileSystemEntry trashEntry; |
507 | QSystemError error; |
508 | if (QFileSystemEngine::moveFileToTrash(fileEntry, trashEntry, error)) { |
509 | setFileName(trashEntry.filePath()); |
510 | unsetError(); |
511 | return true; |
512 | } |
513 | d->setError(QFile::RenameError, error.toString()); |
514 | } |
515 | return false; |
516 | } |
517 | |
518 | /*! |
519 | \since 5.15 |
520 | \overload |
521 | |
522 | Moves the file specified by fileName() to the trash. Returns \c true if successful, |
523 | and sets \a pathInTrash (if provided) to the path at which the file can be found within |
524 | the trash; otherwise returns \c false. |
525 | |
526 | \note On systems where the system API doesn't report the path of the file in the |
527 | trash, \a pathInTrash will be set to the null string once the file has been moved. |
528 | On systems that don't have a trash can, this function always returns false. |
529 | */ |
530 | bool |
531 | QFile::moveToTrash(const QString &fileName, QString *pathInTrash) |
532 | { |
533 | QFile file(fileName); |
534 | if (file.moveToTrash()) { |
535 | if (pathInTrash) |
536 | *pathInTrash = file.fileName(); |
537 | return true; |
538 | } |
539 | return false; |
540 | } |
541 | |
542 | /*! |
543 | Renames the file currently specified by fileName() to \a newName. |
544 | Returns \c true if successful; otherwise returns \c false. |
545 | |
546 | If a file with the name \a newName already exists, rename() returns \c false |
547 | (i.e., QFile will not overwrite it). |
548 | |
549 | The file is closed before it is renamed. |
550 | |
551 | If the rename operation fails, Qt will attempt to copy this file's |
552 | contents to \a newName, and then remove this file, keeping only |
553 | \a newName. If that copy operation fails or this file can't be removed, |
554 | the destination file \a newName is removed to restore the old state. |
555 | |
556 | \sa setFileName() |
557 | */ |
558 | |
559 | bool |
560 | QFile::rename(const QString &newName) |
561 | { |
562 | Q_D(QFile); |
563 | |
564 | // if this is a QTemporaryFile, the virtual fileName() call here may do something |
565 | if (fileName().isEmpty()) { |
566 | qWarning("QFile::rename: Empty or null file name" ); |
567 | return false; |
568 | } |
569 | if (d->fileName == newName) { |
570 | d->setError(QFile::RenameError, tr("Destination file is the same file." )); |
571 | return false; |
572 | } |
573 | if (!exists()) { |
574 | d->setError(QFile::RenameError, tr("Source file does not exist." )); |
575 | return false; |
576 | } |
577 | |
578 | // If the file exists and it is a case-changing rename ("foo" -> "Foo"), |
579 | // compare Ids to make sure it really is a different file. |
580 | // Note: this does not take file engines into account. |
581 | bool changingCase = false; |
582 | QByteArray targetId = QFileSystemEngine::id(QFileSystemEntry(newName)); |
583 | if (!targetId.isNull()) { |
584 | QByteArray fileId = d->fileEngine ? |
585 | d->fileEngine->id() : |
586 | QFileSystemEngine::id(QFileSystemEntry(d->fileName)); |
587 | changingCase = (fileId == targetId && d->fileName.compare(newName, Qt::CaseInsensitive) == 0); |
588 | if (!changingCase) { |
589 | d->setError(QFile::RenameError, tr("Destination file exists" )); |
590 | return false; |
591 | } |
592 | |
593 | #ifdef Q_OS_LINUX |
594 | // rename() on Linux simply does nothing when renaming "foo" to "Foo" on a case-insensitive |
595 | // FS, such as FAT32. Move the file away and rename in 2 steps to work around. |
596 | QTemporaryFileName tfn(d->fileName); |
597 | QFileSystemEntry src(d->fileName); |
598 | QSystemError error; |
599 | for (int attempt = 0; attempt < 16; ++attempt) { |
600 | QFileSystemEntry tmp(tfn.generateNext(), QFileSystemEntry::FromNativePath()); |
601 | |
602 | // rename to temporary name |
603 | if (!QFileSystemEngine::renameFile(src, tmp, error)) |
604 | continue; |
605 | |
606 | // rename to final name |
607 | if (QFileSystemEngine::renameFile(tmp, QFileSystemEntry(newName), error)) { |
608 | d->fileEngine->setFileName(newName); |
609 | d->fileName = newName; |
610 | return true; |
611 | } |
612 | |
613 | // We need to restore the original file. |
614 | QSystemError error2; |
615 | if (QFileSystemEngine::renameFile(tmp, src, error2)) |
616 | break; // report the original error, below |
617 | |
618 | // report both errors |
619 | d->setError(QFile::RenameError, |
620 | tr("Error while renaming: %1" ).arg(error.toString()) |
621 | + QLatin1Char('\n') |
622 | + tr("Unable to restore from %1: %2" ). |
623 | arg(QDir::toNativeSeparators(tmp.filePath()), error2.toString())); |
624 | return false; |
625 | } |
626 | d->setError(QFile::RenameError, |
627 | tr("Error while renaming: %1" ).arg(error.toString())); |
628 | return false; |
629 | #endif // Q_OS_LINUX |
630 | } |
631 | unsetError(); |
632 | close(); |
633 | if(error() == QFile::NoError) { |
634 | if (changingCase ? d->engine()->renameOverwrite(newName) : d->engine()->rename(newName)) { |
635 | unsetError(); |
636 | // engine was able to handle the new name so we just reset it |
637 | d->fileEngine->setFileName(newName); |
638 | d->fileName = newName; |
639 | return true; |
640 | } |
641 | |
642 | if (isSequential()) { |
643 | d->setError(QFile::RenameError, tr("Will not rename sequential file using block copy" )); |
644 | return false; |
645 | } |
646 | |
647 | QFile out(newName); |
648 | if (open(QIODevice::ReadOnly)) { |
649 | if (out.open(QIODevice::WriteOnly | QIODevice::Truncate)) { |
650 | bool error = false; |
651 | char block[4096]; |
652 | qint64 bytes; |
653 | while ((bytes = read(block, sizeof(block))) > 0) { |
654 | if (bytes != out.write(block, bytes)) { |
655 | d->setError(QFile::RenameError, out.errorString()); |
656 | error = true; |
657 | break; |
658 | } |
659 | } |
660 | if (bytes == -1) { |
661 | d->setError(QFile::RenameError, errorString()); |
662 | error = true; |
663 | } |
664 | if(!error) { |
665 | if (!remove()) { |
666 | d->setError(QFile::RenameError, tr("Cannot remove source file" )); |
667 | error = true; |
668 | } |
669 | } |
670 | if (error) { |
671 | out.remove(); |
672 | } else { |
673 | d->fileEngine->setFileName(newName); |
674 | setPermissions(permissions()); |
675 | unsetError(); |
676 | setFileName(newName); |
677 | } |
678 | close(); |
679 | return !error; |
680 | } |
681 | close(); |
682 | d->setError(QFile::RenameError, |
683 | tr("Cannot open destination file: %1" ).arg(out.errorString())); |
684 | } else { |
685 | d->setError(QFile::RenameError, errorString()); |
686 | } |
687 | } |
688 | return false; |
689 | } |
690 | |
691 | /*! |
692 | \overload |
693 | |
694 | Renames the file \a oldName to \a newName. Returns \c true if |
695 | successful; otherwise returns \c false. |
696 | |
697 | If a file with the name \a newName already exists, rename() returns \c false |
698 | (i.e., QFile will not overwrite it). |
699 | |
700 | \sa rename() |
701 | */ |
702 | |
703 | bool |
704 | QFile::rename(const QString &oldName, const QString &newName) |
705 | { |
706 | return QFile(oldName).rename(newName); |
707 | } |
708 | |
709 | /*! |
710 | |
711 | Creates a link named \a linkName that points to the file currently specified by |
712 | fileName(). What a link is depends on the underlying filesystem (be it a |
713 | shortcut on Windows or a symbolic link on Unix). Returns \c true if successful; |
714 | otherwise returns \c false. |
715 | |
716 | This function will not overwrite an already existing entity in the file system; |
717 | in this case, \c link() will return false and set \l{QFile::}{error()} to |
718 | return \l{QFile::}{RenameError}. |
719 | |
720 | \note To create a valid link on Windows, \a linkName must have a \c{.lnk} file extension. |
721 | |
722 | \sa setFileName() |
723 | */ |
724 | |
725 | bool |
726 | QFile::link(const QString &linkName) |
727 | { |
728 | Q_D(QFile); |
729 | if (fileName().isEmpty()) { |
730 | qWarning("QFile::link: Empty or null file name" ); |
731 | return false; |
732 | } |
733 | QFileInfo fi(linkName); |
734 | if (d->engine()->link(fi.absoluteFilePath())) { |
735 | unsetError(); |
736 | return true; |
737 | } |
738 | d->setError(QFile::RenameError, d->fileEngine->errorString()); |
739 | return false; |
740 | } |
741 | |
742 | /*! |
743 | \overload |
744 | |
745 | Creates a link named \a linkName that points to the file \a fileName. What a link is |
746 | depends on the underlying filesystem (be it a shortcut on Windows |
747 | or a symbolic link on Unix). Returns \c true if successful; otherwise |
748 | returns \c false. |
749 | |
750 | \sa link() |
751 | */ |
752 | |
753 | bool |
754 | QFile::link(const QString &fileName, const QString &linkName) |
755 | { |
756 | return QFile(fileName).link(linkName); |
757 | } |
758 | |
759 | /*! |
760 | Copies the file currently specified by fileName() to a file called |
761 | \a newName. Returns \c true if successful; otherwise returns \c false. |
762 | |
763 | Note that if a file with the name \a newName already exists, |
764 | copy() returns \c false (i.e. QFile will not overwrite it). |
765 | |
766 | The source file is closed before it is copied. |
767 | |
768 | \sa setFileName() |
769 | */ |
770 | |
771 | bool |
772 | QFile::copy(const QString &newName) |
773 | { |
774 | Q_D(QFile); |
775 | if (fileName().isEmpty()) { |
776 | qWarning("QFile::copy: Empty or null file name" ); |
777 | return false; |
778 | } |
779 | if (QFile::exists(newName)) { |
780 | // ### Race condition. If a file is moved in after this, it /will/ be |
781 | // overwritten. On Unix, the proper solution is to use hardlinks: |
782 | // return ::link(old, new) && ::remove(old); See also rename(). |
783 | d->setError(QFile::CopyError, tr("Destination file exists" )); |
784 | return false; |
785 | } |
786 | unsetError(); |
787 | close(); |
788 | if(error() == QFile::NoError) { |
789 | if (d->engine()->copy(newName)) { |
790 | unsetError(); |
791 | return true; |
792 | } else { |
793 | bool error = false; |
794 | if(!open(QFile::ReadOnly)) { |
795 | error = true; |
796 | d->setError(QFile::CopyError, tr("Cannot open %1 for input" ).arg(d->fileName)); |
797 | } else { |
798 | const auto fileTemplate = QLatin1String("%1/qt_temp.XXXXXX" ); |
799 | #ifdef QT_NO_TEMPORARYFILE |
800 | QFile out(fileTemplate.arg(QFileInfo(newName).path())); |
801 | if (!out.open(QIODevice::ReadWrite)) |
802 | error = true; |
803 | #else |
804 | QTemporaryFile out(fileTemplate.arg(QFileInfo(newName).path())); |
805 | if (!out.open()) { |
806 | out.setFileTemplate(fileTemplate.arg(QDir::tempPath())); |
807 | if (!out.open()) |
808 | error = true; |
809 | } |
810 | #endif |
811 | if (error) { |
812 | out.close(); |
813 | close(); |
814 | d->setError(QFile::CopyError, tr("Cannot open for output: %1" ).arg(out.errorString())); |
815 | } else { |
816 | if (!d->engine()->cloneTo(out.d_func()->engine())) { |
817 | char block[4096]; |
818 | qint64 totalRead = 0; |
819 | while (!atEnd()) { |
820 | qint64 in = read(block, sizeof(block)); |
821 | if (in <= 0) |
822 | break; |
823 | totalRead += in; |
824 | if (in != out.write(block, in)) { |
825 | close(); |
826 | d->setError(QFile::CopyError, tr("Failure to write block" )); |
827 | error = true; |
828 | break; |
829 | } |
830 | } |
831 | |
832 | if (totalRead != size()) { |
833 | // Unable to read from the source. The error string is |
834 | // already set from read(). |
835 | error = true; |
836 | } |
837 | } |
838 | |
839 | if (!error) { |
840 | // Sync to disk if possible. Ignore errors (e.g. not supported). |
841 | out.d_func()->fileEngine->syncToDisk(); |
842 | |
843 | if (!out.rename(newName)) { |
844 | error = true; |
845 | close(); |
846 | d->setError(QFile::CopyError, tr("Cannot create %1 for output" ).arg(newName)); |
847 | } |
848 | } |
849 | #ifdef QT_NO_TEMPORARYFILE |
850 | if (error) |
851 | out.remove(); |
852 | #else |
853 | if (!error) |
854 | out.setAutoRemove(false); |
855 | #endif |
856 | } |
857 | } |
858 | if(!error) { |
859 | QFile::setPermissions(newName, permissions()); |
860 | close(); |
861 | unsetError(); |
862 | return true; |
863 | } |
864 | } |
865 | } |
866 | return false; |
867 | } |
868 | |
869 | /*! |
870 | \overload |
871 | |
872 | Copies the file \a fileName to \a newName. Returns \c true if successful; |
873 | otherwise returns \c false. |
874 | |
875 | If a file with the name \a newName already exists, copy() returns \c false |
876 | (i.e., QFile will not overwrite it). |
877 | |
878 | \sa rename() |
879 | */ |
880 | |
881 | bool |
882 | QFile::copy(const QString &fileName, const QString &newName) |
883 | { |
884 | return QFile(fileName).copy(newName); |
885 | } |
886 | |
887 | /*! |
888 | Opens the file using OpenMode \a mode, returning true if successful; |
889 | otherwise false. |
890 | |
891 | The \a mode must be QIODevice::ReadOnly, QIODevice::WriteOnly, or |
892 | QIODevice::ReadWrite. It may also have additional flags, such as |
893 | QIODevice::Text and QIODevice::Unbuffered. |
894 | |
895 | \note In \l{QIODevice::}{WriteOnly} or \l{QIODevice::}{ReadWrite} |
896 | mode, if the relevant file does not already exist, this function |
897 | will try to create a new file before opening it. |
898 | |
899 | \sa QIODevice::OpenMode, setFileName() |
900 | */ |
901 | bool QFile::open(OpenMode mode) |
902 | { |
903 | Q_D(QFile); |
904 | if (isOpen()) |
905 | return file_already_open(*this); |
906 | // Either Append or NewOnly implies WriteOnly |
907 | if (mode & (Append | NewOnly)) |
908 | mode |= WriteOnly; |
909 | unsetError(); |
910 | if ((mode & (ReadOnly | WriteOnly)) == 0) { |
911 | qWarning("QIODevice::open: File access not specified" ); |
912 | return false; |
913 | } |
914 | |
915 | // QIODevice provides the buffering, so there's no need to request it from the file engine. |
916 | if (d->engine()->open(mode | QIODevice::Unbuffered)) { |
917 | QIODevice::open(mode); |
918 | if (mode & Append) |
919 | seek(size()); |
920 | return true; |
921 | } |
922 | QFile::FileError err = d->fileEngine->error(); |
923 | if(err == QFile::UnspecifiedError) |
924 | err = QFile::OpenError; |
925 | d->setError(err, d->fileEngine->errorString()); |
926 | return false; |
927 | } |
928 | |
929 | /*! |
930 | \overload |
931 | |
932 | Opens the existing file handle \a fh in the given \a mode. |
933 | \a handleFlags may be used to specify additional options. |
934 | Returns \c true if successful; otherwise returns \c false. |
935 | |
936 | Example: |
937 | \snippet code/src_corelib_io_qfile.cpp 3 |
938 | |
939 | When a QFile is opened using this function, behaviour of close() is |
940 | controlled by the AutoCloseHandle flag. |
941 | If AutoCloseHandle is specified, and this function succeeds, |
942 | then calling close() closes the adopted handle. |
943 | Otherwise, close() does not actually close the file, but only flushes it. |
944 | |
945 | \b{Warning:} |
946 | \list 1 |
947 | \li If \a fh does not refer to a regular file, e.g., it is \c stdin, |
948 | \c stdout, or \c stderr, you may not be able to seek(). size() |
949 | returns \c 0 in those cases. See QIODevice::isSequential() for |
950 | more information. |
951 | \li Since this function opens the file without specifying the file name, |
952 | you cannot use this QFile with a QFileInfo. |
953 | \endlist |
954 | |
955 | \sa close() |
956 | |
957 | \b{Note for the Windows Platform} |
958 | |
959 | \a fh must be opened in binary mode (i.e., the mode string must contain |
960 | 'b', as in "rb" or "wb") when accessing files and other random-access |
961 | devices. Qt will translate the end-of-line characters if you pass |
962 | QIODevice::Text to \a mode. Sequential devices, such as stdin and stdout, |
963 | are unaffected by this limitation. |
964 | |
965 | You need to enable support for console applications in order to use the |
966 | stdin, stdout and stderr streams at the console. To do this, add the |
967 | following declaration to your application's project file: |
968 | |
969 | \snippet code/src_corelib_io_qfile.cpp 4 |
970 | */ |
971 | bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags) |
972 | { |
973 | Q_D(QFile); |
974 | if (isOpen()) |
975 | return file_already_open(*this); |
976 | // Either Append or NewOnly implies WriteOnly |
977 | if (mode & (Append | NewOnly)) |
978 | mode |= WriteOnly; |
979 | unsetError(); |
980 | if ((mode & (ReadOnly | WriteOnly)) == 0) { |
981 | qWarning("QFile::open: File access not specified" ); |
982 | return false; |
983 | } |
984 | |
985 | // QIODevice provides the buffering, so request unbuffered file engines |
986 | if (d->openExternalFile(mode | Unbuffered, fh, handleFlags)) { |
987 | QIODevice::open(mode); |
988 | if (!(mode & Append) && !isSequential()) { |
989 | qint64 pos = (qint64)QT_FTELL(fh); |
990 | if (pos != -1) { |
991 | // Skip redundant checks in QFileDevice::seek(). |
992 | QIODevice::seek(pos); |
993 | } |
994 | } |
995 | return true; |
996 | } |
997 | return false; |
998 | } |
999 | |
1000 | /*! |
1001 | \overload |
1002 | |
1003 | Opens the existing file descriptor \a fd in the given \a mode. |
1004 | \a handleFlags may be used to specify additional options. |
1005 | Returns \c true if successful; otherwise returns \c false. |
1006 | |
1007 | When a QFile is opened using this function, behaviour of close() is |
1008 | controlled by the AutoCloseHandle flag. |
1009 | If AutoCloseHandle is specified, and this function succeeds, |
1010 | then calling close() closes the adopted handle. |
1011 | Otherwise, close() does not actually close the file, but only flushes it. |
1012 | |
1013 | \warning If \a fd is not a regular file, e.g, it is 0 (\c stdin), |
1014 | 1 (\c stdout), or 2 (\c stderr), you may not be able to seek(). In |
1015 | those cases, size() returns \c 0. See QIODevice::isSequential() |
1016 | for more information. |
1017 | |
1018 | \warning Since this function opens the file without specifying the file name, |
1019 | you cannot use this QFile with a QFileInfo. |
1020 | |
1021 | \sa close() |
1022 | */ |
1023 | bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags) |
1024 | { |
1025 | Q_D(QFile); |
1026 | if (isOpen()) |
1027 | return file_already_open(*this); |
1028 | // Either Append or NewOnly implies WriteOnly |
1029 | if (mode & (Append | NewOnly)) |
1030 | mode |= WriteOnly; |
1031 | unsetError(); |
1032 | if ((mode & (ReadOnly | WriteOnly)) == 0) { |
1033 | qWarning("QFile::open: File access not specified" ); |
1034 | return false; |
1035 | } |
1036 | |
1037 | // QIODevice provides the buffering, so request unbuffered file engines |
1038 | if (d->openExternalFile(mode | Unbuffered, fd, handleFlags)) { |
1039 | QIODevice::open(mode); |
1040 | if (!(mode & Append) && !isSequential()) { |
1041 | qint64 pos = (qint64)QT_LSEEK(fd, QT_OFF_T(0), SEEK_CUR); |
1042 | if (pos != -1) { |
1043 | // Skip redundant checks in QFileDevice::seek(). |
1044 | QIODevice::seek(pos); |
1045 | } |
1046 | } |
1047 | return true; |
1048 | } |
1049 | return false; |
1050 | } |
1051 | |
1052 | /*! |
1053 | \reimp |
1054 | */ |
1055 | bool QFile::resize(qint64 sz) |
1056 | { |
1057 | return QFileDevice::resize(sz); // for now |
1058 | } |
1059 | |
1060 | /*! |
1061 | \overload |
1062 | |
1063 | Sets \a fileName to size (in bytes) \a sz. Returns \c true if |
1064 | the resize succeeds; false otherwise. If \a sz is larger than \a |
1065 | fileName currently is the new bytes will be set to 0, if \a sz is |
1066 | smaller the file is simply truncated. |
1067 | |
1068 | \warning This function can fail if the file doesn't exist. |
1069 | |
1070 | \sa resize() |
1071 | */ |
1072 | |
1073 | bool |
1074 | QFile::resize(const QString &fileName, qint64 sz) |
1075 | { |
1076 | return QFile(fileName).resize(sz); |
1077 | } |
1078 | |
1079 | /*! |
1080 | \reimp |
1081 | */ |
1082 | QFile::Permissions QFile::permissions() const |
1083 | { |
1084 | return QFileDevice::permissions(); // for now |
1085 | } |
1086 | |
1087 | /*! |
1088 | \overload |
1089 | |
1090 | Returns the complete OR-ed together combination of |
1091 | QFile::Permission for \a fileName. |
1092 | */ |
1093 | |
1094 | QFile::Permissions |
1095 | QFile::permissions(const QString &fileName) |
1096 | { |
1097 | return QFile(fileName).permissions(); |
1098 | } |
1099 | |
1100 | /*! |
1101 | Sets the permissions for the file to the \a permissions specified. |
1102 | Returns \c true if successful, or \c false if the permissions cannot be |
1103 | modified. |
1104 | |
1105 | \warning This function does not manipulate ACLs, which may limit its |
1106 | effectiveness. |
1107 | |
1108 | \sa permissions(), setFileName() |
1109 | */ |
1110 | |
1111 | bool QFile::setPermissions(Permissions permissions) |
1112 | { |
1113 | return QFileDevice::setPermissions(permissions); // for now |
1114 | } |
1115 | |
1116 | /*! |
1117 | \overload |
1118 | |
1119 | Sets the permissions for \a fileName file to \a permissions. |
1120 | */ |
1121 | |
1122 | bool |
1123 | QFile::setPermissions(const QString &fileName, Permissions permissions) |
1124 | { |
1125 | return QFile(fileName).setPermissions(permissions); |
1126 | } |
1127 | |
1128 | /*! |
1129 | \reimp |
1130 | */ |
1131 | qint64 QFile::size() const |
1132 | { |
1133 | return QFileDevice::size(); // for now |
1134 | } |
1135 | |
1136 | /*! |
1137 | \fn QFile::QFile(const std::filesystem::path &name) |
1138 | \since 6.0 |
1139 | |
1140 | Constructs a new file object to represent the file with the given \a name. |
1141 | */ |
1142 | /*! |
1143 | \fn QFile::QFile(const std::filesystem::path &name, QObject *parent) |
1144 | \since 6.0 |
1145 | |
1146 | Constructs a new file object with the given \a parent to represent the |
1147 | file with the specified \a name. |
1148 | */ |
1149 | /*! |
1150 | \fn std::filesystem::path QFile::filesystemFileName() const |
1151 | \since 6.0 |
1152 | Returns fileName() as \c{std::filesystem::path}. |
1153 | */ |
1154 | /*! |
1155 | \fn void QFile::setFileName(const std::filesystem::path &name) |
1156 | \since 6.0 |
1157 | \overload |
1158 | */ |
1159 | /*! |
1160 | \fn bool QFile::rename(const std::filesystem::path &newName) |
1161 | \since 6.0 |
1162 | \overload |
1163 | */ |
1164 | /*! |
1165 | \fn bool QFile::link(const std::filesystem::path &newName) |
1166 | \since 6.0 |
1167 | \overload |
1168 | */ |
1169 | /*! |
1170 | \fn bool QFile::copy(const std::filesystem::path &newName) |
1171 | \since 6.0 |
1172 | \overload |
1173 | */ |
1174 | /*! |
1175 | \fn QFile::Permissions QFile::permissions(const std::filesystem::path &filename) |
1176 | \since 6.0 |
1177 | \overload |
1178 | */ |
1179 | /*! |
1180 | \fn bool QFile::setPermissions(const std::filesystem::path &filename, Permissions permissionSpec) |
1181 | \since 6.0 |
1182 | \overload |
1183 | */ |
1184 | |
1185 | |
1186 | QT_END_NAMESPACE |
1187 | |
1188 | #ifndef QT_NO_QOBJECT |
1189 | #include "moc_qfile.cpp" |
1190 | #endif |
1191 | |