diff --git a/include/dfm-io/dfm-io/dfile.h b/include/dfm-io/dfm-io/dfile.h index e4162816..6451b113 100644 --- a/include/dfm-io/dfm-io/dfile.h +++ b/include/dfm-io/dfm-io/dfile.h @@ -77,6 +77,18 @@ class DFile }; Q_DECLARE_FLAGS(Permissions, Permission) + enum class FileCopyType : uint8_t { + kCopyTypeByGioStream = 0, // using gio stream copy + kCopyTypeBySys = 1, // using system open read write + + }; + + enum class FileCopySyncType : uint8_t { + kSyncByGioStream = 0, // using gio stream copy + kSyncBySys = 1, // using system open read write + + }; + // callback, use function pointer using ReadCallbackFunc = void (*)(qint64, void *); using ReadQCallbackFunc = void (*)(QByteArray, void *); @@ -97,8 +109,13 @@ class DFile qint64 pos() const; Permissions permissions() const; DFMIOError lastError() const; + void setCopyType(const FileCopyType type); + void setSyncType(const FileCopySyncType type); + FileCopyType copyFileType() const; + FileCopySyncType syncType() const; bool open(OpenFlags mode); + bool open(const int mode, const int permissions); bool close(); bool cancel(); bool seek(qint64 pos, SeekType type = SeekType::kBegin) const; diff --git a/src/dfm-io/dfm-io/dfile.cpp b/src/dfm-io/dfm-io/dfile.cpp index 1897decf..3375e8b5 100644 --- a/src/dfm-io/dfm-io/dfile.cpp +++ b/src/dfm-io/dfm-io/dfile.cpp @@ -14,6 +14,7 @@ #include #include +#include USING_IO_NAMESPACE @@ -189,9 +190,8 @@ quint32 DFilePrivate::buildPermissions(DFile::Permissions permission) bool DFilePrivate::doOpen(DFile::OpenFlags mode) { - if (q->isOpen()) { - error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); - return false; + if (isOpen) { + return true; } // check mode @@ -309,7 +309,7 @@ bool DFilePrivate::doClose() cancellable = nullptr; } - return true; + return doCloseBySys(); } QByteArray DFilePrivate::doReadAll() @@ -407,6 +407,345 @@ qint64 DFilePrivate::doWrite(const QByteArray &data) return doWrite(data.data(), data.length()); } +qint64 DFilePrivate::read(char *data, qint64 maxSize) +{ + GInputStream *inputStream = this->inputStream(); + if (!inputStream) { + error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); + return -1; + } + + g_autoptr(GError) gerror = nullptr; + checkAndResetCancel(); + gssize read = g_input_stream_read(inputStream, + data, + static_cast(maxSize), + cancellable, + &gerror); + if (gerror) { + setErrorFromGError(gerror); + return -1; + } + + return read; +} + +QByteArray DFilePrivate::read(qint64 maxSize) +{ + GInputStream *inputStream = this->inputStream(); + if (!inputStream) { + error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); + return QByteArray(); + } + + char data[maxSize + 1]; + memset(&data, 0, maxSize + 1); + + g_autoptr(GError) gerror = nullptr; + checkAndResetCancel(); + g_input_stream_read(inputStream, + data, + static_cast(maxSize), + cancellable, + &gerror); + if (gerror) { + setErrorFromGError(gerror); + return QByteArray(); + } + + return QByteArray(data); +} + +qint64 DFilePrivate::pos() const +{ + GInputStream *inputStream = const_cast(this)->inputStream(); + if (inputStream) { + // seems g_seekable_can_seek only support local file, survey after. todo lanxs + gboolean canSeek = G_IS_SEEKABLE(inputStream) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; + if (!canSeek) { + return -1; + } + + GSeekable *seekable = G_SEEKABLE(inputStream); + if (!seekable) { + return -2; + } + + goffset pos = g_seekable_tell(seekable); + + return qint64(pos); + } + + GOutputStream *outputStream = const_cast(this)->outputStream(); + if (outputStream){ + // seems g_seekable_can_seek only support local file, survey after. todo lanxs + gboolean canSeek = G_IS_SEEKABLE(outputStream) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; + if (!canSeek) { + return -3; + } + + GSeekable *seekable = G_SEEKABLE(outputStream); + if (!seekable) { + return -4; + } + + goffset pos = g_seekable_tell(seekable); + + return qint64(pos); + } + + const_cast(this)->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); + return -5; +} + +bool DFilePrivate::seek(qint64 pos, DFile::SeekType type) const +{ + GInputStream *inputStream = const_cast(this)->inputStream(); + if (inputStream) { + // seems g_seekable_can_seek only support local file, survey after. todo lanxs + gboolean canSeek = G_IS_SEEKABLE(inputStream) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; + if (!canSeek) { + return false; + } + + GSeekable *seekable = G_SEEKABLE(inputStream); + if (!seekable) { + return false; + } + + bool ret = false; + GError *gerror = nullptr; + GSeekType gtype = G_SEEK_CUR; + switch (type) { + case DFile::SeekType::kBegin: + gtype = G_SEEK_SET; + break; + case DFile::SeekType::kEnd: + gtype = G_SEEK_END; + break; + + default: + break; + } + + const_cast(this)->checkAndResetCancel(); + ret = g_seekable_seek(seekable, pos, gtype, const_cast(this)->cancellable, &gerror); + if (gerror) { + qCritical() << " seek err code = " << gerror->code + << " , seek err msg = " << gerror->message; + const_cast(this)->setErrorFromGError(gerror); + g_error_free(gerror); + } + + return ret; + } + + GOutputStream *out = const_cast(this)->outputStream(); + if (out) { + // seems g_seekable_can_seek only support local file, survey after. todo lanxs + gboolean canSeek = G_IS_SEEKABLE(out) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; + if (!canSeek) { + return false; + } + + GSeekable *seekable = G_SEEKABLE(out); + if (!seekable) { + return false; + } + + bool ret = false; + GError *gerror = nullptr; + GSeekType gtype = G_SEEK_CUR; + switch (type) { + case DFile::SeekType::kBegin: + gtype = G_SEEK_SET; + break; + case DFile::SeekType::kEnd: + gtype = G_SEEK_END; + break; + + default: + break; + } + + const_cast(this)->checkAndResetCancel(); + ret = g_seekable_seek(seekable, pos, gtype, const_cast(this)->cancellable, &gerror); + if (gerror) { + qCritical() << " seek err code = " << gerror->code + << " , seek err msg = " << gerror->message; + const_cast(this)->setErrorFromGError(gerror); + g_error_free(gerror); + } + + return ret; + } + + const_cast(this)->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); + return false; +} + +bool DFilePrivate::doOpenBySys(const int model, const int permissions) +{ + qDebug() << " DFilePrivate::doOpenBySys " << model << permissions << uri; + if (isOpen) { + qWarning() << "DFilePrivate::doOpenBySys file is opened!" << uri; + return true; + } + + if (fileCopyType != DFile::FileCopyType::kCopyTypeBySys) { + qWarning() << "DFilePrivate::doOpenBySys file copy type is not system read write"; + error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); + error.setMessage("file copy type is not system read write"); + return false; + } + + if (!uri.isLocalFile()){ + qWarning() << "DFilePrivate::doOpenBySys file is not local file"; + error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); + error.setMessage("file is not local file, not surport system read write"); + return false; + } + fileFd = ::open(uri.path().toUtf8().data(), model, permissions); + if (fileFd < 0) { + error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); + error.setMessage(strerror(errno)); + qWarning() << "DFilePrivate::doOpenBySys faild, error : " << error.errorMsg(); + return false; + } + isOpen = true; + return true; +} + +bool DFilePrivate::doCloseBySys() +{ + qDebug() << " DFilePrivate::doCloseBySys " << uri; + if (fileFd < 0) { + return true; + } + + int result = ::close(fileFd); + if (result != 0) { + error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); + error.setMessage(strerror(errno)); + qWarning() << "DFilePrivate::doCloseBySys faild, error : " << error.errorMsg(); + return false; + } + + fileFd = -1; + + return true; +} + +qint64 DFilePrivate::doWriteBySys(const char *data, const qint64 maxSize) +{ + qDebug() << " DFilePrivate::doWriteBySys " << maxSize << uri; + if (fileFd < 0) { + error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); + error.setMessage(strerror(errno)); + qWarning() << "DFilePrivate::doWriteBySys faild, error : " << error.errorMsg(); + return -1; + } + qint64 size = static_cast(write(fileFd, data, static_cast(maxSize))); + if (size < 0) { + error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); + error.setMessage(strerror(errno)); + qWarning() << "DFilePrivate::doWriteBySys faild, error : " << error.errorMsg(); + } else if (size < maxSize) { + qWarning() << "DFilePrivate::doWriteBySys realy write size not equire maxSize!"; + } + return size; +} + +qint64 DFilePrivate::doWriteBySys(const char *data) +{ + qDebug() << " DFilePrivate::doWriteBySys " << uri; + return doWriteBySys(data, static_cast(strlen(data))); +} + +qint64 DFilePrivate::doWriteBySys(const QByteArray &data) +{ + qDebug() << " DFilePrivate::doWriteBySys " << data.length() << uri; + return doWriteBySys(data.data(), data.length()); +} + +qint64 DFilePrivate::readBySys(char *data, qint64 maxSize) +{ + qDebug() << " DFilePrivate::readBySys " << maxSize << uri; + if (fileFd < 0) { + error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); + error.setMessage(strerror(errno)); + qWarning() << "DFilePrivate::readBySys faild, error : " << error.errorMsg(); + return -1; + } + + auto size = ::read(fileFd, data, static_cast(maxSize)); + if (size < 0) { + error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); + error.setMessage(strerror(errno)); + qWarning() << "DFilePrivate::readBySys faild, error : " << error.errorMsg(); + } + + return size; +} + +QByteArray DFilePrivate::readBySys(qint64 maxSize) +{ + char data[maxSize + 1]; + memset(&data, 0, static_cast(maxSize + 1)); + readBySys(data, maxSize); + return QByteArray(data); +} + +qint64 DFilePrivate::posBySys() const +{ + qDebug() << " DFilePrivate::posBySys " << uri << fileFd; + if (fileFd < 0) { + const_cast(this)->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); + const_cast(this)->error.setMessage("file fd is unvalid!"); + qWarning() << "DFilePrivate::posBySys file fd is unvalid!"; + return -1; + } + auto result = ::lseek(fileFd, 0, SEEK_CUR); + if (result < 0) { + const_cast(this)->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); + const_cast(this)->error.setMessage(strerror(errno)); + qWarning() << "DFilePrivate::posBySys faild, error : " << error.errorMsg(); + } + return result; +} + +bool DFilePrivate::seekBySys(const qint64 pos, const DFile::SeekType type) const +{ + qDebug() << " DFilePrivate::seekBySys " << uri << pos << int(type) << fileFd; + if (fileFd < 0) { + const_cast(this)->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); + const_cast(this)->error.setMessage("file fd is unvalid!"); + qWarning() << "DFilePrivate::seekBySys file fd is unvalid!"; + return false; + } + + int seekType = SEEK_CUR; + switch (type) { + case DFile::SeekType::kBegin: + seekType = SEEK_SET; + break; + case DFile::SeekType::kEnd: + seekType = SEEK_END; + break; + default: + seekType = SEEK_CUR; + } + auto result = ::lseek(fileFd, pos, seekType); + if (result < 0) { + const_cast(this)->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); + const_cast(this)->error.setMessage(strerror(errno)); + qWarning() << "DFilePrivate::seekBySys faild, error : " << error.errorMsg(); + return false; + } + + return true; +} + void DFilePrivate::readAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { ReadAsyncOp *data = static_cast(userData); @@ -643,6 +982,7 @@ void DFilePrivate::readAsyncFutureCallback(GObject *sourceObject, GAsyncResult * g_free(data); } + /************************************************ * DFile ***********************************************/ @@ -702,45 +1042,9 @@ bool DFile::exists() const qint64 DFile::pos() const { - GInputStream *inputStream = d->inputStream(); - if (inputStream) { - // seems g_seekable_can_seek only support local file, survey after. todo lanxs - gboolean canSeek = G_IS_SEEKABLE(inputStream) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; - if (!canSeek) { - return -1; - } - - GSeekable *seekable = G_SEEKABLE(inputStream); - if (!seekable) { - return -2; - } - - goffset pos = g_seekable_tell(seekable); - - return qint64(pos); - } - - GOutputStream *outputStream = d->outputStream(); - if (outputStream){ - // seems g_seekable_can_seek only support local file, survey after. todo lanxs - gboolean canSeek = G_IS_SEEKABLE(outputStream) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; - if (!canSeek) { - return -3; - } - - GSeekable *seekable = G_SEEKABLE(outputStream); - if (!seekable) { - return -4; - } - - goffset pos = g_seekable_tell(seekable); - - return qint64(pos); - } - - d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); - return -5; - + if (d->fileCopyType == FileCopyType::kCopyTypeBySys) + return d->posBySys(); + return d->pos(); } DFile::Permissions DFile::permissions() const @@ -770,23 +1074,54 @@ DFMIOError DFile::lastError() const return d->error; } +void DFile::setCopyType(const DFile::FileCopyType type) +{ + d->fileCopyType = type; +} + +void DFile::setSyncType(const DFile::FileCopySyncType type) +{ + d->fileSyncType = type; +} + +DFile::FileCopyType DFile::copyFileType() const +{ + return d->fileCopyType; +} + +DFile::FileCopySyncType DFile::syncType() const +{ + return d->fileSyncType; +} + bool DFile::open(DFile::OpenFlags mode) { + if (d->fileCopyType != FileCopyType::kCopyTypeByGioStream) { + d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); + d->error.setMessage("file copy type is not gio stream!"); + qWarning() << "DFile::open file copy type is not gio stream!"; + return false; + } + d->isOpen = d->doOpen(mode); return d->isOpen; } -bool DFile::close() +bool DFile::open(const int mode, const int permissions) { - if (d->isOpen) { - if (d->doClose()) - d->isOpen = false; - else - return false; + if (d->fileCopyType != DFile::FileCopyType::kCopyTypeBySys) { + d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); + d->error.setMessage("file copy type is not system read write!"); + qWarning() << "DFile::open current filecopy type != DFile::FileCopyType::kCopyTypeBySys"; + return false; } + return d->doOpenBySys(mode, permissions); +} - return true; +bool DFile::close() +{ + return d->doClose(); } bool DFile::cancel() @@ -798,94 +1133,45 @@ bool DFile::cancel() bool DFile::seek(qint64 pos, DFile::SeekType type) const { - GInputStream *inputStream = d->inputStream(); - if (inputStream) { - // seems g_seekable_can_seek only support local file, survey after. todo lanxs - gboolean canSeek = G_IS_SEEKABLE(inputStream) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; - if (!canSeek) { - return false; - } - - GSeekable *seekable = G_SEEKABLE(inputStream); - if (!seekable) { - return false; - } - - bool ret = false; - GError *gerror = nullptr; - GSeekType gtype = G_SEEK_CUR; - switch (type) { - case DFile::SeekType::kBegin: - gtype = G_SEEK_SET; - break; - case DFile::SeekType::kEnd: - gtype = G_SEEK_END; - break; - - default: - break; - } + if (d->fileCopyType == FileCopyType::kCopyTypeBySys) + return d->seekBySys(pos, type); - d->checkAndResetCancel(); - ret = g_seekable_seek(seekable, pos, gtype, d->cancellable, &gerror); - if (gerror) { - qCritical() << " seek err code = " << gerror->code - << " , seek err msg = " << gerror->message; - d->setErrorFromGError(gerror); - g_error_free(gerror); - } - - return ret; - } + return d->seek(pos, type); +} - GOutputStream *out = d->outputStream(); - if (out) { - // seems g_seekable_can_seek only support local file, survey after. todo lanxs - gboolean canSeek = G_IS_SEEKABLE(out) /*&& g_seekable_can_seek(G_SEEKABLE(inputStream))*/; - if (!canSeek) { +bool DFile::flush() +{ + if (d->fileCopyType == FileCopyType::kCopyTypeBySys) { + if (d->fileFd < 0) { + d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); + d->error.setMessage("file is not opened!"); return false; } - - GSeekable *seekable = G_SEEKABLE(out); - if (!seekable) { + // 使用 fsync 同步单个文件,而不是 syncfs 同步整个文件系统 + if (fsync(d->fileFd) != 0) { + d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_FAILED); + d->error.setMessage(strerror(errno)); return false; } - - bool ret = false; - GError *gerror = nullptr; - GSeekType gtype = G_SEEK_CUR; - switch (type) { - case DFile::SeekType::kBegin: - gtype = G_SEEK_SET; - break; - case DFile::SeekType::kEnd: - gtype = G_SEEK_END; - break; - - default: - break; + return true; + } + // ftp or not local file not surport kSyncBySys to sync + if (d->fileSyncType == FileCopySyncType::kSyncBySys && d->uri.isLocalFile()) { + if (d->fileFd < 0) { + d->fileFd = ::open(d->uri.path().toUtf8().data(), O_RDONLY); } - d->checkAndResetCancel(); - ret = g_seekable_seek(seekable, pos, gtype, d->cancellable, &gerror); - if (gerror) { - qCritical() << " seek err code = " << gerror->code - << " , seek err msg = " << gerror->message; - d->setErrorFromGError(gerror); - g_error_free(gerror); + if (d->fileFd < 0) { + qWarning() << "DFile::flush FileCopySyncType::kSyncBySys open file error : " << strerror(errno); + } else { + if (fsync(d->fileFd) == 0) { + return true; + } } - - return ret; } - d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); - return false; -} - -bool DFile::flush() -{ GOutputStream *outputStream = d->outputStream(); if (!outputStream) { d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); @@ -918,51 +1204,18 @@ bool DFile::setPermissions(Permissions permission) qint64 DFile::read(char *data, qint64 maxSize) { - GInputStream *inputStream = d->inputStream(); - if (!inputStream) { - d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); - return -1; - } + if (d->fileCopyType == DFile::FileCopyType::kCopyTypeBySys) + return d->readBySys(data, maxSize); - g_autoptr(GError) gerror = nullptr; - d->checkAndResetCancel(); - gssize read = g_input_stream_read(inputStream, - data, - static_cast(maxSize), - d->cancellable, - &gerror); - if (gerror) { - d->setErrorFromGError(gerror); - return -1; - } - - return read; + return d->read(data, maxSize); } QByteArray DFile::read(qint64 maxSize) { - GInputStream *inputStream = d->inputStream(); - if (!inputStream) { - d->error.setCode(DFMIOErrorCode::DFM_IO_ERROR_OPEN_FAILED); - return QByteArray(); - } + if (d->fileCopyType == DFile::FileCopyType::kCopyTypeBySys) + return d->readBySys(maxSize); - char data[maxSize + 1]; - memset(&data, 0, maxSize + 1); - - g_autoptr(GError) gerror = nullptr; - d->checkAndResetCancel(); - g_input_stream_read(inputStream, - data, - static_cast(maxSize), - d->cancellable, - &gerror); - if (gerror) { - d->setErrorFromGError(gerror); - return QByteArray(); - } - - return QByteArray(data); + return d->read(maxSize); } QByteArray DFile::readAll() @@ -986,6 +1239,9 @@ qint64 DFile::write(const char *data, qint64 len) return -1; } + if (d->fileCopyType == FileCopyType::kCopyTypeBySys) + return d->doWriteBySys(data, len); + return d->doWrite(data, len); } @@ -996,6 +1252,9 @@ qint64 DFile::write(const char *data) return -1; } + if (d->fileCopyType == FileCopyType::kCopyTypeBySys) + return d->doWriteBySys(data); + return d->doWrite(data); } @@ -1006,6 +1265,9 @@ qint64 DFile::write(const QByteArray &byteArray) return -1; } + if (d->fileCopyType == FileCopyType::kCopyTypeBySys) + return d->doWriteBySys(byteArray); + return d->doWrite(byteArray); } diff --git a/src/dfm-io/dfm-io/private/dfile_p.h b/src/dfm-io/dfm-io/private/dfile_p.h index 79a84fc5..cf904d7d 100644 --- a/src/dfm-io/dfm-io/private/dfile_p.h +++ b/src/dfm-io/dfm-io/private/dfile_p.h @@ -76,6 +76,20 @@ class DFilePrivate : public QObject qint64 doWrite(const char *data, qint64 maxSize); qint64 doWrite(const char *data); qint64 doWrite(const QByteArray &data); + qint64 read(char *data, qint64 maxSize); + QByteArray read(qint64 maxSize); + qint64 pos() const; + bool seek(qint64 pos, DFile::SeekType type = DFile::SeekType::kBegin) const; + + bool doOpenBySys(const int model, const int permissions); + bool doCloseBySys(); + qint64 doWriteBySys(const char *data, const qint64 maxSize); + qint64 doWriteBySys(const char *data); + qint64 doWriteBySys(const QByteArray &data); + qint64 readBySys(char *data, qint64 maxSize); + QByteArray readBySys(qint64 maxSize); + qint64 posBySys() const; + bool seekBySys(const qint64 pos, const DFile::SeekType type = DFile::SeekType::kBegin) const; static void readAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); static void readQAsyncCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); @@ -97,7 +111,10 @@ class DFilePrivate : public QObject DFMIOError error; QByteArray readAllAsyncRet; QUrl uri; + DFile::FileCopyType fileCopyType { DFile::FileCopyType::kCopyTypeByGioStream }; + DFile::FileCopySyncType fileSyncType { DFile::FileCopySyncType::kSyncByGioStream }; bool isOpen { false }; + int fileFd { -1 }; }; END_IO_NAMESPACE