1
votes

I am trying to use the following code to open the existing file to append data at it's end:

void AddPharmacyForm::addInsertToFile(QString insert)
{
    QFile inserts(":/new/prefix1/insertstatements.txt");

    if(!inserts.exists())
        qDebug() << "File does not exist";

    if(inserts.isOpen())
        qDebug() << "file is open";

    if(inserts.open(QFile::ReadWrite | QFile::Text))
    {
        // Another workaround- could not open file with append flag
        qDebug() << "im here!";

        QString currentInserts;
        QTextStream out(&inserts);

        out >> currentInserts;
        out << endl << insert;

        inserts.close();
    }
    else
    {
        QMessageBox::information(this, tr("Error"), tr("Cannot add new pharmacy! "
                                                       "Please contact program designer."
                                                        ));

        qDebug() << "error code: " + QString::number(inserts.error());

        return;
    }
}

The output of this code is the QMessageBox with the error and in qDebug it produces following line:

"error code: 5"

It does not give notice about file not existing and file being open. I have also tried opening file with different flags: QFile::ReadWrite, QFile::append, QFile::WriteOnly and the same modes within QIODevice. The error code is still the same. When I am opening the file from another class, the file opens without errors (it is not an access error).

What might be causing this problem?

2
By using ":/" do you mean you want to write a qrc bundled file ?? I don't think that is possible at all.Massimo Callegari
So the files included in qrc are read-only? There is no way to edit them within program context?Kokos34
You are referring to pre-compiled resource file, that can only be accessed for reading. If you want to output, then you need to create local copy on disc and write you content into it.Dmitriy

2 Answers

4
votes

There's no support for writing into the resource system, whether implemented using Qt's resource system or native to the platform. Your application typically has no right to modify its own executable, or the application bundle, or its installation location - it'd be a security risk if it did since bugs in networking code could be easily exploited to infect your user's system. So what you're trying to do is just a bad idea.

Instead, store the modified resources in your application's data folder, and revert to reading from the resource if the file doesn't exist. It is also probably not very wise to append to a file if the file is small: such appends are not atomic and can partially fail, leaving the file corrupted. Using a QSaveFile is guaranteed to either completely succeed or to fail without modifying any data.

An example implementation follows. The src.close() is not necessary to close the file, as QFile will automatically close upon destruction, as it is a proper resource-managing C++ class. By closing it earlier we ensure minimal use of the file descriptor - a finite system resource.

// https://github.com/KubaO/stackoverflown/tree/master/questions/resource-bypass-43044268
#include <QtCore>

const char kInsertsFile[] = ":/insertstatements.txt";

QString toWritableName(const QString & qrcFileName) {
   Q_ASSERT (qrcFileName.startsWith(":/"));
   QFileInfo info(qrcFileName);
   return
         QStandardPaths::writableLocation(QStandardPaths::DataLocation)
         + info.path().mid(1) + '/' + info.fileName();
}

QString toReadableName(const QString & qrcFileName) {
   Q_ASSERT (qrcFileName.startsWith(":/"));
   auto writable = toWritableName(qrcFileName);
   return QFileInfo(writable).exists() ? writable : qrcFileName;
}

bool setupWritableFile(QSaveFile & dst, QIODevice::OpenMode mode = {}) {
   Q_ASSERT (dst.fileName().startsWith(":/"));
   Q_ASSERT (mode == QIODevice::OpenMode{} || mode == QIODevice::Text);
   QFile src(toReadableName(dst.fileName()));
   dst.setFileName(toWritableName(dst.fileName()));
   if (!src.open(QIODevice::ReadOnly | mode))
      return false;
   auto data = src.readAll();
   src.close(); // Don't keep the file descriptor tied up any longer.
   QFileInfo dstInfo(dst.fileName());
   if (!dstInfo.dir().exists() && !QDir().mkpath(dstInfo.path()))
      return false;
   if (!dst.open(QIODevice::WriteOnly | mode))
      return false;
   return dst.write(data) == data.size();
}

bool addInsertToFile(const QString & insert) {
   QSaveFile file(kInsertsFile);
   if (!setupWritableFile(file, QIODevice::Text))
      return false;
   if (true) {
      // Alternative 1
      QTextStream s(&file);
      s << insert << '\n';
   } else {
      // Alternative 2
      file.write((insert + '\n').toLocal8Bit());
   }
   return file.commit();
}

QStringList readInserts() {
   QFile file(toReadableName(kInsertsFile));
   if (!file.open(QIODevice::ReadOnly))
      return {};
   return QString::fromLocal8Bit(file.readAll()).split('\n', QString::SkipEmptyParts);
}

int main(int argc, char ** argv) {
   QCoreApplication app{argc, argv};
   app.setApplicationName("resource-bypass-42044268");
   qDebug() << "Original Inserts:" << readInserts();
   auto rc = addInsertToFile("NewInsert");
   qDebug() << "Modification status:" << rc;
   qDebug() << "Current Inserts:" << readInserts();
}
3
votes

When you use the Qt Resource System (qrc files) to add files for your project, they are compiled directly into the binary of your application, so are therefore readonly. As the documentation states: -

Resource data can either be compiled into the binary and thus accessed immediately in application code, or a binary resource can be created and at a later point in application code registered with the resource system.

And...

Currently, Qt always stores the data directly in the executable, even on Windows, macOS, and iOS, where the operating system provides native support for resources. This might change in a future Qt release.