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 = 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 | //! [move-to-trash-common] |
463 | The time for this function to run is independent of the size of the file |
464 | being trashed. If this function is called on a directory, it may be |
465 | proportional to the number of files being trashed. If the current |
466 | fileName() points to a symbolic link, this function will move the link to |
467 | the trash, possibly breaking it, not the target of the link. |
468 | |
469 | This function uses the Windows and \macos APIs to perform the trashing on |
470 | those two operating systems. Elsewhere (Unix systems), this function |
471 | implements the \l{FreeDesktop.org Trash specification version 1.0}. |
472 | |
473 | \note When using the FreeDesktop.org Trash implementation, this function |
474 | will fail if it is unable to move the files to the trash location by way of |
475 | file renames and hardlinks. This condition arises if the file being trashed |
476 | resides on a volume (mount point) on which the current user does not have |
477 | permission to create the \c{.Trash} directory, or with some unusual |
478 | filesystem types or configurations (such as sub-volumes that aren't |
479 | themselves mount points). |
480 | //! [move-to-trash-common] |
481 | |
482 | \note On systems where the system API doesn't report the location of the file in the |
483 | trash, fileName() will be set to the null string once the file has been moved. On |
484 | systems that don't have a trash can, this function always returns false. |
485 | */ |
486 | bool |
487 | QFile::moveToTrash() |
488 | { |
489 | Q_D(QFile); |
490 | if (d->fileName.isEmpty() && |
491 | !static_cast<QFSFileEngine *>(d->engine())->isUnnamedFile()) { |
492 | qWarning(msg: "QFile::remove: Empty or null file name" ); |
493 | return false; |
494 | } |
495 | unsetError(); |
496 | close(); |
497 | if (error() == QFile::NoError) { |
498 | QFileSystemEntry fileEntry(d->fileName); |
499 | QFileSystemEntry trashEntry; |
500 | QSystemError error; |
501 | if (QFileSystemEngine::moveFileToTrash(source: fileEntry, newLocation&: trashEntry, error)) { |
502 | setFileName(trashEntry.filePath()); |
503 | unsetError(); |
504 | return true; |
505 | } |
506 | d->setError(err: QFile::RenameError, errorString: error.toString()); |
507 | } |
508 | return false; |
509 | } |
510 | |
511 | /*! |
512 | \since 5.15 |
513 | \overload |
514 | |
515 | Moves the file specified by \a fileName to the trash. Returns \c true if successful, |
516 | and sets \a pathInTrash (if provided) to the path at which the file can be found within |
517 | the trash; otherwise returns \c false. |
518 | |
519 | \include qfile.cpp move-to-trash-common |
520 | |
521 | \note On systems where the system API doesn't report the path of the file in the |
522 | trash, \a pathInTrash will be set to the null string once the file has been moved. |
523 | On systems that don't have a trash can, this function always returns false. |
524 | |
525 | */ |
526 | bool |
527 | QFile::moveToTrash(const QString &fileName, QString *pathInTrash) |
528 | { |
529 | QFile file(fileName); |
530 | if (file.moveToTrash()) { |
531 | if (pathInTrash) |
532 | *pathInTrash = file.fileName(); |
533 | return true; |
534 | } |
535 | return false; |
536 | } |
537 | |
538 | /*! |
539 | Renames the file currently specified by fileName() to \a newName. |
540 | Returns \c true if successful; otherwise returns \c false. |
541 | |
542 | If a file with the name \a newName already exists, rename() returns \c false |
543 | (i.e., QFile will not overwrite it). |
544 | |
545 | The file is closed before it is renamed. |
546 | |
547 | If the rename operation fails, Qt will attempt to copy this file's |
548 | contents to \a newName, and then remove this file, keeping only |
549 | \a newName. If that copy operation fails or this file can't be removed, |
550 | the destination file \a newName is removed to restore the old state. |
551 | |
552 | \sa setFileName() |
553 | */ |
554 | |
555 | bool |
556 | QFile::rename(const QString &newName) |
557 | { |
558 | Q_D(QFile); |
559 | |
560 | // if this is a QTemporaryFile, the virtual fileName() call here may do something |
561 | if (fileName().isEmpty()) { |
562 | qWarning(msg: "QFile::rename: Empty or null file name" ); |
563 | return false; |
564 | } |
565 | if (d->fileName == newName) { |
566 | d->setError(err: QFile::RenameError, errorString: tr(s: "Destination file is the same file." )); |
567 | return false; |
568 | } |
569 | if (!exists()) { |
570 | d->setError(err: QFile::RenameError, errorString: tr(s: "Source file does not exist." )); |
571 | return false; |
572 | } |
573 | |
574 | // If the file exists and it is a case-changing rename ("foo" -> "Foo"), |
575 | // compare Ids to make sure it really is a different file. |
576 | // Note: this does not take file engines into account. |
577 | bool changingCase = false; |
578 | QByteArray targetId = QFileSystemEngine::id(entry: QFileSystemEntry(newName)); |
579 | if (!targetId.isNull()) { |
580 | QByteArray fileId = d->fileEngine ? |
581 | d->fileEngine->id() : |
582 | QFileSystemEngine::id(entry: QFileSystemEntry(d->fileName)); |
583 | changingCase = (fileId == targetId && d->fileName.compare(s: newName, cs: Qt::CaseInsensitive) == 0); |
584 | if (!changingCase) { |
585 | d->setError(err: QFile::RenameError, errorString: tr(s: "Destination file exists" )); |
586 | return false; |
587 | } |
588 | |
589 | #if defined(Q_OS_LINUX) && QT_CONFIG(temporaryfile) |
590 | // rename() on Linux simply does nothing when renaming "foo" to "Foo" on a case-insensitive |
591 | // FS, such as FAT32. Move the file away and rename in 2 steps to work around. |
592 | QTemporaryFileName tfn(d->fileName); |
593 | QFileSystemEntry src(d->fileName); |
594 | QSystemError error; |
595 | for (int attempt = 0; attempt < 16; ++attempt) { |
596 | QFileSystemEntry tmp(tfn.generateNext(), QFileSystemEntry::FromNativePath()); |
597 | |
598 | // rename to temporary name |
599 | if (!QFileSystemEngine::renameFile(source: src, target: tmp, error)) |
600 | continue; |
601 | |
602 | // rename to final name |
603 | if (QFileSystemEngine::renameFile(source: tmp, target: QFileSystemEntry(newName), error)) { |
604 | d->fileEngine->setFileName(newName); |
605 | d->fileName = newName; |
606 | return true; |
607 | } |
608 | |
609 | // We need to restore the original file. |
610 | QSystemError error2; |
611 | if (QFileSystemEngine::renameFile(source: tmp, target: src, error&: error2)) |
612 | break; // report the original error, below |
613 | |
614 | // report both errors |
615 | d->setError(err: QFile::RenameError, |
616 | errorString: tr(s: "Error while renaming: %1" ).arg(a: error.toString()) |
617 | + u'\n' |
618 | + tr(s: "Unable to restore from %1: %2" ). |
619 | arg(args: QDir::toNativeSeparators(pathName: tmp.filePath()), args: error2.toString())); |
620 | return false; |
621 | } |
622 | d->setError(err: QFile::RenameError, |
623 | errorString: tr(s: "Error while renaming: %1" ).arg(a: error.toString())); |
624 | return false; |
625 | #endif // Q_OS_LINUX |
626 | } |
627 | unsetError(); |
628 | close(); |
629 | if (error() == QFile::NoError) { |
630 | if (changingCase ? d->engine()->renameOverwrite(newName) : d->engine()->rename(newName)) { |
631 | unsetError(); |
632 | // engine was able to handle the new name so we just reset it |
633 | d->fileEngine->setFileName(newName); |
634 | d->fileName = newName; |
635 | return true; |
636 | } |
637 | |
638 | if (isSequential()) { |
639 | d->setError(err: QFile::RenameError, errorString: tr(s: "Will not rename sequential file using block copy" )); |
640 | return false; |
641 | } |
642 | |
643 | QFile out(newName); |
644 | if (open(flags: QIODevice::ReadOnly)) { |
645 | if (out.open(flags: QIODevice::WriteOnly | QIODevice::Truncate)) { |
646 | bool error = false; |
647 | char block[4096]; |
648 | qint64 bytes; |
649 | while ((bytes = read(data: block, maxlen: sizeof(block))) > 0) { |
650 | if (bytes != out.write(data: block, len: bytes)) { |
651 | d->setError(err: QFile::RenameError, errorString: out.errorString()); |
652 | error = true; |
653 | break; |
654 | } |
655 | } |
656 | if (bytes == -1) { |
657 | d->setError(err: QFile::RenameError, errorString: errorString()); |
658 | error = true; |
659 | } |
660 | if (!error) { |
661 | if (!remove()) { |
662 | d->setError(err: QFile::RenameError, errorString: tr(s: "Cannot remove source file" )); |
663 | error = true; |
664 | } |
665 | } |
666 | if (error) { |
667 | out.remove(); |
668 | } else { |
669 | d->fileEngine->setFileName(newName); |
670 | setPermissions(permissions()); |
671 | unsetError(); |
672 | setFileName(newName); |
673 | } |
674 | close(); |
675 | return !error; |
676 | } |
677 | close(); |
678 | d->setError(err: QFile::RenameError, |
679 | errorString: tr(s: "Cannot open destination file: %1" ).arg(a: out.errorString())); |
680 | } else { |
681 | d->setError(err: QFile::RenameError, errorString: errorString()); |
682 | } |
683 | } |
684 | return false; |
685 | } |
686 | |
687 | /*! |
688 | \overload |
689 | |
690 | Renames the file \a oldName to \a newName. Returns \c true if |
691 | successful; otherwise returns \c false. |
692 | |
693 | If a file with the name \a newName already exists, rename() returns \c false |
694 | (i.e., QFile will not overwrite it). |
695 | |
696 | \sa rename() |
697 | */ |
698 | |
699 | bool |
700 | QFile::rename(const QString &oldName, const QString &newName) |
701 | { |
702 | return QFile(oldName).rename(newName); |
703 | } |
704 | |
705 | /*! |
706 | |
707 | Creates a link named \a linkName that points to the file currently specified by |
708 | fileName(). What a link is depends on the underlying filesystem (be it a |
709 | shortcut on Windows or a symbolic link on Unix). Returns \c true if successful; |
710 | otherwise returns \c false. |
711 | |
712 | This function will not overwrite an already existing entity in the file system; |
713 | in this case, \c link() will return false and set \l{QFile::}{error()} to |
714 | return \l{QFile::}{RenameError}. |
715 | |
716 | \note To create a valid link on Windows, \a linkName must have a \c{.lnk} file extension. |
717 | |
718 | \sa setFileName() |
719 | */ |
720 | |
721 | bool |
722 | QFile::link(const QString &linkName) |
723 | { |
724 | Q_D(QFile); |
725 | if (fileName().isEmpty()) { |
726 | qWarning(msg: "QFile::link: Empty or null file name" ); |
727 | return false; |
728 | } |
729 | QFileInfo fi(linkName); |
730 | if (d->engine()->link(newName: fi.absoluteFilePath())) { |
731 | unsetError(); |
732 | return true; |
733 | } |
734 | d->setError(err: QFile::RenameError, errorString: d->fileEngine->errorString()); |
735 | return false; |
736 | } |
737 | |
738 | /*! |
739 | \overload |
740 | |
741 | Creates a link named \a linkName that points to the file \a fileName. What a link is |
742 | depends on the underlying filesystem (be it a shortcut on Windows |
743 | or a symbolic link on Unix). Returns \c true if successful; otherwise |
744 | returns \c false. |
745 | |
746 | \sa link() |
747 | */ |
748 | |
749 | bool |
750 | QFile::link(const QString &fileName, const QString &linkName) |
751 | { |
752 | return QFile(fileName).link(linkName); |
753 | } |
754 | |
755 | /*! |
756 | Copies the file named fileName() to \a newName. |
757 | |
758 | \include qfile-copy.qdocinc |
759 | |
760 | \note On Android, this operation is not yet supported for \c content |
761 | scheme URIs. |
762 | |
763 | \sa setFileName() |
764 | */ |
765 | |
766 | bool |
767 | QFile::copy(const QString &newName) |
768 | { |
769 | Q_D(QFile); |
770 | if (fileName().isEmpty()) { |
771 | qWarning(msg: "QFile::copy: Empty or null file name" ); |
772 | return false; |
773 | } |
774 | if (QFile::exists(fileName: newName)) { |
775 | // ### Race condition. If a file is moved in after this, it /will/ be |
776 | // overwritten. On Unix, the proper solution is to use hardlinks: |
777 | // return ::link(old, new) && ::remove(old); See also rename(). |
778 | d->setError(err: QFile::CopyError, errorString: tr(s: "Destination file exists" )); |
779 | return false; |
780 | } |
781 | unsetError(); |
782 | close(); |
783 | if (error() == QFile::NoError) { |
784 | if (d->engine()->copy(newName)) { |
785 | unsetError(); |
786 | return true; |
787 | } else { |
788 | bool error = false; |
789 | if (!open(flags: QFile::ReadOnly)) { |
790 | error = true; |
791 | d->setError(err: QFile::CopyError, errorString: tr(s: "Cannot open %1 for input" ).arg(a: d->fileName)); |
792 | } else { |
793 | const auto fileTemplate = "%1/qt_temp.XXXXXX"_L1 ; |
794 | #if !QT_CONFIG(temporaryfile) |
795 | QFile out(fileTemplate.arg(QFileInfo(newName).path())); |
796 | if (!out.open(QIODevice::ReadWrite)) |
797 | error = true; |
798 | #else |
799 | QTemporaryFile out(fileTemplate.arg(args: QFileInfo(newName).path())); |
800 | if (!out.open()) { |
801 | out.setFileTemplate(fileTemplate.arg(args: QDir::tempPath())); |
802 | if (!out.open()) |
803 | error = true; |
804 | } |
805 | #endif |
806 | if (error) { |
807 | d->setError(err: QFile::CopyError, errorString: tr(s: "Cannot open for output: %1" ).arg(a: out.errorString())); |
808 | out.close(); |
809 | close(); |
810 | } else { |
811 | if (!d->engine()->cloneTo(target: out.d_func()->engine())) { |
812 | char block[4096]; |
813 | qint64 totalRead = 0; |
814 | while (!atEnd()) { |
815 | qint64 in = read(data: block, maxlen: sizeof(block)); |
816 | if (in <= 0) |
817 | break; |
818 | totalRead += in; |
819 | if (in != out.write(data: block, len: in)) { |
820 | close(); |
821 | d->setError(err: QFile::CopyError, errorString: tr(s: "Failure to write block: %1" ) |
822 | .arg(a: out.errorString())); |
823 | error = true; |
824 | break; |
825 | } |
826 | } |
827 | |
828 | if (totalRead != size()) { |
829 | // Unable to read from the source. The error string is |
830 | // already set from read(). |
831 | error = true; |
832 | } |
833 | } |
834 | |
835 | if (!error) { |
836 | // Sync to disk if possible. Ignore errors (e.g. not supported). |
837 | out.d_func()->fileEngine->syncToDisk(); |
838 | |
839 | if (!out.rename(newName)) { |
840 | error = true; |
841 | close(); |
842 | d->setError(err: QFile::CopyError, errorString: tr(s: "Cannot create %1 for output: %2" ) |
843 | .arg(args: newName, args: out.errorString())); |
844 | } |
845 | } |
846 | #if !QT_CONFIG(temporaryfile) |
847 | if (error) |
848 | out.remove(); |
849 | #else |
850 | if (!error) |
851 | out.setAutoRemove(false); |
852 | #endif |
853 | } |
854 | } |
855 | if (!error) { |
856 | QFile::setPermissions(filename: newName, permissionSpec: permissions()); |
857 | close(); |
858 | unsetError(); |
859 | return true; |
860 | } |
861 | } |
862 | } |
863 | return false; |
864 | } |
865 | |
866 | /*! |
867 | \overload |
868 | |
869 | Copies the file named \a fileName to \a newName. |
870 | |
871 | \include qfile-copy.qdocinc |
872 | |
873 | \note On Android, this operation is not yet supported for \c content |
874 | scheme URIs. |
875 | |
876 | \sa rename() |
877 | */ |
878 | |
879 | bool |
880 | QFile::copy(const QString &fileName, const QString &newName) |
881 | { |
882 | return QFile(fileName).copy(newName); |
883 | } |
884 | |
885 | /*! |
886 | Opens the file using OpenMode \a mode, returning true if successful; |
887 | otherwise false. |
888 | |
889 | The \a mode must be QIODevice::ReadOnly, QIODevice::WriteOnly, or |
890 | QIODevice::ReadWrite. It may also have additional flags, such as |
891 | QIODevice::Text and QIODevice::Unbuffered. |
892 | |
893 | \note In \l{QIODevice::}{WriteOnly} or \l{QIODevice::}{ReadWrite} |
894 | mode, if the relevant file does not already exist, this function |
895 | will try to create a new file before opening it. The file will be |
896 | created with mode 0666 masked by the umask on POSIX systems, and |
897 | with permissions inherited from the parent directory on Windows. |
898 | On Android, it's expected to have access permission to the parent |
899 | of the file name, otherwise, it won't be possible to create this |
900 | non-existing file. |
901 | |
902 | \sa QT_USE_NODISCARD_FILE_OPEN |
903 | |
904 | \sa QIODevice::OpenMode, setFileName() |
905 | */ |
906 | bool QFile::open(OpenMode mode) |
907 | { |
908 | Q_D(QFile); |
909 | if (isOpen()) |
910 | return file_already_open(file&: *this); |
911 | // Either Append or NewOnly implies WriteOnly |
912 | if (mode & (Append | NewOnly)) |
913 | mode |= WriteOnly; |
914 | unsetError(); |
915 | if ((mode & (ReadOnly | WriteOnly)) == 0) { |
916 | qWarning(msg: "QIODevice::open: File access not specified" ); |
917 | return false; |
918 | } |
919 | |
920 | // QIODevice provides the buffering, so there's no need to request it from the file engine. |
921 | if (d->engine()->open(openMode: mode | QIODevice::Unbuffered)) { |
922 | QIODevice::open(mode); |
923 | if (mode & Append) |
924 | seek(offset: size()); |
925 | return true; |
926 | } |
927 | QFile::FileError err = d->fileEngine->error(); |
928 | if (err == QFile::UnspecifiedError) |
929 | err = QFile::OpenError; |
930 | d->setError(err, errorString: d->fileEngine->errorString()); |
931 | return false; |
932 | } |
933 | |
934 | /*! |
935 | \overload |
936 | |
937 | If the file does not exist and \a mode implies creating it, it is created |
938 | with the specified \a permissions. |
939 | |
940 | On POSIX systems the actual permissions are influenced by the |
941 | value of \c umask. |
942 | |
943 | On Windows the permissions are emulated using ACLs. These ACLs may be in non-canonical |
944 | order when the group is granted less permissions than others. Files and directories with |
945 | such permissions will generate warnings when the Security tab of the Properties dialog |
946 | is opened. Granting the group all permissions granted to others avoids such warnings. |
947 | |
948 | \sa QIODevice::OpenMode, setFileName(), QT_USE_NODISCARD_FILE_OPEN |
949 | \since 6.3 |
950 | */ |
951 | bool QFile::open(OpenMode mode, QFile::Permissions permissions) |
952 | { |
953 | Q_D(QFile); |
954 | if (isOpen()) |
955 | return file_already_open(file&: *this); |
956 | // Either Append or NewOnly implies WriteOnly |
957 | if (mode & (Append | NewOnly)) |
958 | mode |= WriteOnly; |
959 | unsetError(); |
960 | if ((mode & (ReadOnly | WriteOnly)) == 0) { |
961 | qWarning(msg: "QIODevice::open: File access not specified" ); |
962 | return false; |
963 | } |
964 | |
965 | // QIODevice provides the buffering, so there's no need to request it from the file engine. |
966 | if (d->engine()->open(openMode: mode | QIODevice::Unbuffered, permissions)) { |
967 | QIODevice::open(mode); |
968 | if (mode & Append) |
969 | seek(offset: size()); |
970 | return true; |
971 | } |
972 | QFile::FileError err = d->fileEngine->error(); |
973 | if (err == QFile::UnspecifiedError) |
974 | err = QFile::OpenError; |
975 | d->setError(err, errorString: d->fileEngine->errorString()); |
976 | return false; |
977 | } |
978 | |
979 | /*! |
980 | \overload |
981 | |
982 | Opens the existing file handle \a fh in the given \a mode. |
983 | \a handleFlags may be used to specify additional options. |
984 | Returns \c true if successful; otherwise returns \c false. |
985 | |
986 | Example: |
987 | \snippet code/src_corelib_io_qfile.cpp 3 |
988 | |
989 | When a QFile is opened using this function, behaviour of close() is |
990 | controlled by the AutoCloseHandle flag. |
991 | If AutoCloseHandle is specified, and this function succeeds, |
992 | then calling close() closes the adopted handle. |
993 | Otherwise, close() does not actually close the file, but only flushes it. |
994 | |
995 | \b{Warning:} |
996 | \list 1 |
997 | \li If \a fh does not refer to a regular file, e.g., it is \c stdin, |
998 | \c stdout, or \c stderr, you may not be able to seek(). size() |
999 | returns \c 0 in those cases. See QIODevice::isSequential() for |
1000 | more information. |
1001 | \li Since this function opens the file without specifying the file name, |
1002 | you cannot use this QFile with a QFileInfo. |
1003 | \endlist |
1004 | |
1005 | \sa close(), QT_USE_NODISCARD_FILE_OPEN |
1006 | |
1007 | \b{Note for the Windows Platform} |
1008 | |
1009 | \a fh must be opened in binary mode (i.e., the mode string must contain |
1010 | 'b', as in "rb" or "wb") when accessing files and other random-access |
1011 | devices. Qt will translate the end-of-line characters if you pass |
1012 | QIODevice::Text to \a mode. Sequential devices, such as stdin and stdout, |
1013 | are unaffected by this limitation. |
1014 | |
1015 | You need to enable support for console applications in order to use the |
1016 | stdin, stdout and stderr streams at the console. To do this, add the |
1017 | following declaration to your application's project file: |
1018 | |
1019 | \snippet code/src_corelib_io_qfile.cpp 4 |
1020 | */ |
1021 | bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags) |
1022 | { |
1023 | Q_D(QFile); |
1024 | if (isOpen()) |
1025 | return file_already_open(file&: *this); |
1026 | // Either Append or NewOnly implies WriteOnly |
1027 | if (mode & (Append | NewOnly)) |
1028 | mode |= WriteOnly; |
1029 | unsetError(); |
1030 | if ((mode & (ReadOnly | WriteOnly)) == 0) { |
1031 | qWarning(msg: "QFile::open: File access not specified" ); |
1032 | return false; |
1033 | } |
1034 | |
1035 | // QIODevice provides the buffering, so request unbuffered file engines |
1036 | if (d->openExternalFile(flags: mode | Unbuffered, fh, handleFlags)) { |
1037 | QIODevice::open(mode); |
1038 | if (!(mode & Append) && !isSequential()) { |
1039 | qint64 pos = (qint64)QT_FTELL(stream: fh); |
1040 | if (pos != -1) { |
1041 | // Skip redundant checks in QFileDevice::seek(). |
1042 | QIODevice::seek(pos); |
1043 | } |
1044 | } |
1045 | return true; |
1046 | } |
1047 | return false; |
1048 | } |
1049 | |
1050 | /*! |
1051 | \overload |
1052 | |
1053 | Opens the existing file descriptor \a fd in the given \a mode. |
1054 | \a handleFlags may be used to specify additional options. |
1055 | Returns \c true if successful; otherwise returns \c false. |
1056 | |
1057 | When a QFile is opened using this function, behaviour of close() is |
1058 | controlled by the AutoCloseHandle flag. |
1059 | If AutoCloseHandle is specified, and this function succeeds, |
1060 | then calling close() closes the adopted handle. |
1061 | Otherwise, close() does not actually close the file, but only flushes it. |
1062 | |
1063 | \warning If \a fd is not a regular file, e.g, it is 0 (\c stdin), |
1064 | 1 (\c stdout), or 2 (\c stderr), you may not be able to seek(). In |
1065 | those cases, size() returns \c 0. See QIODevice::isSequential() |
1066 | for more information. |
1067 | |
1068 | \warning Since this function opens the file without specifying the file name, |
1069 | you cannot use this QFile with a QFileInfo. |
1070 | |
1071 | \sa close(), QT_USE_NODISCARD_FILE_OPEN |
1072 | */ |
1073 | bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags) |
1074 | { |
1075 | Q_D(QFile); |
1076 | if (isOpen()) |
1077 | return file_already_open(file&: *this); |
1078 | // Either Append or NewOnly implies WriteOnly |
1079 | if (mode & (Append | NewOnly)) |
1080 | mode |= WriteOnly; |
1081 | unsetError(); |
1082 | if ((mode & (ReadOnly | WriteOnly)) == 0) { |
1083 | qWarning(msg: "QFile::open: File access not specified" ); |
1084 | return false; |
1085 | } |
1086 | |
1087 | // QIODevice provides the buffering, so request unbuffered file engines |
1088 | if (d->openExternalFile(flags: mode | Unbuffered, fd, handleFlags)) { |
1089 | QIODevice::open(mode); |
1090 | if (!(mode & Append) && !isSequential()) { |
1091 | qint64 pos = (qint64)QT_LSEEK(fd: fd, QT_OFF_T(0), SEEK_CUR); |
1092 | if (pos != -1) { |
1093 | // Skip redundant checks in QFileDevice::seek(). |
1094 | QIODevice::seek(pos); |
1095 | } |
1096 | } |
1097 | return true; |
1098 | } |
1099 | return false; |
1100 | } |
1101 | |
1102 | /*! |
1103 | \reimp |
1104 | */ |
1105 | bool QFile::resize(qint64 sz) |
1106 | { |
1107 | return QFileDevice::resize(sz); // for now |
1108 | } |
1109 | |
1110 | /*! |
1111 | \overload |
1112 | |
1113 | Sets \a fileName to size (in bytes) \a sz. Returns \c true if |
1114 | the resize succeeds; false otherwise. If \a sz is larger than \a |
1115 | fileName currently is the new bytes will be set to 0, if \a sz is |
1116 | smaller the file is simply truncated. |
1117 | |
1118 | \warning This function can fail if the file doesn't exist. |
1119 | |
1120 | \sa resize() |
1121 | */ |
1122 | |
1123 | bool |
1124 | QFile::resize(const QString &fileName, qint64 sz) |
1125 | { |
1126 | return QFile(fileName).resize(sz); |
1127 | } |
1128 | |
1129 | /*! |
1130 | \reimp |
1131 | */ |
1132 | QFile::Permissions QFile::permissions() const |
1133 | { |
1134 | return QFileDevice::permissions(); // for now |
1135 | } |
1136 | |
1137 | /*! |
1138 | \overload |
1139 | |
1140 | Returns the complete OR-ed together combination of |
1141 | QFile::Permission for \a fileName. |
1142 | */ |
1143 | |
1144 | QFile::Permissions |
1145 | QFile::permissions(const QString &fileName) |
1146 | { |
1147 | return QFile(fileName).permissions(); |
1148 | } |
1149 | |
1150 | /*! |
1151 | Sets the permissions for the file to the \a permissions specified. |
1152 | Returns \c true if successful, or \c false if the permissions cannot be |
1153 | modified. |
1154 | |
1155 | \warning This function does not manipulate ACLs, which may limit its |
1156 | effectiveness. |
1157 | |
1158 | \sa permissions(), setFileName() |
1159 | */ |
1160 | |
1161 | bool QFile::setPermissions(Permissions permissions) |
1162 | { |
1163 | return QFileDevice::setPermissions(permissions); // for now |
1164 | } |
1165 | |
1166 | /*! |
1167 | \overload |
1168 | |
1169 | Sets the permissions for \a fileName file to \a permissions. |
1170 | */ |
1171 | |
1172 | bool |
1173 | QFile::setPermissions(const QString &fileName, Permissions permissions) |
1174 | { |
1175 | return QFile(fileName).setPermissions(permissions); |
1176 | } |
1177 | |
1178 | /*! |
1179 | \reimp |
1180 | */ |
1181 | qint64 QFile::size() const |
1182 | { |
1183 | return QFileDevice::size(); // for now |
1184 | } |
1185 | |
1186 | /*! |
1187 | \fn QFile::QFile(const std::filesystem::path &name) |
1188 | \since 6.0 |
1189 | |
1190 | Constructs a new file object to represent the file with the given \a name. |
1191 | |
1192 | \include qfile.cpp qfile-explicit-constructor-note |
1193 | */ |
1194 | /*! |
1195 | \fn QFile::QFile(const std::filesystem::path &name, QObject *parent) |
1196 | \since 6.0 |
1197 | |
1198 | Constructs a new file object with the given \a parent to represent the |
1199 | file with the specified \a name. |
1200 | */ |
1201 | /*! |
1202 | \fn std::filesystem::path QFile::filesystemFileName() const |
1203 | \since 6.0 |
1204 | Returns fileName() as \c{std::filesystem::path}. |
1205 | */ |
1206 | /*! |
1207 | \fn void QFile::setFileName(const std::filesystem::path &name) |
1208 | \since 6.0 |
1209 | \overload |
1210 | */ |
1211 | /*! |
1212 | \fn bool QFile::rename(const std::filesystem::path &newName) |
1213 | \since 6.0 |
1214 | \overload |
1215 | */ |
1216 | /*! |
1217 | \fn bool QFile::link(const std::filesystem::path &newName) |
1218 | \since 6.0 |
1219 | \overload |
1220 | */ |
1221 | /*! |
1222 | \fn bool QFile::copy(const std::filesystem::path &newName) |
1223 | \since 6.0 |
1224 | \overload |
1225 | */ |
1226 | /*! |
1227 | \fn QFile::Permissions QFile::permissions(const std::filesystem::path &filename) |
1228 | \since 6.0 |
1229 | \overload |
1230 | */ |
1231 | /*! |
1232 | \fn bool QFile::setPermissions(const std::filesystem::path &filename, Permissions permissionSpec) |
1233 | \since 6.0 |
1234 | \overload |
1235 | */ |
1236 | /*! |
1237 | \fn bool exists(const std::filesystem::path &fileName) |
1238 | \since 6.3 |
1239 | \overload |
1240 | */ |
1241 | /*! |
1242 | \fn std::filesystem::path QFile::filesystemSymLinkTarget() const |
1243 | \since 6.3 |
1244 | Returns symLinkTarget() as \c{std::filesystem::path}. |
1245 | */ |
1246 | /*! |
1247 | \fn std::filesystem::path QFile::filesystemSymLinkTarget(const std::filesystem::path &fileName) |
1248 | \since 6.3 |
1249 | Returns symLinkTarget() as \c{std::filesystem::path} of \a fileName. |
1250 | */ |
1251 | /*! |
1252 | \fn bool remove(const std::filesystem::path &fileName) |
1253 | \since 6.3 |
1254 | \overload |
1255 | */ |
1256 | /*! |
1257 | \fn bool moveToTrash(const std::filesystem::path &fileName, QString *pathInTrash) |
1258 | \since 6.3 |
1259 | \overload |
1260 | */ |
1261 | /*! |
1262 | \fn bool rename(const std::filesystem::path &oldName, const std::filesystem::path &newName) |
1263 | \since 6.3 |
1264 | \overload |
1265 | */ |
1266 | /*! |
1267 | \fn bool link(const std::filesystem::path &fileName, const std::filesystem::path &newName); |
1268 | \since 6.3 |
1269 | \overload |
1270 | */ |
1271 | /*! |
1272 | \fn bool copy(const std::filesystem::path &fileName, const std::filesystem::path &newName); |
1273 | \since 6.3 |
1274 | \overload |
1275 | */ |
1276 | |
1277 | |
1278 | /*! |
1279 | \class QNtfsPermissionCheckGuard |
1280 | \since 6.6 |
1281 | \inmodule QtCore |
1282 | \brief The QNtfsPermissionCheckGuard class is a RAII class to manage NTFS |
1283 | permission checking. |
1284 | |
1285 | \ingroup io |
1286 | |
1287 | For performance reasons, QFile, QFileInfo, and related classes do not |
1288 | perform full ownership and permission (ACL) checking on NTFS file systems |
1289 | by default. During the lifetime of any instance of this class, that |
1290 | default is overridden and advanced checking is performed. This provides |
1291 | a safe and easy way to manage enabling and disabling this change to the |
1292 | default behavior. |
1293 | |
1294 | Example: |
1295 | |
1296 | \snippet ntfsp.cpp raii |
1297 | |
1298 | This class is available only on Windows. |
1299 | |
1300 | \section1 qt_ntfs_permission_lookup |
1301 | |
1302 | Prior to Qt 6.6, the user had to directly manipulate the global variable |
1303 | \c qt_ntfs_permission_lookup. However, this was a non-atomic global |
1304 | variable and as such it was prone to data races. |
1305 | |
1306 | The variable \c qt_ntfs_permission_lookup is therefore deprecated since Qt |
1307 | 6.6. |
1308 | */ |
1309 | |
1310 | /*! |
1311 | \fn QNtfsPermissionCheckGuard::QNtfsPermissionCheckGuard() |
1312 | |
1313 | Creates a guard and calls the function qEnableNtfsPermissionChecks(). |
1314 | */ |
1315 | |
1316 | /*! |
1317 | \fn QNtfsPermissionCheckGuard::~QNtfsPermissionCheckGuard() |
1318 | |
1319 | Destroys the guard and calls the function qDisableNtfsPermissionChecks(). |
1320 | */ |
1321 | |
1322 | |
1323 | /*! |
1324 | \fn bool qEnableNtfsPermissionChecks() |
1325 | \since 6.6 |
1326 | \threadsafe |
1327 | \relates QNtfsPermissionCheckGuard |
1328 | |
1329 | Enables permission checking on NTFS file systems. Returns \c true if the check |
1330 | was already enabled before the call to this function, meaning that there |
1331 | are other users. |
1332 | |
1333 | This function is only available on Windows and makes the direct |
1334 | manipulation of \l qt_ntfs_permission_lookup obsolete. |
1335 | |
1336 | This is a low-level function, please consider the RAII class |
1337 | \l QNtfsPermissionCheckGuard instead. |
1338 | |
1339 | \note The thread-safety of this function holds only as long as there are no |
1340 | concurrent updates to \l qt_ntfs_permission_lookup. |
1341 | */ |
1342 | |
1343 | /*! |
1344 | \fn bool qDisableNtfsPermissionChecks() |
1345 | \since 6.6 |
1346 | \threadsafe |
1347 | \relates QNtfsPermissionCheckGuard |
1348 | |
1349 | Disables permission checking on NTFS file systems. Returns \c true if the |
1350 | check is disabled, meaning that there are no more users. |
1351 | |
1352 | This function is only available on Windows and makes the direct |
1353 | manipulation of \l qt_ntfs_permission_lookup obsolete. |
1354 | |
1355 | This is a low-level function and must (only) be called to match one earlier |
1356 | call to qEnableNtfsPermissionChecks(). Please consider the RAII class |
1357 | \l QNtfsPermissionCheckGuard instead. |
1358 | |
1359 | \note The thread-safety of this function holds only as long as there are no |
1360 | concurrent updates to \l qt_ntfs_permission_lookup. |
1361 | */ |
1362 | |
1363 | /*! |
1364 | \fn bool qAreNtfsPermissionChecksEnabled() |
1365 | \since 6.6 |
1366 | \threadsafe |
1367 | \relates QNtfsPermissionCheckGuard |
1368 | |
1369 | Checks the status of the permission checks on NTFS file systems. Returns |
1370 | \c true if the check is enabled. |
1371 | |
1372 | This function is only available on Windows and makes the direct |
1373 | manipulation of \l qt_ntfs_permission_lookup obsolete. |
1374 | |
1375 | \note The thread-safety of this function holds only as long as there are no |
1376 | concurrent updates to \l qt_ntfs_permission_lookup. |
1377 | */ |
1378 | |
1379 | QT_END_NAMESPACE |
1380 | |
1381 | #ifndef QT_NO_QOBJECT |
1382 | #include "moc_qfile.cpp" |
1383 | #endif |
1384 | |