Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Why does QDir::move() fail where mv succeeds?
Qt 6.11 is out! See what's new in the release blog

Why does QDir::move() fail where mv succeeds?

Scheduled Pinned Locked Moved Solved General and Desktop
15 Posts 4 Posters 9.3k Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V Offline
    V Offline
    Vadi2
    wrote on last edited by Vadi2
    #1

    Given this code, QDir::move fails - but calling mv right after it as a process works:

                // FIXME don't hardcode name in case we want to change it
                QFileInfo fi("/tmp/Mudlet.AppImage");
                qDebug() << "gotta move " << fi.filePath() << "to" << QCoreApplication::applicationFilePath();
                if (!QDir().rename(fi.filePath(), QCoreApplication::applicationFilePath())) {
                    qDebug() << "replacing Mudlet with new version failed, original here:" << fi.exists();
    
                    QProcess mv;
                    mv.setProcessChannelMode(QProcess::MergedChannels);
                    mv.start("mv",  QStringList() << fi.filePath() << QCoreApplication::applicationFilePath());
                    if (!mv.waitForFinished()) {
                        qDebug() << "Moving failed:" << mv.errorString();
                    } else {
                        qDebug() << "moving file with mv worked!";
                    }
                }
    
    gotta move  "/tmp/Mudlet.AppImage" to "/home/vadi/Programs/Mudlet/mudlet/build-mudlet-Desktop_Qt_5_9_0_Clang_64bit-Debug/mudlet"
    replacing Mudlet with new version failed, original here: true
    moving file with mv worked!
    

    This is on Ubuntu 17.04. I'd rather use the built-in function than spawning a process here, any idea why it's not working?

    1 Reply Last reply
    0
    • sierdzioS Offline
      sierdzioS Offline
      sierdzio
      Moderators
      wrote on last edited by
      #2

      See the documentation. It states that QDir::rename() will fail if destination file exists, which is true in your case (you are trying to replace the application executable if I am not mistaken).

      (Z(:^

      1 Reply Last reply
      1
      • V Offline
        V Offline
        Vadi2
        wrote on last edited by
        #3

        It says "on most filesystems":

        On most file systems, rename() fails only if oldName does not exist, or if a file with the new name already exists

        I'm on a filesystem where this does not fail since mv works fine doing just the same!

        sierdzioS JonBJ 2 Replies Last reply
        0
        • V Vadi2

          It says "on most filesystems":

          On most file systems, rename() fails only if oldName does not exist, or if a file with the new name already exists

          I'm on a filesystem where this does not fail since mv works fine doing just the same!

          sierdzioS Offline
          sierdzioS Offline
          sierdzio
          Moderators
          wrote on last edited by
          #4

          @Vadi2 said in Why does QDir::move() fail where mv succeeds?:

          It says "on most filesystems":

          On most file systems, rename() fails only if oldName does not exist, or if a file with the new name already exists

          I'm on a filesystem where this does not fail since mv works fine doing just the same!

          If it didn't fail you wouldn't have created this thread, right? It does not matter if mv works or not. QDir does not work in this case (it doesn't use mv), and the documentation warns about it. You can try reporting this as a bug if you feel it is one. Or find another working solution (like running mv or removing the destination file before moving the updated one).

          (Z(:^

          1 Reply Last reply
          2
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #5

            Hi,

            To add to @sierdzio, mv just overwrites unless you added the -i where you'll be prompted if you try to overwrite something. Qt is on the safe side by not allowing you to overwrite files blindly.

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            1 Reply Last reply
            2
            • V Offline
              V Offline
              Vadi2
              wrote on last edited by
              #6

              Ah, yeah, thanks - deleting the file first worked.

              Is it possible to move and keep the executable bit?

              sierdzioS 1 Reply Last reply
              0
              • V Vadi2

                Ah, yeah, thanks - deleting the file first worked.

                Is it possible to move and keep the executable bit?

                sierdzioS Offline
                sierdzioS Offline
                sierdzio
                Moderators
                wrote on last edited by
                #7

                @Vadi2 said in Why does QDir::move() fail where mv succeeds?:

                Ah, yeah, thanks - deleting the file first worked.

                Nice :)

                Is it possible to move and keep the executable bit?

                You can use QFile::setPermissions for that. To make sure you keep exactly the same permissions, you can read them from old file and then set the same on the new one.

                (Z(:^

                1 Reply Last reply
                2
                • V Offline
                  V Offline
                  Vadi2
                  wrote on last edited by
                  #8

                  Worked great. Thanks!

                  1 Reply Last reply
                  1
                  • V Vadi2

                    It says "on most filesystems":

                    On most file systems, rename() fails only if oldName does not exist, or if a file with the new name already exists

                    I'm on a filesystem where this does not fail since mv works fine doing just the same!

                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by JonB
                    #9

                    @Vadi2 said in Why does QDir::move() fail where mv succeeds?:

                    It says "on most filesystems":

                    On most file systems, rename() fails only if oldName does not exist, or if a file with the new name already exists

                    I'm on a filesystem where this does not fail since mv works fine doing just the same!

                    Just for the record: Linux mv is a program, not an operating system call, which can (and does) do whatever it likes.

                    Given that all you seem to want to do is move a file to another (possibly pre-existing) file, you could just use the native OS call (much preferable to running the mv program) under Linux from your code instead of QDir()::rename():

                    int rename(const char *oldpath, const char *newpath);
                    

                    Run man 2 rename:

                    If newpath already exists, it will be atomically replaced

                    P.S.
                    @sierdzio said in Why does QDir::move() fail where mv succeeds?:

                    @Vadi2 said in Why does QDir::move() fail where mv succeeds?:

                    Ah, yeah, thanks - deleting the file first worked.

                    Nice :)

                    Is it possible to move and keep the executable bit?

                    You can use QFile::setPermissions for that. To make sure you keep exactly the same permissions, you can read them from old file and then set the same on the new one.

                    I'm afraid this is not a good thing to attempt to do! There are many reasons why attempting to place the same permissions/attributes on a file from those on another file will fail. If you wish to preserve permissions/attributes etc. , you are much better using OS call rename() than all of this.

                    sierdzioS 1 Reply Last reply
                    0
                    • JonBJ JonB

                      @Vadi2 said in Why does QDir::move() fail where mv succeeds?:

                      It says "on most filesystems":

                      On most file systems, rename() fails only if oldName does not exist, or if a file with the new name already exists

                      I'm on a filesystem where this does not fail since mv works fine doing just the same!

                      Just for the record: Linux mv is a program, not an operating system call, which can (and does) do whatever it likes.

                      Given that all you seem to want to do is move a file to another (possibly pre-existing) file, you could just use the native OS call (much preferable to running the mv program) under Linux from your code instead of QDir()::rename():

                      int rename(const char *oldpath, const char *newpath);
                      

                      Run man 2 rename:

                      If newpath already exists, it will be atomically replaced

                      P.S.
                      @sierdzio said in Why does QDir::move() fail where mv succeeds?:

                      @Vadi2 said in Why does QDir::move() fail where mv succeeds?:

                      Ah, yeah, thanks - deleting the file first worked.

                      Nice :)

                      Is it possible to move and keep the executable bit?

                      You can use QFile::setPermissions for that. To make sure you keep exactly the same permissions, you can read them from old file and then set the same on the new one.

                      I'm afraid this is not a good thing to attempt to do! There are many reasons why attempting to place the same permissions/attributes on a file from those on another file will fail. If you wish to preserve permissions/attributes etc. , you are much better using OS call rename() than all of this.

                      sierdzioS Offline
                      sierdzioS Offline
                      sierdzio
                      Moderators
                      wrote on last edited by
                      #10

                      @JNBarchan said in Why does QDir::move() fail where mv succeeds?:

                      I'm afraid this is not a good thing to attempt to do! There are many reasons why attempting to place the same permissions/attributes on a file from those on another file will fail. If you wish to preserve permissions/attributes etc. , you are much better using OS call rename() than all of this.

                      Rename does not preserve any permissions of the target file, it will preserve the perms of source file. I don't think that is what OP wants.

                      Yes, there are ways in which it can fail.

                      IMO using OS calls everywhere by hand is not necessary, at least in this case, Qt already does that behind the scenes - and in a cross-platform way.

                      (Z(:^

                      JonBJ 1 Reply Last reply
                      1
                      • sierdzioS sierdzio

                        @JNBarchan said in Why does QDir::move() fail where mv succeeds?:

                        I'm afraid this is not a good thing to attempt to do! There are many reasons why attempting to place the same permissions/attributes on a file from those on another file will fail. If you wish to preserve permissions/attributes etc. , you are much better using OS call rename() than all of this.

                        Rename does not preserve any permissions of the target file, it will preserve the perms of source file. I don't think that is what OP wants.

                        Yes, there are ways in which it can fail.

                        IMO using OS calls everywhere by hand is not necessary, at least in this case, Qt already does that behind the scenes - and in a cross-platform way.

                        JonBJ Offline
                        JonBJ Offline
                        JonB
                        wrote on last edited by JonB
                        #11

                        @sierdzio

                        @Vadi2 said in Why does QDir::move() fail where mv succeeds?:

                        Is it possible to move and keep the executable bit?

                        I read that as he wants to preserve the source's attributes/permissions (the source is an executable), surely? I don't know what QDir::move() does, since the docs don't say.... It does also talk about possibly implementing the rename via copy & delete in whatever circumstances, I don't know what that might do about attributes/permissions.

                        sierdzioS 1 Reply Last reply
                        0
                        • JonBJ JonB

                          @sierdzio

                          @Vadi2 said in Why does QDir::move() fail where mv succeeds?:

                          Is it possible to move and keep the executable bit?

                          I read that as he wants to preserve the source's attributes/permissions (the source is an executable), surely? I don't know what QDir::move() does, since the docs don't say.... It does also talk about possibly implementing the rename via copy & delete in whatever circumstances, I don't know what that might do about attributes/permissions.

                          sierdzioS Offline
                          sierdzioS Offline
                          sierdzio
                          Moderators
                          wrote on last edited by
                          #12

                          @JNBarchan said in Why does QDir::move() fail where mv succeeds?:

                          I don't know what QDir::move() does, since the docs don't say....

                          True.

                          (Z(:^

                          1 Reply Last reply
                          0
                          • V Offline
                            V Offline
                            Vadi2
                            wrote on last edited by
                            #13

                            Yeah, I want to keep the permissions of the original file. This operation will be specific to the Linux platform, so I'll try the system call - seems more efficient.

                            JonBJ 1 Reply Last reply
                            0
                            • V Vadi2

                              Yeah, I want to keep the permissions of the original file. This operation will be specific to the Linux platform, so I'll try the system call - seems more efficient.

                              JonBJ Offline
                              JonBJ Offline
                              JonB
                              wrote on last edited by
                              #14

                              @Vadi2
                              Just a couple of observations:

                              • You are moving a file from /tmp to somewhere else. It's possible that is mounted on a different file system (e.g. run df) from your destination, in which case the rename will actually be a copy + delete instead of a simple move.

                              • "I want to keep the permissions of the original file" Which do you mean by "original"? Depending, you may actually need code to copy across permissions/attributes after all.

                              • You are replacing the executable file you are actually running, while it is running. This is a very strange thing to do! (I would have expected you would do this from a separate application.) Linux may let you do it, I doubt Windows would. Just saying.

                              • qDebug() << "replacing Mudlet with new version failed, original here:" << fi.exists(); should be fi.filePath() in place of fi.exists() :)

                              1 Reply Last reply
                              0
                              • V Offline
                                V Offline
                                Vadi2
                                wrote on last edited by
                                #15

                                Hello!

                                • Okay, sounds good
                                • I was thinking of the downloaded file in the tar, but after more thought, I just want it to be executable while not flagrantly violating security rules
                                • I know, I don't want to deal with the hassle of another executable and I'm glad Linux allows you this. Windows doens't, so that will be a pain in the butt.
                                • ah yeah, I have better code here now:
                                void Updater::silentlyUpdate() const
                                {
                                    QObject::connect(feed, &dblsqd::Feed::ready, [=]() { feed->downloadRelease(feed->getUpdates().first()); });
                                
                                    QObject::connect(feed, &dblsqd::Feed::downloadFinished, [=]() {
                                        auto file = feed->getDownloadFile();
                                
                                        QFuture<void> future = QtConcurrent::run(this, &Updater::untarOnLinux, file->fileName());
                                
                                        // replace current binary with the unzipped one
                                        auto watcher = new QFutureWatcher<void>;
                                        connect(watcher, &QFutureWatcher<void>::finished, this, &Updater::updateBinaryOnLinux);
                                        watcher->setFuture(future);
                                    });
                                }
                                
                                void Updater::untarOnLinux(const QString& fileName) const
                                {
                                    QProcess tar;
                                    tar.setProcessChannelMode(QProcess::MergedChannels);
                                    // we can assume tar to be present on a Linux system. If it's not, it'd be rather broken.
                                    tar.start("tar",
                                              QStringList() << "-xvf" << fileName << "-C"
                                                            << QStandardPaths::writableLocation(QStandardPaths::TempLocation) << "/");
                                    if (!tar.waitForFinished()) {
                                        qDebug() << "Untarring" << fileName << "failed:" << tar.errorString();
                                    } else {
                                        qDebug() << "Tar output:" << tar.readAll().trimmed();
                                    }
                                }
                                
                                void Updater::updateBinaryOnLinux() const
                                { // FIXME don't hardcode name in case we want to change it
                                    QFileInfo unzippedBinary(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/Mudlet.AppImage");
                                    QString installedBinaryPath(QCoreApplication::applicationFilePath());
                                
                                    auto executablePermissions = unzippedBinary.permissions();
                                    executablePermissions |= QFileDevice::ExeOwner | QFileDevice::ExeUser;
                                
                                    QDir dir;
                                    if (!(dir.remove(installedBinaryPath) && QDir().rename(unzippedBinary.filePath(), installedBinaryPath))) {
                                        qDebug() << "updating" << installedBinaryPath << "with new version from" << unzippedBinary.filePath() << "failed";
                                        return;
                                    }
                                
                                    QFile updatedBinary(QCoreApplication::applicationFilePath());
                                    if (!updatedBinary.setPermissions(executablePermissions)) {
                                        qDebug() << "couldn't executable permissions on updated Mudlet binary at" << installedBinaryPath;
                                        return;
                                    }
                                
                                    qDebug() << "Successfully updated Mudlet to" << feed->getUpdates().first().getVersion();
                                }
                                
                                1 Reply Last reply
                                0

                                • Login

                                • Login or register to search.
                                • First post
                                  Last post
                                0
                                • Categories
                                • Recent
                                • Tags
                                • Popular
                                • Users
                                • Groups
                                • Search
                                • Get Qt
                                • Unsolved