Ticket #2641: vfs_DELETED_folders+fix.patch

File vfs_DELETED_folders+fix.patch, 11.0 KB (added by leper, 4 years ago)

Support .DELETED, fix .DELETED when mounting a lower priority mod.

  • source/lib/file/vfs/tests/test_vfs_tree.h

     
    1 /* Copyright (c) 2013 Wildfire Games
     1/* Copyright (c) 2016 Wildfire Games
    22 *
    33 * Permission is hereby granted, free of charge, to any person obtaining
    44 * a copy of this software and associated documentation files (the
     
    2222
    2323#include "lib/self_test.h"
    2424
    25 #include "lib/file/vfs/vfs_tree.h"
    2625#include "lib/file/common/file_loader.h"
     26#include "lib/file/vfs/vfs.h"
     27#include "lib/file/vfs/vfs_lookup.h"
     28#include "lib/file/vfs/vfs_tree.h"
    2729
    2830class MockLoader : public IFileLoader
    2931{
     
    4345
    4446class TestVfsTree : public CxxTest::TestSuite
    4547{
     48    /**
     49     * We create a few different "mods" here to test proper .DELETED
     50     * behavior.
     51     *
     52     * To check which file is used we use the priority.
     53     *
     54     * 1
     55     * +--a
     56     * +--b/
     57     * |  +--a
     58     * |  \--b/
     59     * |     \--a
     60     * \--c/
     61     *    +--a
     62     *    \--b
     63     *
     64     * 2
     65     * +--a.DELETED
     66     * +--b/
     67     * |  +--a
     68     * |  \--b.DELETED
     69     * +--c.DELETED
     70     * \--c/
     71     *    +--a
     72     *    \--b
     73     *
     74     * 3
     75     * +--a
     76     * \--b/
     77     *    \--b/
     78     *       \--a
     79     */
     80    void mount_mod(size_t mod, VfsDirectory& dir)
     81    {
     82        size_t priority = mod;
     83        PIFileLoader loader(new MockLoader(1));
     84        switch(mod)
     85        {
     86        case 1:
     87        {
     88            dir.AddFile(VfsFile("a", 0, 0, priority, loader));
     89            VfsDirectory* b = dir.AddSubdirectory("b");
     90                b->AddFile(VfsFile("a", 0, 0, priority, loader));
     91                VfsDirectory* b_b = b->AddSubdirectory("b");
     92                    b_b->AddFile(VfsFile("a", 0, 0, priority, loader));
     93            VfsDirectory* c = dir.AddSubdirectory("c");
     94                c->AddFile(VfsFile("a", 0, 0, priority, loader));
     95                c->AddFile(VfsFile("b", 0, 0, priority, loader));
     96            break;
     97        }
     98        case 2:
     99        {
     100            dir.DeleteSubtree(VfsFile("a.DELETED", 0, 0, priority, loader));
     101            VfsDirectory* b = dir.AddSubdirectory("b");
     102                b->AddFile(VfsFile("a", 0, 0, priority, loader));
     103                b->DeleteSubtree(VfsFile("b.DELETED", 0, 0, priority, loader));
     104            dir.DeleteSubtree(VfsFile("c.DELETED", 0, 0, priority, loader));
     105            VfsDirectory* c = dir.AddSubdirectory("c");
     106                c->AddFile(VfsFile("a", 0, 0, priority, loader));
     107                c->AddFile(VfsFile("b", 0, 0, priority, loader));
     108            break;
     109        }
     110        case 3:
     111        {
     112            dir.AddFile(VfsFile("a", 0, 0, priority, loader));
     113            VfsDirectory* b = dir.AddSubdirectory("b");
     114                VfsDirectory* b_b = b->AddSubdirectory("b");
     115                    b_b->AddFile(VfsFile("a", 0, 0, priority, loader));
     116            break;
     117        }
     118        NODEFAULT;
     119        }
     120    }
     121
     122    void check_priority(VfsDirectory& root, const VfsPath& path, size_t priority)
     123    {
     124        VfsDirectory* dir; VfsFile* file;
     125        TS_ASSERT_OK(vfs_Lookup(path, &root, dir, &file, VFS_LOOKUP_SKIP_POPULATE));
     126        TS_ASSERT_EQUALS(file->Priority(), priority);
     127    }
     128
     129    void file_does_not_exists(VfsDirectory& root, const VfsPath& path)
     130    {
     131        VfsDirectory* dir; VfsFile* file;
     132        TS_ASSERT_EQUALS(vfs_Lookup(path, &root, dir, &file, VFS_LOOKUP_SKIP_POPULATE), ERR::VFS_FILE_NOT_FOUND);
     133    }
     134
     135    void directory_exists(VfsDirectory& root, const VfsPath& path, Status error = INFO::OK)
     136    {
     137        VfsDirectory* dir;
     138        TS_ASSERT_EQUALS(vfs_Lookup(path, &root, dir, nullptr, VFS_LOOKUP_SKIP_POPULATE), error);
     139    }
     140
    46141public:
    47142    void test_replacement()
    48143    {
     
    62157        TS_ASSERT_EQUALS(dir.AddFile(file2)->MTime(), file2.MTime());
    63158        TS_ASSERT_EQUALS(dir.AddFile(file1)->MTime(), file2.MTime());
    64159    }
     160
     161    void test_deleted()
     162    {
     163        VfsDirectory dir;
     164
     165        mount_mod(1, dir);
     166        mount_mod(2, dir);
     167        file_does_not_exists(dir, "a");
     168        check_priority(dir, "b/a", 2);
     169        directory_exists(dir, "b/b/", ERR::VFS_DIR_NOT_FOUND);
     170        directory_exists(dir, "c/");
     171        check_priority(dir, "c/a", 2);
     172        check_priority(dir, "c/b", 2);
     173        dir.Clear();
     174
     175
     176        mount_mod(1, dir);
     177        mount_mod(2, dir);
     178        mount_mod(3, dir);
     179        check_priority(dir, "a", 3);
     180        check_priority(dir, "b/b/a", 3);
     181        dir.Clear();
     182
     183
     184        mount_mod(1, dir);
     185        mount_mod(3, dir);
     186        mount_mod(2, dir);
     187        check_priority(dir, "a", 3);
     188        check_priority(dir, "b/b/a", 3);
     189        dir.Clear();
     190    }
    65191};
  • source/lib/file/vfs/vfs_populate.cpp

     
    1 /* Copyright (c) 2013 Wildfire Games
     1/* Copyright (c) 2016 Wildfire Games
    22 *
    33 * Permission is hereby granted, free of charge, to any person obtaining
    44 * a copy of this software and associated documentation files (the
     
    5858        DirectoryNames subdirectoryNames; subdirectoryNames.reserve(50);
    5959        RETURN_STATUS_IF_ERR(GetDirectoryEntries(m_realDirectory->Path(), &files, &subdirectoryNames));
    6060
    61         // to support .DELETED files inside archives safely, we need to load
    62         // archives and loose files in a deterministic order in case they add
    63         // and then delete the same file (or vice versa, depending on loading
    64         // order). GetDirectoryEntries has undefined order so sort its output
    65         std::sort(files.begin(), files.end(), CompareFileInfoByName());
    66         std::sort(subdirectoryNames.begin(), subdirectoryNames.end());
    67 
     61        // Since .DELETED files only remove files in lower priority mods
     62        // loose files and archive files have no conflicts so we do not need
     63        // to sort them.
     64        // We add directories after they might have been removed by .DELETED
     65        // files (as they did not contain any files at that point). The order
     66        // of GetDirectoryEntries is undefined, but that does not really matter (TODO really?)
     67        // so we do not need to sort its output.
    6868        RETURN_STATUS_IF_ERR(AddFiles(files));
    6969        AddSubdirectories(subdirectoryNames);
    7070
     
    7575    void AddFile(const CFileInfo& fileInfo) const
    7676    {
    7777        const VfsPath name = fileInfo.Name();
     78        const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory);
    7879        if(name.Extension() == L".DELETED")
    7980        {
    80             m_directory->RemoveFile(name.Basename());
     81            m_directory->DeleteSubtree(file);
    8182            if(!(m_realDirectory->Flags() & VFS_MOUNT_KEEP_DELETED))
    8283                return;
    8384        }
    8485
    85         const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory);
    8686        m_directory->AddFile(file);
    8787    }
    8888
     
    9797        WARN_IF_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags));
    9898
    9999        const VfsPath name = fileInfo.Name();
     100        const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile);
    100101        if(name.Extension() == L".DELETED")
    101102        {
    102             directory->RemoveFile(name.Basename());
     103            directory->DeleteSubtree(file);
    103104            if(!(this_->m_realDirectory->Flags() & VFS_MOUNT_KEEP_DELETED))
    104105                return;
    105106        }
    106107
    107         const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile);
    108108        directory->AddFile(file);
    109109    }
    110110
  • source/lib/file/vfs/vfs_tree.cpp

     
    1 /* Copyright (c) 2013 Wildfire Games
     1/* Copyright (c) 2016 Wildfire Games
    22 *
    33 * Permission is hereby granted, free of charge, to any person obtaining
    44 * a copy of this software and associated documentation files (the
     
    4949{
    5050}
    5151
     52static bool ShouldDelete(const VfsFile& file, const VfsFile& deletedFile)
     53{
     54    // We only check priority here, a .DELETED file in a mod should not
     55    // delete files in that mod. For the same reason we ignore loose
     56    // .DELETED files next to an archive.
     57    return file.Priority() < deletedFile.Priority();
     58}
    5259
    5360static bool ShouldReplaceWith(const VfsFile& previousFile, const VfsFile& newFile)
    5461{
     
    7885}
    7986
    8087
     88void VfsDirectory::DeleteSubtree(const VfsFile& file)
     89{
     90    ENSURE(file.Name().Extension() == L".DELETED");
     91
     92    const VfsPath basename = file.Name().Basename();
     93    std::map<VfsPath, VfsFile>::iterator fit = m_files.find(basename);
     94    if(fit != m_files.end() && ShouldDelete(fit->second, file))
     95        m_files.erase(basename);
     96
     97    std::map<VfsPath, VfsDirectory>::iterator dit = m_subdirectories.find(basename);
     98    if(dit != m_subdirectories.end() && dit->second.DeleteTree(file))
     99        m_subdirectories.erase(dit);
     100}
     101
     102bool VfsDirectory::DeleteTree(const VfsFile& file)
     103{
     104    for(std::map<VfsPath, VfsFile>::iterator it = m_files.begin(); it != m_files.end();)
     105        if(ShouldDelete(it->second, file))
     106            it = m_files.erase(it);
     107        else
     108            ++it;
     109
     110    for(std::map<VfsPath, VfsDirectory>::iterator it = m_subdirectories.begin(); it != m_subdirectories.end();)
     111        if(it->second.DeleteTree(file))
     112            it = m_subdirectories.erase(it);
     113        else
     114            ++it;
     115
     116    return m_files.empty() && m_subdirectories.empty();
     117}
     118
     119
    81120VfsFile* VfsDirectory::AddFile(const VfsFile& file)
    82121{
    83122    std::pair<VfsPath, VfsFile> value = std::make_pair(file.Name(), file);
     
    122161    return &it->second;
    123162}
    124163
    125 
    126164VfsDirectory* VfsDirectory::GetSubdirectory(const VfsPath& name)
    127165{
    128166    VfsSubdirectories::iterator it = m_subdirectories.find(name.string());
  • source/lib/file/vfs/vfs_tree.h

     
    1 /* Copyright (c) 2010 Wildfire Games
     1/* Copyright (c) 2016 Wildfire Games
    22 *
    33 * Permission is hereby granted, free of charge, to any person obtaining
    44 * a copy of this software and associated documentation files (the
     
    7777
    7878class VfsDirectory
    7979{
     80    /**
     81     * remove all files with a lower priority than @p file
     82     * and do the same for all subdirectories recursively.
     83     * @return true if the directory is empty afterwards
     84     **/
     85    bool DeleteTree(const VfsFile& file);
     86
    8087public:
    8188    typedef std::map<VfsPath, VfsFile> VfsFiles;
    8289    typedef std::map<VfsPath, VfsDirectory> VfsSubdirectories;
     
    8491    VfsDirectory();
    8592
    8693    /**
     94     * remove the given file or subdirectory according to the priority of the
     95     * passed .DELETED file.
     96     * CAUTION: Invalidates all previously returned pointers of the file or
     97     *          subdirectory (and contents) if those have lower priority
     98     *          than @p file.
     99     **/
     100    void DeleteSubtree(const VfsFile& file);
     101
     102    /**
    87103     * @return address of existing or newly inserted file.
    88104     **/
    89105    VfsFile* AddFile(const VfsFile& file);
  • source/ps/ArchiveBuilder.cpp

     
    1 /* Copyright (C) 2014 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    7777
    7878    CXeromyces xero;
    7979
    80     for (size_t i = 0; i < m_Files.size(); ++i)
     80    for (const VfsPath& path : m_Files)
    8181    {
    8282        Status ret;
    83 
    84         const VfsPath path = m_Files[i];
    8583        OsPath realPath;
    8684        ret = m_VFS->GetRealPath(path, realPath);
    8785        ENSURE(ret == INFO::OK);