1 /* Copyright (C) 2017 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
26 #include "lib/allocators/shared_ptr.h"
27 #include "lib/posix/posix_pthread.h"
28 #include "lib/file/file_system.h"
29 #include "lib/file/common/file_stats.h"
30 #include "lib/file/common/trace.h"
31 #include "lib/file/archive/archive.h"
32 #include "lib/file/io/io.h"
33 #include "lib/file/vfs/vfs_tree.h"
34 #include "lib/file/vfs/vfs_lookup.h"
35 #include "lib/file/vfs/vfs_populate.h"
36
37 static const StatusDefinition vfsStatusDefinitions[] = {
38 { ERR::VFS_DIR_NOT_FOUND, L"VFS directory not found" },
39 { ERR::VFS_FILE_NOT_FOUND, L"VFS file not found" },
40 { ERR::VFS_ALREADY_MOUNTED, L"VFS path already mounted" }
41 };
42 STATUS_ADD_DEFINITIONS(vfsStatusDefinitions);
43
44 static pthread_mutex_t vfs_mutex = PTHREAD_MUTEX_INITIALIZER;
45 namespace {
46 struct ScopedLock
47 {
ScopedLock__anonbaba9e240111::ScopedLock48 ScopedLock() { pthread_mutex_lock(&vfs_mutex); }
~ScopedLock__anonbaba9e240111::ScopedLock49 ~ScopedLock() { pthread_mutex_unlock(&vfs_mutex); }
50 };
51 } // namespace
52
53 class VFS : public IVFS
54 {
55 public:
VFS()56 VFS() : m_trace(CreateDummyTrace(8*MiB))
57 {
58 }
59
Mount(const VfsPath & mountPoint,const OsPath & path,size_t flags,size_t priority)60 virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags /* = 0 */, size_t priority /* = 0 */)
61 {
62 ScopedLock s;
63 if(!DirectoryExists(path))
64 {
65 if(flags & VFS_MOUNT_MUST_EXIST)
66 return ERR::VFS_DIR_NOT_FOUND; // NOWARN
67 else
68 RETURN_STATUS_IF_ERR(CreateDirectories(path, 0700));
69 }
70
71 VfsDirectory* directory;
72 WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_SKIP_POPULATE));
73
74 PRealDirectory realDirectory(new RealDirectory(path, priority, flags));
75 RETURN_STATUS_IF_ERR(vfs_Attach(directory, realDirectory));
76 return INFO::OK;
77 }
78
GetFileInfo(const VfsPath & pathname,CFileInfo * pfileInfo) const79 virtual Status GetFileInfo(const VfsPath& pathname, CFileInfo* pfileInfo) const
80 {
81 ScopedLock s;
82 VfsDirectory* directory;
83 VfsFile* file;
84
85 Status ret = vfs_Lookup(pathname, &m_rootDirectory, directory, &file);
86 if(!pfileInfo) // just indicate if the file exists without raising warnings.
87 return ret;
88 WARN_RETURN_STATUS_IF_ERR(ret);
89 *pfileInfo = CFileInfo(file->Name(), file->Size(), file->MTime());
90 return INFO::OK;
91 }
92
GetFilePriority(const VfsPath & pathname,size_t * ppriority) const93 virtual Status GetFilePriority(const VfsPath& pathname, size_t* ppriority) const
94 {
95 ScopedLock s;
96 VfsDirectory* directory; VfsFile* file;
97 RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));
98 *ppriority = file->Priority();
99 return INFO::OK;
100 }
101
GetDirectoryEntries(const VfsPath & path,CFileInfos * fileInfos,DirectoryNames * subdirectoryNames) const102 virtual Status GetDirectoryEntries(const VfsPath& path, CFileInfos* fileInfos, DirectoryNames* subdirectoryNames) const
103 {
104 ScopedLock s;
105 VfsDirectory* directory;
106 RETURN_STATUS_IF_ERR(vfs_Lookup(path, &m_rootDirectory, directory, 0));
107
108 if(fileInfos)
109 {
110 const VfsDirectory::VfsFiles& files = directory->Files();
111 fileInfos->clear();
112 fileInfos->reserve(files.size());
113 for(VfsDirectory::VfsFiles::const_iterator it = files.begin(); it != files.end(); ++it)
114 {
115 const VfsFile& file = it->second;
116 fileInfos->push_back(CFileInfo(file.Name(), file.Size(), file.MTime()));
117 }
118 }
119
120 if(subdirectoryNames)
121 {
122 const VfsDirectory::VfsSubdirectories& subdirectories = directory->Subdirectories();
123 subdirectoryNames->clear();
124 subdirectoryNames->reserve(subdirectories.size());
125 for(VfsDirectory::VfsSubdirectories::const_iterator it = subdirectories.begin(); it != subdirectories.end(); ++it)
126 subdirectoryNames->push_back(it->first);
127 }
128
129 return INFO::OK;
130 }
131
CreateFile(const VfsPath & pathname,const shared_ptr<u8> & fileContents,size_t size)132 virtual Status CreateFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size)
133 {
134 ScopedLock s;
135 VfsDirectory* directory;
136 Status st;
137 st = vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE|VFS_LOOKUP_CREATE_ALWAYS);
138 if (st == ERR::FILE_ACCESS)
139 return ERR::FILE_ACCESS;
140
141 WARN_RETURN_STATUS_IF_ERR(st);
142
143 const PRealDirectory& realDirectory = directory->AssociatedDirectory();
144 const OsPath name = pathname.Filename();
145 RETURN_STATUS_IF_ERR(realDirectory->Store(name, fileContents, size));
146
147 const VfsFile file(name, size, time(0), realDirectory->Priority(), realDirectory);
148 directory->AddFile(file);
149
150 m_trace->NotifyStore(pathname, size);
151 return INFO::OK;
152 }
153
ReplaceFile(const VfsPath & pathname,const shared_ptr<u8> & fileContents,size_t size)154 virtual Status ReplaceFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size)
155 {
156 ScopedLock s;
157 VfsDirectory* directory;
158 VfsFile* file;
159 Status st;
160 st = vfs_Lookup(pathname, &m_rootDirectory, directory, &file, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE);
161
162 // There is no such file, create it.
163 if (st == ERR::VFS_FILE_NOT_FOUND)
164 {
165 s.~ScopedLock();
166 return CreateFile(pathname, fileContents, size);
167 }
168
169 WARN_RETURN_STATUS_IF_ERR(st);
170
171 RealDirectory realDirectory(file->Loader()->Path(), file->Priority(), directory->AssociatedDirectory()->Flags());
172 RETURN_STATUS_IF_ERR(realDirectory.Store(pathname.Filename(), fileContents, size));
173
174 directory->AddFile(*file);
175
176 m_trace->NotifyStore(pathname, size);
177 return INFO::OK;
178 }
179
LoadFile(const VfsPath & pathname,shared_ptr<u8> & fileContents,size_t & size)180 virtual Status LoadFile(const VfsPath& pathname, shared_ptr<u8>& fileContents, size_t& size)
181 {
182 ScopedLock s;
183
184 VfsDirectory* directory; VfsFile* file;
185 // per 2010-05-01 meeting, this shouldn't raise 'scary error
186 // dialogs', which might fail to display the culprit pathname
187 // instead, callers should log the error, including pathname.
188 RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));
189
190 fileContents = DummySharedPtr((u8*)0);
191 size = file->Size();
192
193 RETURN_STATUS_IF_ERR(AllocateAligned(fileContents, size, maxSectorSize));
194 RETURN_STATUS_IF_ERR(file->Loader()->Load(file->Name(), fileContents, file->Size()));
195
196 stats_io_user_request(size);
197 m_trace->NotifyLoad(pathname, size);
198
199 return INFO::OK;
200 }
201
TextRepresentation() const202 virtual std::wstring TextRepresentation() const
203 {
204 ScopedLock s;
205 std::wstring textRepresentation;
206 textRepresentation.reserve(100*KiB);
207 DirectoryDescriptionR(textRepresentation, m_rootDirectory, 0);
208 return textRepresentation;
209 }
210
GetRealPath(const VfsPath & pathname,OsPath & realPathname)211 virtual Status GetRealPath(const VfsPath& pathname, OsPath& realPathname)
212 {
213 ScopedLock s;
214 VfsDirectory* directory; VfsFile* file;
215 WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));
216 realPathname = file->Loader()->Path() / pathname.Filename();
217 return INFO::OK;
218 }
219
GetDirectoryRealPath(const VfsPath & pathname,OsPath & realPathname)220 virtual Status GetDirectoryRealPath(const VfsPath& pathname, OsPath& realPathname)
221 {
222 ScopedLock s;
223 VfsDirectory* directory;
224 WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, NULL));
225 realPathname = directory->AssociatedDirectory()->Path();
226 return INFO::OK;
227 }
228
GetVirtualPath(const OsPath & realPathname,VfsPath & pathname)229 virtual Status GetVirtualPath(const OsPath& realPathname, VfsPath& pathname)
230 {
231 ScopedLock s;
232 const OsPath realPath = realPathname.Parent()/"";
233 VfsPath path;
234 RETURN_STATUS_IF_ERR(FindRealPathR(realPath, m_rootDirectory, L"", path));
235 pathname = path / realPathname.Filename();
236 return INFO::OK;
237 }
238
RemoveFile(const VfsPath & pathname)239 virtual Status RemoveFile(const VfsPath& pathname)
240 {
241 ScopedLock s;
242
243 VfsDirectory* directory; VfsFile* file;
244 RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));
245 directory->RemoveFile(file->Name());
246
247 return INFO::OK;
248 }
249
RepopulateDirectory(const VfsPath & path)250 virtual Status RepopulateDirectory(const VfsPath& path)
251 {
252 ScopedLock s;
253
254 VfsDirectory* directory;
255 RETURN_STATUS_IF_ERR(vfs_Lookup(path, &m_rootDirectory, directory, 0));
256 directory->RequestRepopulate();
257
258 return INFO::OK;
259 }
260
Clear()261 virtual void Clear()
262 {
263 ScopedLock s;
264 m_rootDirectory.Clear();
265 }
266
267 private:
FindRealPathR(const OsPath & realPath,const VfsDirectory & directory,const VfsPath & curPath,VfsPath & path)268 Status FindRealPathR(const OsPath& realPath, const VfsDirectory& directory, const VfsPath& curPath, VfsPath& path)
269 {
270 PRealDirectory realDirectory = directory.AssociatedDirectory();
271 if(realDirectory && realDirectory->Path() == realPath)
272 {
273 path = curPath;
274 return INFO::OK;
275 }
276
277 const VfsDirectory::VfsSubdirectories& subdirectories = directory.Subdirectories();
278 for(VfsDirectory::VfsSubdirectories::const_iterator it = subdirectories.begin(); it != subdirectories.end(); ++it)
279 {
280 const OsPath& subdirectoryName = it->first;
281 const VfsDirectory& subdirectory = it->second;
282 Status ret = FindRealPathR(realPath, subdirectory, curPath / subdirectoryName/"", path);
283 if(ret == INFO::OK)
284 return INFO::OK;
285 }
286
287 return ERR::PATH_NOT_FOUND; // NOWARN
288 }
289
290 PITrace m_trace;
291 mutable VfsDirectory m_rootDirectory;
292 };
293
294 //-----------------------------------------------------------------------------
295
CreateVfs()296 PIVFS CreateVfs()
297 {
298 return PIVFS(new VFS());
299 }
300