LCOV - code coverage report
Current view: top level - source/lib/file/vfs - vfs_lookup.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 55 68 80.9 %
Date: 2023-01-19 00:18:29 Functions: 4 4 100.0 %

          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             :  * look up directories/files by traversing path components.
      25             :  */
      26             : 
      27             : #include "precompiled.h"
      28             : #include "lib/file/vfs/vfs_lookup.h"
      29             : 
      30             : #include "lib/external_libraries/suppress_boost_warnings.h"
      31             : 
      32             : #include "lib/sysdep/filesystem.h"
      33             : #include "lib/file/file.h"
      34             : #include "lib/file/vfs/vfs.h" // error codes
      35             : #include "lib/file/vfs/vfs_tree.h"
      36             : #include "lib/file/vfs/vfs_populate.h"
      37             : 
      38             : #include "lib/timer.h"
      39             : 
      40             : 
      41         116 : static Status CreateDirectory(const OsPath& path)
      42             : {
      43             :     {
      44         116 :         const mode_t mode = S_IRWXU; // 0700 as prescribed by XDG basedir
      45         116 :         const int ret = wmkdir(path, mode);
      46         116 :         if(ret == 0)    // success
      47         116 :             return INFO::OK;
      48             :     }
      49             : 
      50             :     // Failed because the directory already exists.
      51             :     // Return 'success' to attach the existing directory.
      52           0 :     if(errno == EEXIST)
      53             :     {
      54             :         // But first ensure it's really a directory
      55             :         // (otherwise, a file is "in the way" and needs to be deleted).
      56             :         struct stat s;
      57           0 :         const int ret = wstat(path, &s);
      58           0 :         ENSURE(ret == 0);   // (wmkdir said it existed)
      59           0 :         ENSURE(S_ISDIR(s.st_mode));
      60           0 :         return INFO::OK;
      61             :     }
      62             : 
      63           0 :     if (errno == EACCES)
      64           0 :         return ERR::FILE_ACCESS;
      65             : 
      66             :     // unexpected failure
      67           0 :     debug_printf("wmkdir failed with errno=%d\n", errno);
      68           0 :     DEBUG_WARN_ERR(ERR::LOGIC);
      69           0 :     WARN_RETURN(StatusFromErrno());
      70             : }
      71             : 
      72             : 
      73        7937 : Status vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDirectory*& directory, VfsFile** pfile, size_t flags)
      74             : {
      75             :     // extract and validate flags (ensure no unknown bits are set)
      76        7937 :     const bool addMissingDirectories    = (flags & VFS_LOOKUP_ADD) != 0;
      77        7937 :     const bool skipPopulate = (flags & VFS_LOOKUP_SKIP_POPULATE) != 0;
      78        7937 :     const bool realPath = (flags & VFS_LOOKUP_REAL_PATH) != 0;
      79        7937 :     ENSURE((flags & ~(VFS_LOOKUP_ADD|VFS_LOOKUP_SKIP_POPULATE|VFS_LOOKUP_REAL_PATH)) == 0);
      80             : 
      81        7937 :     directory = startDirectory;
      82        7937 :     if (pfile)
      83        7406 :         *pfile = 0;
      84             : 
      85        7937 :     if (!skipPopulate)
      86        7752 :         RETURN_STATUS_IF_ERR(vfs_Populate(directory));
      87             : 
      88             :     // early-out for pathname == "" when mounting into VFS root
      89        7937 :     if (pathname.empty())   // (prevent iterator error in loop end condition)
      90             :     {
      91             :         // Preserve a guarantee that if pfile then we either return an error or set *pfile,
      92             :         // and if looking for a real path ensure an associated directory.
      93          91 :         if (pfile || (realPath && !directory->AssociatedDirectory()))
      94           3 :             return ERR::VFS_FILE_NOT_FOUND;
      95             :         else
      96          88 :             return INFO::OK;
      97             :     }
      98             : 
      99             :     // for each directory component:
     100        7846 :     size_t pos = 0; // (needed outside of loop)
     101             :     for(;;)
     102             :     {
     103       23727 :         const size_t nextSlash = pathname.string().find_first_of('/', pos);
     104       23727 :         if (nextSlash == VfsPath::String::npos)
     105        7316 :             break;
     106       32292 :         const VfsPath subdirectoryName = pathname.string().substr(pos, nextSlash-pos);
     107       16411 :         pos = nextSlash+1;
     108             : 
     109       16411 :         VfsDirectory* subdirectory = directory->GetSubdirectory(subdirectoryName);
     110       16411 :         if (!subdirectory)
     111             :         {
     112         671 :             if (addMissingDirectories)
     113         141 :                 subdirectory = directory->AddSubdirectory(subdirectoryName);
     114             :             else
     115         530 :                 return ERR::VFS_DIR_NOT_FOUND;  // NOWARN
     116             :         }
     117             :         // When looking for a real path, we need to keep the path of the highest priority subdirectory.
     118             :         // If the current directory has an associated directory, and the subdir does not / is lower priority,
     119             :         // we will overwrite it.
     120       31762 :         PRealDirectory realDir = directory->AssociatedDirectory();
     121       16304 :         if (realPath && realDir &&
     122         504 :             (!subdirectory->AssociatedDirectory() ||
     123         197 :             realDir->Priority() > subdirectory->AssociatedDirectory()->Priority()))
     124             :         {
     125         232 :             OsPath currentPath = directory->AssociatedDirectory()->Path();
     126         116 :             currentPath = currentPath / subdirectoryName / "";
     127             : 
     128             :             // Only actually create the directory if we're in LOOKUP_ADD mode.
     129         116 :             if (addMissingDirectories)
     130         116 :                 RETURN_STATUS_IF_ERR(CreateDirectory(currentPath));
     131           0 :             else if (!DirectoryExists(currentPath))
     132           0 :                 return ERR::VFS_DIR_NOT_FOUND;
     133             : 
     134             :             // Propagate priority and flags to the subdirectory.
     135             :             // If it already existed, it will be replaced & the memory freed.
     136             :             PRealDirectory realDirectory(new RealDirectory(currentPath,
     137         116 :                 realDir ? realDir->Priority() : 0,
     138         116 :                 realDir ? realDir->Flags() : 0)
     139         232 :             );
     140         116 :             RETURN_STATUS_IF_ERR(vfs_Attach(subdirectory, realDirectory));
     141             :         }
     142             : 
     143       15881 :         if (!skipPopulate)
     144       15839 :             RETURN_STATUS_IF_ERR(vfs_Populate(subdirectory));
     145             : 
     146       15881 :         directory = subdirectory;
     147       15881 :     }
     148             : 
     149        7423 :     if (realPath && !directory->AssociatedDirectory())
     150           0 :         return ERR::VFS_DIR_NOT_FOUND;
     151             : 
     152        7316 :     if (pfile)
     153             :     {
     154        6900 :         ENSURE(!pathname.IsDirectory());
     155       13010 :         const VfsPath filename = pathname.string().substr(pos);
     156        6900 :         *pfile = directory->GetFile(filename);
     157        6900 :         if (!*pfile)
     158         790 :             return ERR::VFS_FILE_NOT_FOUND; // NOWARN
     159             :     }
     160             : 
     161        6526 :     return INFO::OK;
     162           3 : }

Generated by: LCOV version 1.13