SpartanJ / efsw

efsw is a C++ cross-platform file system watcher and notifier.
Other
662 stars 101 forks source link

Cannot track symlinked file in Linux #145

Closed megumumpkin closed 2 years ago

megumumpkin commented 2 years ago

The file exists on the same folder as the program, as I did update the script file it won't detect the changes

megumumpkin commented 2 years ago

Already set the option for both followSymlinks and allowOutOfScopeLinks to true.

megumumpkin commented 2 years ago

Folders too

SpartanJ commented 2 years ago

Hi! What file system are you using? Is it a network file system (NFS, SMB)?

megumumpkin commented 2 years ago

I use ext4 on Linux.

SpartanJ commented 2 years ago

I think I'll need a minimum example to be able to reproduce it. I'm on the same setup and works just fine with the efsw test using followSymlinks and allowOutOfScopeLinks enabled and adding a symlink to a folder out of scope. If possible try to provide an example that creates that symlink and writes a file to the symlinked folder. Thanks.

megumumpkin commented 2 years ago

So first, this is the code I'm writing for checking file changes:

struct FSEvent{
    enum TYPE{
        ADD,
        MODIFY,
        DELETE
    };
    TYPE type;
    std::string filetype;
    std::string filepath;
};

wi::unordered_map<std::string, FSEvent> fsevents;
std::mutex event_sync;

class FSUpdateListener : public efsw::FileWatchListener
{
    void handleFileAction(efsw::WatchID watchid, const std::string& dir, const std::string& filename, efsw::Action action, std::string oldFilename) override
    {
        bool store = false;
        FSEvent::TYPE action_type;
        switch(action){
            case efsw::Actions::Add:
            {
                store = true;
                action_type = FSEvent::ADD;
                break;
            }
            case efsw::Actions::Modified:
            {
                store = true;
                action_type = FSEvent::MODIFY;
                break;
            }
            case efsw::Actions::Delete:
            {
                store = true;
                action_type = FSEvent::DELETE;
                break;
            }
            default:
            break;
        }
        std::scoped_lock lock (event_sync);
        fsevents[filename] = {
            action_type,
            wi::helper::toUpper(wi::helper::GetExtensionFromFileName(filename)),
            dir+filename,
        };
    }
};

std::shared_ptr<efsw::FileWatcher> filewatcher;
std::shared_ptr<FSUpdateListener> fwlistener;
efsw::WatchID watch_all;

void Game::LiveUpdate::Init()
{
    filewatcher = std::make_shared<efsw::FileWatcher>();
    fwlistener = std::make_shared<FSUpdateListener>();
    watch_all = filewatcher->addWatch(wi::helper::GetCurrentPath(), fwlistener.get(), true);
    filewatcher->watch();
}

void Game::LiveUpdate::Update(float dt)
{
    std::scoped_lock lock (event_sync);

    for(auto& pair_event : fsevents){
        wi::backlog::post("wiix");
        auto& event = pair_event.second;
        if(event.filetype == "LUA"){
            Game::ScriptBindings::LiveUpdate::PushEvent({
                (event.type == FSEvent::DELETE) ? Game::ScriptBindings::LiveUpdate::ScriptReloadEvent::UNLOAD : Game::ScriptBindings::LiveUpdate::ScriptReloadEvent::RELOAD,
                Game::ScriptBindings::LiveUpdate::ScriptReloadEvent::LUA,
                event.filepath
            });
        }
    }
    fsevents.clear();

    Resources::LiveUpdate::Update(dt);
    ScriptBindings::LiveUpdate::Update(dt);
}

Second, this is the file structure:

Program
script.lua <-- symlink to a file out of scope

Expected behavior: Program detects changes to the symlink file after the file is updated (saved over) from a text editor. What happened instead: Program does not detect file changes at all.

If i have a folder that is symlinked like this:

Program
FolderX <-- symlink to a folder out of scope
|-- script.lua

symlink is using ln -s script.lua

The result is the same, it won't react to the change at all.

Does efsw needs some specific file/folder flags for symlinked ones to work? Atleast for non symlink with those two structures above is working for me.

For the full repo i have it in here: https://github.com/megumumpkin/WickedEngine-Demo/ The code mentioned above is from LiveUpdate.cpp from Sources/ folder Program ran from build/ folder (exists after bulding)

SpartanJ commented 2 years ago

Oh. The first example shouldn't return any event, since the file descriptor of the symlink isn't modified, what's being modified is the file that it's being linked to. In that case, you will need to manually detect symlink files and add the folder that contains the real file. This is not a limitation of the library but how the file system works. The second example I'm not sure if I understand you, but, if the only symlink is the folder FolderX and script.lua is an actual file, it should work, but if script.lua is also a symlink, the same issue should happen.

megumumpkin commented 2 years ago

So I set up the liveupdate tracking like this:

void Game::LiveUpdate::Init()
{
    filewatcher = std::make_shared<efsw::FileWatcher>();
    fwlistener = std::make_shared<FSUpdateListener>();
    watch_all = filewatcher->addWatch(wi::helper::GetCurrentPath(), fwlistener.get(), true);
    filewatcher->followSymlinks(true);
    filewatcher->allowOutOfScopeLinks(true);
    filewatcher->watch();
}

Trying out the 2nd case, and it does not detect too.

For file links it needs to be hardlink and not symlink, and has to edit the hardlinked one, not the source it seems.

SpartanJ commented 2 years ago

The function call order is wrong. The configuration should be done before adding the watch (when the flags are used).

void Game::LiveUpdate::Init()
{
    filewatcher = std::make_shared<efsw::FileWatcher>();
    fwlistener = std::make_shared<FSUpdateListener>();
    filewatcher->followSymlinks(true);
    filewatcher->allowOutOfScopeLinks(true);
    watch_all = filewatcher->addWatch(wi::helper::GetCurrentPath(), fwlistener.get(), true);
    filewatcher->watch();
}
megumumpkin commented 2 years ago

Yeah, it works now, thanks :+1: