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