Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
vfs.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2013 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 #include "precompiled.h"
24 #include "lib/file/vfs/vfs.h"
25 
28 #include "lib/file/file_system.h"
30 #include "lib/file/common/trace.h"
32 #include "lib/file/io/io.h"
33 #include "lib/file/vfs/vfs_tree.h"
37 
39  { ERR::VFS_DIR_NOT_FOUND, L"VFS directory not found" },
40  { ERR::VFS_FILE_NOT_FOUND, L"VFS file not found" },
41  { ERR::VFS_ALREADY_MOUNTED, L"VFS path already mounted" }
42 };
43 STATUS_ADD_DEFINITIONS(vfsStatusDefinitions);
44 
46 struct ScopedLock
47 {
50 };
51 
52 class VFS : public IVFS
53 {
54 public:
55  VFS(size_t cacheSize)
56  : m_cacheSize(cacheSize), m_fileCache(m_cacheSize)
58  {
59  }
60 
61  virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags /* = 0 */, size_t priority /* = 0 */)
62  {
63  ScopedLock s;
64  if(!DirectoryExists(path))
65  {
66  if(flags & VFS_MOUNT_MUST_EXIST)
67  return ERR::VFS_DIR_NOT_FOUND; // NOWARN
68  else
70  }
71 
72  VfsDirectory* directory;
74 
75  PRealDirectory realDirectory(new RealDirectory(path, priority, flags));
76  RETURN_STATUS_IF_ERR(vfs_Attach(directory, realDirectory));
77  return INFO::OK;
78  }
79 
80  virtual Status GetFileInfo(const VfsPath& pathname, CFileInfo* pfileInfo) const
81  {
82  ScopedLock s;
83  VfsDirectory* directory; VfsFile* file;
84  Status ret = vfs_Lookup(pathname, &m_rootDirectory, directory, &file);
85  if(!pfileInfo) // just indicate if the file exists without raising warnings.
86  return ret;
88  *pfileInfo = CFileInfo(file->Name(), file->Size(), file->MTime());
89  return INFO::OK;
90  }
91 
92  virtual Status GetFilePriority(const VfsPath& pathname, size_t* ppriority) const
93  {
94  ScopedLock s;
95  VfsDirectory* directory; VfsFile* file;
96  RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));
97  *ppriority = file->Priority();
98  return INFO::OK;
99  }
100 
101  virtual Status GetDirectoryEntries(const VfsPath& path, CFileInfos* fileInfos, DirectoryNames* subdirectoryNames) const
102  {
103  ScopedLock s;
104  VfsDirectory* directory;
106 
107  if(fileInfos)
108  {
109  const VfsDirectory::VfsFiles& files = directory->Files();
110  fileInfos->clear();
111  fileInfos->reserve(files.size());
112  for(VfsDirectory::VfsFiles::const_iterator it = files.begin(); it != files.end(); ++it)
113  {
114  const VfsFile& file = it->second;
115  fileInfos->push_back(CFileInfo(file.Name(), file.Size(), file.MTime()));
116  }
117  }
118 
119  if(subdirectoryNames)
120  {
121  const VfsDirectory::VfsSubdirectories& subdirectories = directory->Subdirectories();
122  subdirectoryNames->clear();
123  subdirectoryNames->reserve(subdirectories.size());
124  for(VfsDirectory::VfsSubdirectories::const_iterator it = subdirectories.begin(); it != subdirectories.end(); ++it)
125  subdirectoryNames->push_back(it->first);
126  }
127 
128  return INFO::OK;
129  }
130 
131  virtual Status CreateFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size)
132  {
133  ScopedLock s;
134  VfsDirectory* directory;
135  Status st;
137  if (st == ERR::FILE_ACCESS)
138  return ERR::FILE_ACCESS;
139 
141 
142  const PRealDirectory& realDirectory = directory->AssociatedDirectory();
143  const OsPath name = pathname.Filename();
144  RETURN_STATUS_IF_ERR(realDirectory->Store(name, fileContents, size));
145 
146  // wipe out any cached blocks. this is necessary to cover the (rare) case
147  // of file cache contents predating the file write.
148  m_fileCache.Remove(pathname);
149 
150  const VfsFile file(name, size, time(0), realDirectory->Priority(), realDirectory);
151  directory->AddFile(file);
152 
153  m_trace->NotifyStore(pathname, size);
154  return INFO::OK;
155  }
156 
157  virtual Status ReplaceFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size)
158  {
159  ScopedLock s;
160  VfsDirectory* directory;
161  VfsFile* file;
162  Status st;
163  st = vfs_Lookup(pathname, &m_rootDirectory, directory, &file, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE);
164 
165  // There is no such file, create it.
166  if (st == ERR::VFS_FILE_NOT_FOUND)
167  {
168  s.~ScopedLock();
169  return CreateFile(pathname, fileContents, size);
170  }
171 
173 
174  RealDirectory realDirectory(file->Loader()->Path(), file->Priority(), directory->AssociatedDirectory()->Flags());
175  RETURN_STATUS_IF_ERR(realDirectory.Store(pathname.Filename(), fileContents, size));
176 
177  // See comment in CreateFile
178  m_fileCache.Remove(pathname);
179 
180  directory->AddFile(*file);
181 
182  m_trace->NotifyStore(pathname, size);
183  return INFO::OK;
184  }
185 
186  virtual Status LoadFile(const VfsPath& pathname, shared_ptr<u8>& fileContents, size_t& size)
187  {
188  ScopedLock s;
189  const bool isCacheHit = m_fileCache.Retrieve(pathname, fileContents, size);
190  if(!isCacheHit)
191  {
192  VfsDirectory* directory; VfsFile* file;
193  // per 2010-05-01 meeting, this shouldn't raise 'scary error
194  // dialogs', which might fail to display the culprit pathname
195  // instead, callers should log the error, including pathname.
196  RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));
197 
198  fileContents = DummySharedPtr((u8*)0);
199  size = file->Size();
200  if(size != 0) // (the file cache can't handle zero-length allocations)
201  {
202  if(size < m_cacheSize/2) // (avoid evicting lots of previous data)
203  fileContents = m_fileCache.Reserve(size);
204  if(fileContents)
205  {
206  RETURN_STATUS_IF_ERR(file->Loader()->Load(file->Name(), fileContents, file->Size()));
207  m_fileCache.Add(pathname, fileContents, size);
208  }
209  else
210  {
211  RETURN_STATUS_IF_ERR(AllocateAligned(fileContents, size, maxSectorSize));
212  RETURN_STATUS_IF_ERR(file->Loader()->Load(file->Name(), fileContents, file->Size()));
213  }
214  }
215  }
216 
217  stats_io_user_request(size);
218  stats_cache(isCacheHit? CR_HIT : CR_MISS, size);
219  m_trace->NotifyLoad(pathname, size);
220 
221  return INFO::OK;
222  }
223 
224  virtual std::wstring TextRepresentation() const
225  {
226  ScopedLock s;
227  std::wstring textRepresentation;
228  textRepresentation.reserve(100*KiB);
229  DirectoryDescriptionR(textRepresentation, m_rootDirectory, 0);
230  return textRepresentation;
231  }
232 
233  virtual Status GetRealPath(const VfsPath& pathname, OsPath& realPathname)
234  {
235  ScopedLock s;
236  VfsDirectory* directory; VfsFile* file;
237  WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));
238  realPathname = file->Loader()->Path() / pathname.Filename();
239  return INFO::OK;
240  }
241 
242  virtual Status GetDirectoryRealPath(const VfsPath& pathname, OsPath& realPathname)
243  {
244  ScopedLock s;
245  VfsDirectory* directory;
246  WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, NULL));
247  realPathname = directory->AssociatedDirectory()->Path();
248  return INFO::OK;
249  }
250 
251  virtual Status GetVirtualPath(const OsPath& realPathname, VfsPath& pathname)
252  {
253  ScopedLock s;
254  const OsPath realPath = realPathname.Parent()/"";
255  VfsPath path;
256  RETURN_STATUS_IF_ERR(FindRealPathR(realPath, m_rootDirectory, L"", path));
257  pathname = path / realPathname.Filename();
258  return INFO::OK;
259  }
260 
261  virtual Status RemoveFile(const VfsPath& pathname)
262  {
263  ScopedLock s;
264  m_fileCache.Remove(pathname);
265 
266  VfsDirectory* directory; VfsFile* file;
267  RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));
268  directory->RemoveFile(file->Name());
269 
270  return INFO::OK;
271  }
272 
273  virtual Status RepopulateDirectory(const VfsPath& path)
274  {
275  ScopedLock s;
276 
277  VfsDirectory* directory;
278  RETURN_STATUS_IF_ERR(vfs_Lookup(path, &m_rootDirectory, directory, 0));
279  directory->RequestRepopulate();
280 
281  return INFO::OK;
282  }
283 
284  virtual void Clear()
285  {
286  ScopedLock s;
288  }
289 
290 private:
291  Status FindRealPathR(const OsPath& realPath, const VfsDirectory& directory, const VfsPath& curPath, VfsPath& path)
292  {
293  PRealDirectory realDirectory = directory.AssociatedDirectory();
294  if(realDirectory && realDirectory->Path() == realPath)
295  {
296  path = curPath;
297  return INFO::OK;
298  }
299 
300  const VfsDirectory::VfsSubdirectories& subdirectories = directory.Subdirectories();
301  for(VfsDirectory::VfsSubdirectories::const_iterator it = subdirectories.begin(); it != subdirectories.end(); ++it)
302  {
303  const OsPath& subdirectoryName = it->first;
304  const VfsDirectory& subdirectory = it->second;
305  Status ret = FindRealPathR(realPath, subdirectory, curPath / subdirectoryName/"", path);
306  if(ret == INFO::OK)
307  return INFO::OK;
308  }
309 
310  return ERR::PATH_NOT_FOUND; // NOWARN
311  }
312 
313  size_t m_cacheSize;
317 };
318 
319 //-----------------------------------------------------------------------------
320 
321 PIVFS CreateVfs(size_t cacheSize)
322 {
323  return PIVFS(new VFS(cacheSize));
324 }
VfsDirectory m_rootDirectory
Definition: vfs.cpp:316
ScopedLock()
Definition: vfs.cpp:48
#define u8
Definition: types.h:39
void * pthread_mutex_t
Definition: wpthread.h:82
VfsFile * AddFile(const VfsFile &file)
Definition: vfs_tree.cpp:81
Status vfs_Lookup(const VfsPath &pathname, VfsDirectory *startDirectory, VfsDirectory *&directory, VfsFile **pfile, size_t flags)
Resolve a pathname.
Definition: vfs_lookup.cpp:75
virtual Status GetVirtualPath(const OsPath &realPathname, VfsPath &pathname)
retrieve the VFS pathname that corresponds to a real file.
Definition: vfs.cpp:251
const Status VFS_ALREADY_MOUNTED
Definition: vfs.h:38
Path Filename() const
Definition: path.h:158
~ScopedLock()
Definition: vfs.cpp:49
const VfsPath & Name() const
Definition: vfs_tree.h:42
const Status OK
Definition: status.h:386
const Status VFS_DIR_NOT_FOUND
Definition: vfs.h:36
shared_ptr< ITrace > PITrace
Definition: trace.h:123
void RemoveFile(const VfsPath &name)
remove the given file from the virtual directory (no effect on the physical file).
Definition: vfs_tree.cpp:111
virtual Status LoadFile(const VfsPath &pathname, shared_ptr< u8 > &fileContents, size_t &size)
Read an entire file into memory.
Definition: vfs.cpp:186
virtual void Clear()
empty the contents of the filesystem.
Definition: vfs.cpp:284
void DirectoryDescriptionR(std::wstring &descriptions, const VfsDirectory &directory, size_t indentLevel)
append each directory&#39;s files&#39; description to the given string.
Definition: vfs_tree.cpp:198
virtual std::wstring TextRepresentation() const
Definition: vfs.cpp:224
static const uintptr_t maxSectorSize
Definition: alignment.h:82
shared_ptr< IVFS > PIVFS
Definition: vfs.h:226
virtual Status ReplaceFile(const VfsPath &pathname, const shared_ptr< u8 > &fileContents, size_t size)
Replace a file with the given contents.
Definition: vfs.cpp:157
Path Parent() const
Definition: path.h:150
virtual Status GetDirectoryRealPath(const VfsPath &pathname, OsPath &realPathname)
retrieve the real (POSIX) pathname underlying a VFS directory.
Definition: vfs.cpp:242
bool Retrieve(const VfsPath &pathname, shared_ptr< u8 > &data, size_t &size)
Attempt to retrieve a file&#39;s contents from the file cache.
Definition: file_cache.cpp:250
const VfsFiles & Files() const
Definition: vfs_tree.h:116
const PRealDirectory & AssociatedDirectory() const
Definition: vfs_tree.h:131
virtual Status GetFilePriority(const VfsPath &pathname, size_t *ppriority) const
Retrieve mount priority for a file.
Definition: vfs.cpp:92
virtual Status GetDirectoryEntries(const VfsPath &path, CFileInfos *fileInfos, DirectoryNames *subdirectoryNames) const
Retrieve lists of all files and subdirectories in a directory.
Definition: vfs.cpp:101
Status FindRealPathR(const OsPath &realPath, const VfsDirectory &directory, const VfsPath &curPath, VfsPath &path)
Definition: vfs.cpp:291
PITrace m_trace
Definition: vfs.cpp:315
virtual Status GetFileInfo(const VfsPath &pathname, CFileInfo *pfileInfo) const
Retrieve information about a file (similar to POSIX stat).
Definition: vfs.cpp:80
virtual Status CreateFile(const VfsPath &pathname, const shared_ptr< u8 > &fileContents, size_t size)
Create a file with the given contents.
Definition: vfs.cpp:131
size_t Size() const
Definition: vfs_tree.h:47
return ERR::VFS_DIR_NOT_FOUND if the given real path doesn&#39;t exist.
Definition: vfs.h:60
cache of file contents with support for zero-copy IO.
Definition: file_cache.h:48
const VfsSubdirectories & Subdirectories() const
Definition: vfs_tree.h:121
#define WARN_RETURN_STATUS_IF_ERR(expression)
Definition: status.h:287
VFS(size_t cacheSize)
Definition: vfs.cpp:55
PIVFS CreateVfs(size_t cacheSize)
create an instance of a Virtual File System.
Definition: vfs.cpp:321
virtual Status RepopulateDirectory(const VfsPath &path)
request the directory be re-populated when it is next accessed.
Definition: vfs.cpp:273
Definition: vfs.cpp:52
Definition: path.h:75
virtual Status GetRealPath(const VfsPath &pathname, OsPath &realPathname)
retrieve the real (POSIX) pathname underlying a VFS file.
Definition: vfs.cpp:233
shared_ptr< RealDirectory > PRealDirectory
Status vfs_Attach(VfsDirectory *directory, const PRealDirectory &realDirectory)
attach a real directory to a VFS directory.
const Status VFS_FILE_NOT_FOUND
Definition: vfs.h:37
static const size_t KiB
Definition: alignment.h:71
static const size_t MiB
Definition: alignment.h:72
int pthread_mutex_lock(pthread_mutex_t *m)
Definition: wpthread.cpp:329
static pthread_mutex_t vfs_mutex
Definition: vfs.cpp:45
size_t Priority() const
Definition: vfs_tree.h:57
time_t MTime() const
Definition: vfs_tree.h:52
i64 Status
Error handling system.
Definition: status.h:171
shared_ptr< T > DummySharedPtr(T *ptr)
Definition: shared_ptr.h:38
#define stats_io_user_request(user_size)
Definition: file_stats.h:101
#define STATUS_ADD_DEFINITIONS(definitions)
add a module&#39;s array of StatusDefinition to the list.
Definition: status.h:216
const PIFileLoader & Loader() const
Definition: vfs_tree.h:62
void Remove(const VfsPath &pathname)
Remove a file&#39;s contents from the cache (if it exists).
Definition: file_cache.cpp:245
shared_ptr< u8 > Reserve(size_t size)
Reserve a chunk of the cache&#39;s memory region.
Definition: file_cache.cpp:235
static Status AllocateAligned(shared_ptr< T > &p, size_t size, size_t alignment=cacheLineSize)
Definition: shared_ptr.h:66
void Clear()
empty file and subdirectory lists (e.g.
Definition: vfs_tree.cpp:155
int pthread_mutex_unlock(pthread_mutex_t *m)
Definition: wpthread.cpp:347
std::vector< OsPath > DirectoryNames
Definition: file_system.h:77
std::map< VfsPath, VfsFile > VfsFiles
Definition: vfs_tree.h:81
PITrace CreateDummyTrace(size_t maxSize)
Definition: trace.cpp:226
#define stats_cache(cr, size)
Definition: file_stats.h:111
void RequestRepopulate()
ensure the next ShouldPopulate returns true.
Definition: vfs_tree.cpp:149
void Add(const VfsPath &pathname, const shared_ptr< u8 > &data, size_t size, size_t cost=1)
Add a file&#39;s contents to the cache.
Definition: file_cache.cpp:240
const Status FILE_ACCESS
Definition: file.h:35
std::map< VfsPath, VfsDirectory > VfsSubdirectories
Definition: vfs_tree.h:82
Definition: vfs.h:82
FileCache m_fileCache
Definition: vfs.cpp:314
#define PTHREAD_MUTEX_INITIALIZER
Definition: wpthread.h:84
Status CreateDirectories(const OsPath &path, mode_t mode)
bool DirectoryExists(const OsPath &path)
Definition: file_system.cpp:37
std::vector< CFileInfo > CFileInfos
Definition: file_system.h:76
virtual Status RemoveFile(const VfsPath &pathname)
remove file from the virtual directory listing and evict its data from the cache. ...
Definition: vfs.cpp:261
static const StatusDefinition vfsStatusDefinitions[]
Definition: vfs.cpp:38
const Status PATH_NOT_FOUND
Definition: path.h:50
virtual Status Mount(const VfsPath &mountPoint, const OsPath &path, size_t flags, size_t priority)
mount a directory into the VFS.
Definition: vfs.cpp:61
#define RETURN_STATUS_IF_ERR(expression)
Definition: status.h:276
size_t m_cacheSize
Definition: vfs.cpp:313