LCOV - code coverage report
Current view: top level - source/lib/file/vfs - vfs_populate.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 52 71 73.2 %
Date: 2023-01-19 00:18:29 Functions: 10 11 90.9 %

          Line data    Source code
       1             : /* Copyright (C) 2021 Wildfire Games.
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining
       4             :  * a copy of this software and associated documentation files (the
       5             :  * "Software"), to deal in the Software without restriction, including
       6             :  * without limitation the rights to use, copy, modify, merge, publish,
       7             :  * distribute, sublicense, and/or sell copies of the Software, and to
       8             :  * permit persons to whom the Software is furnished to do so, subject to
       9             :  * the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included
      12             :  * in all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      15             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      16             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      17             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      18             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      19             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      20             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : /*
      24             :  * populate VFS directories with files
      25             :  */
      26             : 
      27             : #include "precompiled.h"
      28             : #include "lib/file/vfs/vfs_populate.h"
      29             : 
      30             : #include "lib/file/archive/archive_zip.h"
      31             : #include "lib/file/vfs/vfs_tree.h"
      32             : #include "lib/file/vfs/vfs_lookup.h"
      33             : #include "lib/file/vfs/vfs.h" // error codes
      34             : 
      35             : struct CompareFileInfoByName
      36             : {
      37             :     bool operator()(const CFileInfo& a, const CFileInfo& b)
      38             :     {
      39             :         return a.Name() < b.Name();
      40             :     }
      41             : };
      42             : 
      43             : // helper class that allows breaking up the logic into sub-functions without
      44             : // always having to pass directory/realDirectory as parameters.
      45         528 : class PopulateHelper
      46             : {
      47             :     NONCOPYABLE(PopulateHelper);
      48             : public:
      49         528 :     PopulateHelper(VfsDirectory* directory, const PRealDirectory& realDirectory)
      50         528 :         : m_directory(directory), m_realDirectory(realDirectory)
      51             :     {
      52         528 :     }
      53             : 
      54         528 :     Status AddEntries() const
      55             :     {
      56        1056 :         CFileInfos files; files.reserve(500);
      57        1056 :         DirectoryNames subdirectoryNames; subdirectoryNames.reserve(50);
      58         528 :         RETURN_STATUS_IF_ERR(GetDirectoryEntries(m_realDirectory->Path(), &files, &subdirectoryNames));
      59             : 
      60             :         // Since .DELETED files only remove files in lower priority mods
      61             :         // loose files and archive files have no conflicts so we do not need
      62             :         // to sort them.
      63             :         // We add directories after they might have been removed by .DELETED
      64             :         // files (as they did not contain any files at that point). The order
      65             :         // of GetDirectoryEntries is undefined, but that does not really matter (TODO really?)
      66             :         // so we do not need to sort its output.
      67         528 :         RETURN_STATUS_IF_ERR(AddFiles(files));
      68         528 :         AddSubdirectories(subdirectoryNames);
      69             : 
      70         528 :         return INFO::OK;
      71             :     }
      72             : 
      73             : private:
      74        2315 :     void AddFile(const CFileInfo& fileInfo) const
      75             :     {
      76        4630 :         const VfsPath name = fileInfo.Name();
      77        4630 :         const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory);
      78        2315 :         if(name.Extension() == L".DELETED")
      79             :         {
      80           0 :             m_directory->DeleteSubtree(file);
      81           0 :             if(!(m_realDirectory->Flags() & VFS_MOUNT_KEEP_DELETED))
      82           0 :                 return;
      83             :         }
      84             : 
      85        2315 :         m_directory->AddFile(file);
      86             :     }
      87             : 
      88           0 :     static void AddArchiveFile(const VfsPath& pathname, const CFileInfo& fileInfo, PIArchiveFile archiveFile, uintptr_t cbData)
      89             :     {
      90           0 :         PopulateHelper* this_ = (PopulateHelper*)cbData;
      91             : 
      92             :         // (we have to create missing subdirectoryNames because archivers
      93             :         // don't always place directory entries before their files)
      94           0 :         const size_t flags = VFS_LOOKUP_ADD|VFS_LOOKUP_SKIP_POPULATE;
      95             :         VfsDirectory* directory;
      96           0 :         WARN_IF_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags));
      97             : 
      98           0 :         const VfsPath name = fileInfo.Name();
      99           0 :         const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile);
     100           0 :         if(name.Extension() == L".DELETED")
     101             :         {
     102           0 :             directory->DeleteSubtree(file);
     103           0 :             if(!(this_->m_realDirectory->Flags() & VFS_MOUNT_KEEP_DELETED))
     104           0 :                 return;
     105             :         }
     106             : 
     107           0 :         directory->AddFile(file);
     108             :     }
     109             : 
     110         528 :     Status AddFiles(const CFileInfos& files) const
     111             :     {
     112        1056 :         const OsPath path(m_realDirectory->Path());
     113             : 
     114        2843 :         for(size_t i = 0; i < files.size(); i++)
     115             :         {
     116        4630 :             const OsPath pathname = path / files[i].Name();
     117        2315 :             if(pathname.Extension() == L".zip")
     118             :             {
     119           0 :                 PIArchiveReader archiveReader = CreateArchiveReader_Zip(pathname);
     120             :                 // archiveReader == nullptr if file could not be opened (e.g. because
     121             :                 // archive is currently open in another program)
     122           0 :                 if(archiveReader)
     123           0 :                     RETURN_STATUS_IF_ERR(archiveReader->ReadEntries(AddArchiveFile, (uintptr_t)this));
     124             :             }
     125             :             else    // regular (non-archive) file
     126        2315 :                 AddFile(files[i]);
     127             :         }
     128             : 
     129         528 :         return INFO::OK;
     130             :     }
     131             : 
     132         528 :     void AddSubdirectories(const DirectoryNames& subdirectoryNames) const
     133             :     {
     134        1096 :         for(size_t i = 0; i < subdirectoryNames.size(); i++)
     135             :         {
     136             :             // skip version control directories - this avoids cluttering the
     137             :             // VFS with hundreds of irrelevant files.
     138         568 :             if(subdirectoryNames[i] == L".svn" || subdirectoryNames[i] == L".git")
     139           0 :                 continue;
     140             : 
     141         568 :             VfsDirectory* subdirectory = m_directory->AddSubdirectory(subdirectoryNames[i]);
     142        1136 :             PRealDirectory realDirectory = CreateRealSubdirectory(m_realDirectory, subdirectoryNames[i]);
     143         568 :             vfs_Attach(subdirectory, realDirectory);
     144             :         }
     145         528 :     }
     146             : 
     147             :     VfsDirectory* const m_directory;
     148             :     PRealDirectory m_realDirectory;
     149             : };
     150             : 
     151             : 
     152       24450 : Status vfs_Populate(VfsDirectory* directory)
     153             : {
     154       24450 :     if(!directory->ShouldPopulate())
     155       23922 :         return INFO::OK;
     156             : 
     157         528 :     const PRealDirectory& realDirectory = directory->AssociatedDirectory();
     158             : 
     159         528 :     if(realDirectory->Flags() & VFS_MOUNT_WATCH)
     160           0 :         realDirectory->Watch();
     161             : 
     162        1056 :     PopulateHelper helper(directory, realDirectory);
     163         528 :     RETURN_STATUS_IF_ERR(helper.AddEntries());
     164             : 
     165         528 :     return INFO::OK;
     166             : }
     167             : 
     168             : 
     169         859 : Status vfs_Attach(VfsDirectory* directory, const PRealDirectory& realDirectory)
     170             : {
     171        1718 :     PRealDirectory existingRealDir = directory->AssociatedDirectory();
     172             : 
     173             :     // Don't allow replacing the real directory by a lower-priority one.
     174         859 :     if (!existingRealDir || existingRealDir->Priority() < realDirectory->Priority())
     175             :     {
     176             :         // This ordering is peculiar but useful, as it "defers" the population call.
     177             :         // If there is already a real directory, we will replace it (and lose track of it),
     178             :         // so we'll populate it right away, but the 'new' real directory can wait until we access it.
     179         840 :         RETURN_STATUS_IF_ERR(vfs_Populate(directory));
     180         840 :         directory->SetAssociatedDirectory(realDirectory);
     181         840 :         return INFO::OK;
     182             :     }
     183             :     // We are attaching a lower-priority real directory.
     184             :     // Because of deferred population, we need to immediately populate this new directory.
     185          19 :     bool shouldPop = directory->ShouldPopulate();
     186             :     // This sets "should populate" to true, so the vfs_Populate call below immediately populates.
     187          19 :     directory->SetAssociatedDirectory(realDirectory);
     188          19 :     RETURN_STATUS_IF_ERR(vfs_Populate(directory));
     189             :     // Reset to the higher priority realDirectory, which resets ShouldPopulate to true.
     190          19 :     directory->SetAssociatedDirectory(existingRealDir);
     191             :     // Avoid un-necessary repopulation by clearing the flag.
     192          19 :     if (!shouldPop)
     193           3 :         directory->ShouldPopulate();
     194             : 
     195          19 :     return INFO::OK;
     196           3 : }

Generated by: LCOV version 1.13