Ticket #12424: 0009-RemoteFile-Add-Move-operation.patch

File 0009-RemoteFile-Add-Move-operation.patch, 8.3 KB (added by Roger Siddons <dizygotheca@…>, 5 years ago)
  • mythtv/libs/libmythbase/remotefile.cpp

    From 117046af0139d6717dc96cc7a5def88b1734c0a7 Mon Sep 17 00:00:00 2001
    From: Roger Siddons <dizygotheca@ntlworld.com>
    Date: Sat, 22 Aug 2015 14:56:17 +0100
    Subject: [PATCH 09/15] RemoteFile: Add Move operation
    
    Adds support for renaming files.
    Remote files can only be moved within the same storage group.
    Between local/remote locations the source is only deleted after being successfully copied.
    
    diff --git a/mythtv/libs/libmythbase/remotefile.cpp b/mythtv/libs/libmythbase/remotefile.cpp
    index 594a6eb..28721f7 100644
    a b bool RemoteFile::CopyFile (const QString& src, const QString& dst, 
    648648    return success;
    649649}
    650650
     651bool RemoteFile::MoveFile (const QString& src, const QString& dst, bool overwrite)
     652{
     653    LOG(VB_FILE, LOG_INFO,
     654        QString("RemoteFile::MoveFile: Moving file from '%1' to '%2'").arg(src).arg(dst));
     655
     656    // sanity check
     657    if (src == dst)
     658    {
     659        LOG(VB_GENERAL, LOG_ERR, "RemoteFile::MoveFile: Cannot move a file to itself");
     660        return false;
     661    }
     662
     663    if (isLocal(src) != isLocal(dst))
     664    {
     665        // Moving between local & remote requires a copy & delete
     666        bool ok = CopyFile(src, dst, overwrite, true);
     667        if (ok)
     668        {
     669            if (!DeleteFile(src))
     670                LOG(VB_FILE, LOG_ERR,
     671                    "RemoteFile::MoveFile: Failed to delete file after successful copy");
     672        }
     673        return ok;
     674    }
     675
     676    if (overwrite)
     677    {
     678        DeleteFile(dst);
     679    }
     680    else if (Exists(dst))
     681    {
     682        LOG(VB_GENERAL, LOG_ERR, "RemoteFile::MoveFile: File already exists");
     683        return false;
     684    }
     685
     686    if (isLocal(src))
     687    {
     688        // Moving local -> local
     689        QFileInfo fi(dst);
     690        if (QDir().mkpath(fi.path()) && QFile::rename(src, dst))
     691            return true;
     692
     693        LOG(VB_FILE, LOG_ERR, "RemoteFile::MoveFile: Rename failed");
     694        return false;
     695    }
     696
     697    // Moving remote -> remote
     698    QUrl srcUrl(src);
     699    QUrl dstUrl(dst);
     700
     701    if (srcUrl.userName() != dstUrl.userName())
     702    {
     703        LOG(VB_FILE, LOG_ERR, "RemoteFile::MoveFile: Cannot change a file's Storage Group");
     704        return false;
     705    }
     706
     707    QStringList strlist("MOVE_FILE");
     708    strlist << srcUrl.userName() << srcUrl.path() << dstUrl.path();
     709
     710    gCoreContext->SendReceiveStringList(strlist);
     711
     712    if (!strlist.isEmpty() && strlist[0] == "1")
     713        return true;
     714
     715    LOG(VB_FILE, LOG_ERR, QString("RemoteFile::MoveFile: MOVE_FILE failed with: %1")
     716        .arg(strlist.join(",")));
     717    return false;
     718}
     719
    651720void RemoteFile::Reset(void)
    652721{
    653722    if (isLocal())
  • mythtv/libs/libmythbase/remotefile.h

    diff --git a/mythtv/libs/libmythbase/remotefile.h b/mythtv/libs/libmythbase/remotefile.h
    index 42fd38e..7144e78 100644
    a b class MBASE_PUBLIC RemoteFile 
    4343                                    bool allowFallback = false);
    4444    static bool CopyFile(const QString &src, const QString &dest,
    4545                         bool overwrite = false, bool verify = false);
     46    static bool MoveFile(const QString &src, const QString &dest,
     47                         bool overwrite = false);
    4648
    4749    int Write(const void *data, int size);
    4850    int Read(void *data, int size);
  • mythtv/programs/mythbackend/mainserver.cpp

    diff --git a/mythtv/programs/mythbackend/mainserver.cpp b/mythtv/programs/mythbackend/mainserver.cpp
    index 656fa6e..b76534d 100644
    a b void MainServer::ProcessRequestWork(MythSocket *sock) 
    611611        else
    612612            HandleDeleteFile(listline, pbs);
    613613    }
     614    else if (command == "MOVE_FILE")
     615    {
     616        if (listline.size() < 4)
     617            SendErrorResponse(pbs, "Bad MOVE_FILE command");
     618        else
     619            HandleMoveFile(pbs, listline[1], listline[2], listline[3]);
     620    }
    614621    else if (command == "STOP_RECORDING")
    615622    {
    616623        HandleStopRecording(listline, pbs);
    void MainServer::GetFilesystemInfos(QList<FileSystemInfo> &fsInfos) 
    51545161    }
    51555162}
    51565163
     5164void MainServer::HandleMoveFile(PlaybackSock *pbs, const QString &storagegroup,
     5165                                const QString &src, const QString &dst)
     5166{
     5167    StorageGroup sgroup(storagegroup, "", false);
     5168    QStringList retlist;
     5169
     5170    if (src.isEmpty() || dst.isEmpty()
     5171        || src.contains("..") || dst.contains(".."))
     5172    {
     5173        LOG(VB_GENERAL, LOG_ERR, LOC +
     5174            QString("HandleMoveFile: ERROR moving file '%1' -> '%2', "
     5175                    "a path fails sanity checks").arg(src, dst));
     5176        retlist << "0" << "Invalid path";
     5177        SendResponse(pbs->getSocket(), retlist);
     5178        return;
     5179    }
     5180
     5181    QString srcAbs = sgroup.FindFile(src);
     5182    if (srcAbs.isEmpty())
     5183    {
     5184        LOG(VB_GENERAL, LOG_ERR, LOC +
     5185            QString("HandleMoveFile: Unable to find %1").arg(src));
     5186        retlist << "0" << "Source file not found";
     5187        SendResponse(pbs->getSocket(), retlist);
     5188        return;
     5189    }
     5190
     5191    // Path of files must be unique within SG. Rename will permit <sgdir1>/<dst>
     5192    // even when <sgdir2>/<dst> already exists.
     5193    // Directory paths do not have to be unique.
     5194    QString dstAbs = sgroup.FindFile(dst);
     5195    if (!dstAbs.isEmpty() && QFileInfo(dstAbs).isFile())
     5196    {
     5197        LOG(VB_GENERAL, LOG_ERR, LOC +
     5198            QString("HandleMoveFile: Destination exists at %1").arg(dstAbs));
     5199        retlist << "0" << "Destination file exists";
     5200        SendResponse(pbs->getSocket(), retlist);
     5201        return;
     5202    }
     5203
     5204    // Files never move filesystems, so use current SG dir
     5205    int sgPathSize = srcAbs.size() - src.size();
     5206    dstAbs = srcAbs.mid(0, sgPathSize) + dst;
     5207
     5208    // Renaming on same filesystem should always be fast but is liable to delays
     5209    // for unknowable reasons so we delegate to a separate thread for safety.
     5210    RenameThread *renamer = new RenameThread(*this, *pbs, srcAbs, dstAbs);
     5211    MThreadPool::globalInstance()->start(renamer, "Rename");
     5212}
     5213
     5214QMutex RenameThread::m_renamelock;
     5215
     5216void RenameThread::run()
     5217{
     5218    // Only permit one rename to run at any time
     5219    QMutexLocker lock(&m_renamelock);
     5220    LOG(VB_FILE, LOG_INFO, QString("MainServer::RenameThread: Renaming %1 -> %2")
     5221        .arg(m_src, m_dst));
     5222
     5223    QStringList retlist;
     5224    QFileInfo   fi(m_dst);
     5225
     5226    if (QDir().mkpath(fi.path()) && QFile::rename(m_src, m_dst))
     5227    {
     5228        retlist << "1";
     5229    }
     5230    else
     5231    {
     5232        retlist << "0" << "Rename failed";
     5233        LOG(VB_FILE, LOG_ERR, "MainServer::DoRenameThread: Rename failed");
     5234    }
     5235    m_ms.SendResponse(m_pbs.getSocket(), retlist);
     5236}
     5237
    51575238void TruncateThread::run(void)
    51585239{
    51595240    if (m_ms)
  • mythtv/programs/mythbackend/mainserver.h

    diff --git a/mythtv/programs/mythbackend/mainserver.h b/mythtv/programs/mythbackend/mainserver.h
    index 4e5e2dd..ce117d8 100644
    a b class TruncateThread : public QRunnable, public DeleteStruct 
    9292    void run(void);
    9393};
    9494
     95class RenameThread : public QRunnable
     96{
     97public:
     98    RenameThread(MainServer &ms, PlaybackSock &pbs, QString src, QString dst)
     99        : m_ms(ms), m_pbs(pbs), m_src(src), m_dst(dst) {}
     100    void run(void);
     101
     102private:
     103    static QMutex m_renamelock;
     104
     105    MainServer   &m_ms;
     106    PlaybackSock &m_pbs;
     107    QString       m_src, m_dst;
     108};
     109
    95110class MainServer : public QObject, public MythSocketCBs
    96111{
    97112    Q_OBJECT
    class MainServer : public QObject, public MythSocketCBs 
    99114    friend class DeleteThread;
    100115    friend class TruncateThread;
    101116    friend class FreeSpaceUpdater;
     117    friend class RenameThread;
    102118  public:
    103119    MainServer(bool master, int port,
    104120               QMap<int, EncoderLink *> *tvList,
    class MainServer : public QObject, public MythSocketCBs 
    147163    void GetActiveBackends(QStringList &hosts);
    148164    void HandleActiveBackendsQuery(PlaybackSock *pbs);
    149165    void HandleIsActiveBackendQuery(QStringList &slist, PlaybackSock *pbs);
     166    void HandleMoveFile(PlaybackSock *pbs, const QString &storagegroup,
     167                        const QString &src, const QString &dst);
    150168    bool HandleDeleteFile(QStringList &slist, PlaybackSock *pbs);
    151169    bool HandleDeleteFile(QString filename, QString storagegroup,
    152170                          PlaybackSock *pbs = NULL);