aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llvfs
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llvfs
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llvfs')
-rwxr-xr-xlinden/indra/llvfs/files.linux.lst1
-rw-r--r--linden/indra/llvfs/files.lst5
-rwxr-xr-xlinden/indra/llvfs/files.win32.lst1
-rw-r--r--linden/indra/llvfs/lldir.cpp480
-rw-r--r--linden/indra/llvfs/lldir.h121
-rw-r--r--linden/indra/llvfs/lldir_linux.cpp350
-rw-r--r--linden/indra/llvfs/lldir_linux.h60
-rw-r--r--linden/indra/llvfs/lldir_mac.cpp381
-rw-r--r--linden/indra/llvfs/lldir_mac.h59
-rw-r--r--linden/indra/llvfs/lldir_win32.cpp401
-rw-r--r--linden/indra/llvfs/lldir_win32.h56
-rw-r--r--linden/indra/llvfs/lllfsthread.cpp327
-rw-r--r--linden/indra/llvfs/lllfsthread.h138
-rw-r--r--linden/indra/llvfs/llvfile.cpp434
-rw-r--r--linden/indra/llvfs/llvfile.h94
-rw-r--r--linden/indra/llvfs/llvfs.cpp2071
-rw-r--r--linden/indra/llvfs/llvfs.h170
-rw-r--r--linden/indra/llvfs/llvfs.vcproj201
-rw-r--r--linden/indra/llvfs/llvfsthread.cpp314
-rw-r--r--linden/indra/llvfs/llvfsthread.h144
20 files changed, 5808 insertions, 0 deletions
diff --git a/linden/indra/llvfs/files.linux.lst b/linden/indra/llvfs/files.linux.lst
new file mode 100755
index 0000000..2c306eb
--- /dev/null
+++ b/linden/indra/llvfs/files.linux.lst
@@ -0,0 +1 @@
llvfs/lldir_linux.cpp
diff --git a/linden/indra/llvfs/files.lst b/linden/indra/llvfs/files.lst
new file mode 100644
index 0000000..758ab48
--- /dev/null
+++ b/linden/indra/llvfs/files.lst
@@ -0,0 +1,5 @@
1llvfs/lldir.cpp
2llvfs/lllfsthread.cpp
3llvfs/llvfile.cpp
4llvfs/llvfs.cpp
5llvfs/llvfsthread.cpp
diff --git a/linden/indra/llvfs/files.win32.lst b/linden/indra/llvfs/files.win32.lst
new file mode 100755
index 0000000..34920d1
--- /dev/null
+++ b/linden/indra/llvfs/files.win32.lst
@@ -0,0 +1 @@
llvfs/lldir_win32.cpp
diff --git a/linden/indra/llvfs/lldir.cpp b/linden/indra/llvfs/lldir.cpp
new file mode 100644
index 0000000..b0d9d92
--- /dev/null
+++ b/linden/indra/llvfs/lldir.cpp
@@ -0,0 +1,480 @@
1/**
2 * @file lldir.cpp
3 * @brief implementation of directory utilities base class
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#if !LL_WINDOWS
31#include <sys/stat.h>
32#include <sys/types.h>
33#else
34#include <direct.h>
35#endif
36
37#include "lldir.h"
38#include "llerror.h"
39#include "lluuid.h"
40
41#if LL_WINDOWS
42#include "lldir_win32.h"
43LLDir_Win32 gDirUtil;
44#elif LL_DARWIN
45#include "lldir_mac.h"
46LLDir_Mac gDirUtil;
47#else
48#include "lldir_linux.h"
49LLDir_Linux gDirUtil;
50#endif
51
52LLDir *gDirUtilp = (LLDir *)&gDirUtil;
53
54LLDir::LLDir()
55: mAppName(""),
56 mExecutablePathAndName(""),
57 mExecutableFilename(""),
58 mExecutableDir(""),
59 mAppRODataDir(""),
60 mOSUserDir(""),
61 mOSUserAppDir(""),
62 mLindenUserDir(""),
63 mCAFile(""),
64 mTempDir(""),
65 mDirDelimiter("")
66{
67}
68
69LLDir::~LLDir()
70{
71}
72
73
74S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
75{
76 S32 count = 0;
77 std::string filename;
78 std::string fullpath;
79 S32 result;
80 while (getNextFileInDir(dirname, mask, filename, FALSE))
81 {
82 if ((filename == ".") || (filename == ".."))
83 {
84 // skipping directory traversal filenames
85 count++;
86 continue;
87 }
88 fullpath = dirname;
89 fullpath += getDirDelimiter();
90 fullpath += filename;
91
92 S32 retry_count = 0;
93 while (retry_count < 5)
94 {
95 if (0 != LLFile::remove(fullpath.c_str()))
96 {
97 result = errno;
98 llwarns << "Problem removing " << fullpath << " - errorcode: "
99 << result << " attempt " << retry_count << llendl;
100 ms_sleep(1000);
101 }
102 else
103 {
104 if (retry_count)
105 {
106 llwarns << "Successfully removed " << fullpath << llendl;
107 }
108 break;
109 }
110 retry_count++;
111 }
112 count++;
113 }
114 return count;
115}
116
117const std::string LLDir::findFile(const std::string &filename,
118 const std::string searchPath1,
119 const std::string searchPath2,
120 const std::string searchPath3)
121{
122 std::vector<std::string> search_paths;
123 search_paths.push_back(searchPath1);
124 search_paths.push_back(searchPath2);
125 search_paths.push_back(searchPath3);
126
127 std::vector<std::string>::iterator search_path_iter;
128 for (search_path_iter = search_paths.begin();
129 search_path_iter != search_paths.end();
130 ++search_path_iter)
131 {
132 if (!search_path_iter->empty())
133 {
134 std::string filename_and_path = (*search_path_iter) + getDirDelimiter() + filename;
135 if (fileExists(filename_and_path))
136 {
137 return filename_and_path;
138 }
139 }
140 }
141 return "";
142}
143
144
145const std::string &LLDir::getExecutablePathAndName() const
146{
147 return mExecutablePathAndName;
148}
149
150const std::string &LLDir::getExecutableFilename() const
151{
152 return mExecutableFilename;
153}
154
155const std::string &LLDir::getExecutableDir() const
156{
157 return mExecutableDir;
158}
159
160const std::string &LLDir::getWorkingDir() const
161{
162 return mWorkingDir;
163}
164
165const std::string &LLDir::getAppName() const
166{
167 return mAppName;
168}
169
170const std::string &LLDir::getAppRODataDir() const
171{
172 return mAppRODataDir;
173}
174
175const std::string &LLDir::getOSUserDir() const
176{
177 return mOSUserDir;
178}
179
180const std::string &LLDir::getOSUserAppDir() const
181{
182 return mOSUserAppDir;
183}
184
185const std::string &LLDir::getLindenUserDir() const
186{
187 return mLindenUserDir;
188}
189
190const std::string &LLDir::getChatLogsDir() const
191{
192 return mChatLogsDir;
193}
194
195const std::string &LLDir::getPerAccountChatLogsDir() const
196{
197 return mPerAccountChatLogsDir;
198}
199
200const std::string &LLDir::getTempDir() const
201{
202 return mTempDir;
203}
204
205const std::string &LLDir::getCAFile() const
206{
207 return mCAFile;
208}
209
210const std::string &LLDir::getDirDelimiter() const
211{
212 return mDirDelimiter;
213}
214
215const std::string &LLDir::getSkinDir() const
216{
217 return mSkinDir;
218}
219
220std::string LLDir::getExpandedFilename(ELLPath location, const std::string &filename) const
221{
222 std::string prefix;
223 switch (location)
224 {
225 case LL_PATH_NONE:
226 // Do nothing
227 break;
228
229 case LL_PATH_APP_SETTINGS:
230 prefix = getAppRODataDir();
231 prefix += mDirDelimiter;
232 prefix += "app_settings";
233 break;
234
235 case LL_PATH_CHARACTER:
236 prefix = getAppRODataDir();
237 prefix += mDirDelimiter;
238 prefix += "character";
239 break;
240
241 case LL_PATH_MOTIONS:
242 prefix = getAppRODataDir();
243 prefix += mDirDelimiter;
244 prefix += "motions";
245 break;
246
247 case LL_PATH_HELP:
248 prefix = "help";
249 break;
250
251 case LL_PATH_CACHE:
252 if (getOSUserAppDir().empty())
253 {
254 prefix = "data";
255 }
256 else
257 {
258 prefix = getOSUserAppDir();
259 prefix += mDirDelimiter;
260 prefix += "cache";
261 }
262 break;
263
264 case LL_PATH_USER_SETTINGS:
265 prefix = getOSUserAppDir();
266 prefix += mDirDelimiter;
267 prefix += "user_settings";
268 break;
269
270 case LL_PATH_PER_SL_ACCOUNT:
271 prefix = getLindenUserDir();
272 break;
273
274 case LL_PATH_CHAT_LOGS:
275 prefix = getChatLogsDir();
276 break;
277
278 case LL_PATH_PER_ACCOUNT_CHAT_LOGS:
279 prefix = getPerAccountChatLogsDir();
280 break;
281
282 case LL_PATH_LOGS:
283 prefix = getOSUserAppDir();
284 prefix += mDirDelimiter;
285 prefix += "logs";
286 break;
287
288 case LL_PATH_TEMP:
289 prefix = getTempDir();
290 break;
291
292 case LL_PATH_TOP_SKIN:
293 prefix = getSkinDir();
294 break;
295
296 case LL_PATH_SKINS:
297 prefix = getAppRODataDir();
298 prefix += mDirDelimiter;
299 prefix += "skins";
300 break;
301
302 case LL_PATH_MOZILLA_PROFILE:
303 prefix = getOSUserAppDir();
304 prefix += mDirDelimiter;
305 prefix += "browser_profile";
306 break;
307
308 default:
309 llassert(0);
310 }
311
312 std::string expanded_filename;
313 if (!filename.empty())
314 {
315 if (!prefix.empty())
316 {
317 expanded_filename += prefix;
318 expanded_filename += mDirDelimiter;
319 expanded_filename += filename;
320 }
321 else
322 {
323 expanded_filename = filename;
324 }
325 }
326 else
327 if (!prefix.empty())
328 {
329 // Directory only, no file name.
330 expanded_filename = prefix;
331 }
332 else
333 {
334 expanded_filename.assign("");
335 }
336
337 //llinfos << "*** EXPANDED FILENAME: <" << mExpandedFilename << ">" << llendl;
338
339 return expanded_filename;
340}
341
342std::string LLDir::getTempFilename() const
343{
344 LLUUID random_uuid;
345 char uuid_str[64];
346
347 random_uuid.generate();
348 random_uuid.toString(uuid_str);
349
350 std::string temp_filename = getTempDir();
351 temp_filename += mDirDelimiter;
352 temp_filename += uuid_str;
353 temp_filename += ".tmp";
354
355 return temp_filename;
356}
357
358void LLDir::setLindenUserDir(const std::string &first, const std::string &last)
359{
360 // if both first and last aren't set, assume we're grabbing the cached dir
361 if (!first.empty() && !last.empty())
362 {
363 // some platforms have case-sensitive filesystems, so be
364 // utterly consistent with our firstname/lastname case.
365 LLString firstlower(first);
366 LLString::toLower(firstlower);
367 LLString lastlower(last);
368 LLString::toLower(lastlower);
369 mLindenUserDir = getOSUserAppDir();
370 mLindenUserDir += mDirDelimiter;
371 mLindenUserDir += firstlower.c_str();
372 mLindenUserDir += "_";
373 mLindenUserDir += lastlower.c_str();
374 }
375 else
376 {
377 llerrs << "Invalid name for LLDir::setLindenUserDir" << llendl;
378 }
379
380 dumpCurrentDirectories();
381}
382
383void LLDir::setChatLogsDir(const std::string &path)
384{
385 if (!path.empty() )
386 {
387 mChatLogsDir = path;
388 }
389 else
390 {
391 llwarns << "Invalid name for LLDir::setChatLogsDir" << llendl;
392 }
393}
394
395void LLDir::setPerAccountChatLogsDir(const std::string &first, const std::string &last)
396{
397 // if both first and last aren't set, assume we're grabbing the cached dir
398 if (!first.empty() && !last.empty())
399 {
400 // some platforms have case-sensitive filesystems, so be
401 // utterly consistent with our firstname/lastname case.
402 LLString firstlower(first);
403 LLString::toLower(firstlower);
404 LLString lastlower(last);
405 LLString::toLower(lastlower);
406 mPerAccountChatLogsDir = getChatLogsDir();
407 mPerAccountChatLogsDir += mDirDelimiter;
408 mPerAccountChatLogsDir += firstlower.c_str();
409 mPerAccountChatLogsDir += "_";
410 mPerAccountChatLogsDir += lastlower.c_str();
411 }
412 else
413 {
414 llwarns << "Invalid name for LLDir::setPerAccountChatLogsDir" << llendl;
415 }
416}
417
418void LLDir::setSkinFolder(const std::string &skin_folder)
419{
420 mSkinDir = getAppRODataDir();
421 mSkinDir += mDirDelimiter;
422 mSkinDir += "skins";
423 mSkinDir += mDirDelimiter;
424 mSkinDir += skin_folder;
425}
426
427void LLDir::dumpCurrentDirectories()
428{
429 llinfos << "Current Directories:" << llendl;
430
431 llinfos << " CurPath: " << getCurPath() << llendl;
432 llinfos << " AppName: " << getAppName() << llendl;
433 llinfos << " ExecutableFilename: " << getExecutableFilename() << llendl;
434 llinfos << " ExecutableDir: " << getExecutableDir() << llendl;
435 llinfos << " ExecutablePathAndName: " << getExecutablePathAndName() << llendl;
436 llinfos << " WorkingDir: " << getWorkingDir() << llendl;
437 llinfos << " AppRODataDir: " << getAppRODataDir() << llendl;
438 llinfos << " OSUserDir: " << getOSUserDir() << llendl;
439 llinfos << " OSUserAppDir: " << getOSUserAppDir() << llendl;
440 llinfos << " LindenUserDir: " << getLindenUserDir() << llendl;
441 llinfos << " TempDir: " << getTempDir() << llendl;
442 llinfos << " CAFile: " << getCAFile() << llendl;
443 llinfos << " SkinDir: " << getSkinDir() << llendl;
444}
445
446
447void dir_exists_or_crash(const std::string &dir_name)
448{
449#if LL_WINDOWS
450 // *FIX: lame - it doesn't do the same thing on windows. not so
451 // important since we don't deploy simulator to windows boxes.
452 LLFile::mkdir(dir_name.c_str(), 0700);
453#else
454 struct stat dir_stat;
455 if(0 != LLFile::stat(dir_name.c_str(), &dir_stat))
456 {
457 S32 stat_rv = errno;
458 if(ENOENT == stat_rv)
459 {
460 if(0 != LLFile::mkdir(dir_name.c_str(), 0700)) // octal
461 {
462 llerrs << "Unable to create directory: " << dir_name << llendl;
463 }
464 }
465 else
466 {
467 llerrs << "Unable to stat: " << dir_name << " errno = " << stat_rv
468 << llendl;
469 }
470 }
471 else
472 {
473 // data_dir exists, make sure it's a directory.
474 if(!S_ISDIR(dir_stat.st_mode))
475 {
476 llerrs << "Data directory collision: " << dir_name << llendl;
477 }
478 }
479#endif
480}
diff --git a/linden/indra/llvfs/lldir.h b/linden/indra/llvfs/lldir.h
new file mode 100644
index 0000000..2012f1b
--- /dev/null
+++ b/linden/indra/llvfs/lldir.h
@@ -0,0 +1,121 @@
1/**
2 * @file lldir.h
3 * @brief Definition of directory utilities class
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_LLDIR_H
29#define LL_LLDIR_H
30
31typedef enum ELLPath
32{
33 LL_PATH_NONE = 0,
34 LL_PATH_USER_SETTINGS = 1,
35 LL_PATH_APP_SETTINGS = 2,
36 LL_PATH_PER_SL_ACCOUNT = 3,
37 LL_PATH_CACHE = 4,
38 LL_PATH_CHARACTER = 5,
39 LL_PATH_MOTIONS = 6,
40 LL_PATH_HELP = 7,
41 LL_PATH_LOGS = 8,
42 LL_PATH_TEMP = 9,
43 LL_PATH_SKINS = 10,
44 LL_PATH_TOP_SKIN = 11,
45 LL_PATH_CHAT_LOGS = 12,
46 LL_PATH_PER_ACCOUNT_CHAT_LOGS = 13,
47 LL_PATH_MOZILLA_PROFILE = 14,
48 LL_PATH_COUNT = 15
49} ELLPath;
50
51
52class LLDir
53{
54 public:
55 LLDir();
56 virtual ~LLDir();
57
58 virtual void initAppDirs(const std::string &app_name) = 0;
59 public:
60 virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask);
61
62// pure virtual functions
63 virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask) = 0;
64 virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) = 0;
65 virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) = 0;
66 virtual std::string getCurPath() = 0;
67 virtual BOOL fileExists(const std::string &filename) = 0;
68
69 const std::string findFile(const std::string &filename, const std::string searchPath1 = "", const std::string searchPath2 = "", const std::string searchPath3 = "");
70 const std::string &getExecutablePathAndName() const; // Full pathname of the executable
71 const std::string &getAppName() const; // install directory under progams/ ie "SecondLife"
72 const std::string &getExecutableDir() const; // Directory where the executable is located
73 const std::string &getExecutableFilename() const;// Filename of .exe
74 const std::string &getWorkingDir() const; // Current working directory
75 const std::string &getAppRODataDir() const; // Location of read-only data files
76 const std::string &getOSUserDir() const; // Location of the os-specific user dir
77 const std::string &getOSUserAppDir() const; // Location of the os-specific user app dir
78 const std::string &getLindenUserDir() const; // Location of the Linden user dir.
79 const std::string &getChatLogsDir() const; // Location of the chat logs dir.
80 const std::string &getPerAccountChatLogsDir() const; // Location of the per account chat logs dir.
81 const std::string &getTempDir() const; // Common temporary directory
82 const std::string &getCAFile() const; // File containing TLS certificate authorities
83 const std::string &getDirDelimiter() const; // directory separator for platform (ie. '\' or '/' or ':')
84 const std::string &getSkinDir() const; // User-specified skin folder.
85
86 // Expanded filename
87 std::string getExpandedFilename(ELLPath location, const std::string &filename) const;
88
89 // random filename in common temporary directory
90 std::string getTempFilename() const;
91
92 virtual void setChatLogsDir(const std::string &path); // Set the chat logs dir to this user's dir
93 virtual void setPerAccountChatLogsDir(const std::string &first, const std::string &last); // Set the per user chat log directory.
94 virtual void setLindenUserDir(const std::string &first, const std::string &last); // Set the linden user dir to this user's dir
95 virtual void setSkinFolder(const std::string &skin_folder);
96
97 virtual void dumpCurrentDirectories();
98
99protected:
100 std::string mAppName; // install directory under progams/ ie "SecondLife"
101 std::string mExecutablePathAndName; // full path + Filename of .exe
102 std::string mExecutableFilename; // Filename of .exe
103 std::string mExecutableDir; // Location of executable
104 std::string mWorkingDir; // Current working directory
105 std::string mAppRODataDir; // Location for static app data
106 std::string mOSUserDir; // OS Specific user directory
107 std::string mOSUserAppDir; // OS Specific user app directory
108 std::string mLindenUserDir; // Location for Linden user-specific data
109 std::string mPerAccountChatLogsDir; // Location for chat logs.
110 std::string mChatLogsDir; // Location for chat logs.
111 std::string mCAFile; // Location of the TLS certificate authority PEM file.
112 std::string mTempDir;
113 std::string mDirDelimiter;
114 std::string mSkinDir; // Location for u ser-specified skin info.
115};
116
117void dir_exists_or_crash(const std::string &dir_name);
118
119extern LLDir *gDirUtilp;
120
121#endif // LL_LLDIR_H
diff --git a/linden/indra/llvfs/lldir_linux.cpp b/linden/indra/llvfs/lldir_linux.cpp
new file mode 100644
index 0000000..8557901
--- /dev/null
+++ b/linden/indra/llvfs/lldir_linux.cpp
@@ -0,0 +1,350 @@
1/**
2 * @file lldir_linux.cpp
3 * @brief Implementation of directory utilities for linux
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include "lldir_linux.h"
31#include "llerror.h"
32#include "llrand.h" // for gLindenLabRandomNumber
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <unistd.h>
36#include <glob.h>
37#include <pwd.h>
38
39
40static std::string getCurrentUserHome(char* fallback)
41{
42 const uid_t uid = getuid();
43 struct passwd *pw;
44 char *result_cstr = fallback;
45
46 pw = getpwuid(uid);
47 if ((pw != NULL) && (pw->pw_dir != NULL))
48 {
49 result_cstr = (char*) pw->pw_dir;
50 }
51 else
52 {
53 llinfos << "Couldn't detect home directory from passwd - trying $HOME" << llendl;
54 const char *const home_env = getenv("HOME");
55 if (home_env)
56 {
57 result_cstr = (char*) home_env;
58 }
59 else
60 {
61 llwarns << "Couldn't detect home directory! Falling back to " << fallback << llendl;
62 }
63 }
64
65 return std::string(result_cstr);
66}
67
68
69LLDir_Linux::LLDir_Linux()
70{
71 mDirDelimiter = "/";
72 mCurrentDirIndex = -1;
73 mCurrentDirCount = -1;
74 mDirp = NULL;
75
76 char tmp_str[LL_MAX_PATH];
77 getcwd(tmp_str, LL_MAX_PATH);
78
79 mExecutableFilename = "";
80 mExecutablePathAndName = "";
81 mExecutableDir = tmp_str;
82 mWorkingDir = tmp_str;
83 mAppRODataDir = tmp_str;
84 mOSUserDir = getCurrentUserHome(tmp_str);
85 mOSUserAppDir = "";
86 mLindenUserDir = tmp_str;
87
88 char path [32];
89
90 // *NOTE: /proc/%d/exe doesn't work on FreeBSD. But that's ok,
91 // because this is the linux implementation.
92
93 sprintf (path, "/proc/%d/exe", (int) getpid ());
94 int rc = readlink (path, tmp_str, sizeof (tmp_str)-1);
95 if ( (rc != -1) && (rc <= ((int) sizeof (tmp_str)-1)) )
96 {
97 tmp_str[rc] = '\0'; //readlink() doesn't 0-terminate the buffer
98 mExecutablePathAndName = tmp_str;
99 char *path_end;
100 if ((path_end = strrchr(tmp_str,'/')))
101 {
102 *path_end = '\0';
103 mExecutableDir = tmp_str;
104 mWorkingDir = tmp_str;
105 mExecutableFilename = path_end+1;
106 }
107 else
108 {
109 mExecutableFilename = tmp_str;
110 }
111 }
112
113 // *TODO: don't use /tmp, use $HOME/.secondlife/tmp or something.
114 mTempDir = "/tmp";
115}
116
117LLDir_Linux::~LLDir_Linux()
118{
119}
120
121// Implementation
122
123
124void LLDir_Linux::initAppDirs(const std::string &app_name)
125{
126 mAppName = app_name;
127
128 LLString upper_app_name(app_name);
129 LLString::toUpper(upper_app_name);
130
131 char* app_home_env = getenv((upper_app_name + "_USER_DIR").c_str());
132 if (app_home_env)
133 {
134 // user has specified own userappdir i.e. $SECONDLIFE_USER_DIR
135 mOSUserAppDir = app_home_env;
136 }
137 else
138 {
139 // traditionally on unixoids, MyApp gets ~/.myapp dir for data
140 mOSUserAppDir = mOSUserDir;
141 mOSUserAppDir += "/";
142 mOSUserAppDir += ".";
143 LLString lower_app_name(app_name);
144 LLString::toLower(lower_app_name);
145 mOSUserAppDir += lower_app_name;
146 }
147
148 // create any directories we expect to write to.
149
150 int res = LLFile::mkdir(mOSUserAppDir.c_str());
151 if (res == -1)
152 {
153 if (errno != EEXIST)
154 {
155 llwarns << "Couldn't create app user dir " << mOSUserAppDir << llendl;
156 llwarns << "Default to base dir" << mOSUserDir << llendl;
157 mOSUserAppDir = mOSUserDir;
158 }
159 }
160
161 res = LLFile::mkdir(getExpandedFilename(LL_PATH_LOGS,"").c_str());
162 if (res == -1)
163 {
164 if (errno != EEXIST)
165 {
166 llwarns << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << llendl;
167 }
168 }
169
170 res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS,"").c_str());
171 if (res == -1)
172 {
173 if (errno != EEXIST)
174 {
175 llwarns << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << llendl;
176 }
177 }
178
179 res = LLFile::mkdir(getExpandedFilename(LL_PATH_CACHE,"").c_str());
180 if (res == -1)
181 {
182 if (errno != EEXIST)
183 {
184 llwarns << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << llendl;
185 }
186 }
187
188 res = LLFile::mkdir(getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"").c_str());
189 if (res == -1)
190 {
191 if (errno != EEXIST)
192 {
193 llwarns << "Couldn't create LL_PATH_MOZILLA_PROFILE dir " << getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"") << llendl;
194 }
195 }
196
197 mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem");
198}
199
200U32 LLDir_Linux::countFilesInDir(const std::string &dirname, const std::string &mask)
201{
202 U32 file_count = 0;
203 glob_t g;
204
205 std::string tmp_str;
206 tmp_str = dirname;
207 tmp_str += mask;
208
209 if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
210 {
211 file_count = g.gl_pathc;
212
213 globfree(&g);
214 }
215
216 return (file_count);
217}
218
219// get the next file in the directory
220// automatically wrap if we've hit the end
221BOOL LLDir_Linux::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap)
222{
223 glob_t g;
224 BOOL result = FALSE;
225 fname = "";
226
227 if(!(dirname == mCurrentDir))
228 {
229 // different dir specified, close old search
230 mCurrentDirIndex = -1;
231 mCurrentDirCount = -1;
232 mCurrentDir = dirname;
233 }
234
235 std::string tmp_str;
236 tmp_str = dirname;
237 tmp_str += mask;
238
239 if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
240 {
241 if(g.gl_pathc > 0)
242 {
243 if((int)g.gl_pathc != mCurrentDirCount)
244 {
245 // Number of matches has changed since the last search, meaning a file has been added or deleted.
246 // Reset the index.
247 mCurrentDirIndex = -1;
248 mCurrentDirCount = g.gl_pathc;
249 }
250
251 mCurrentDirIndex++;
252
253 if((mCurrentDirIndex >= (int)g.gl_pathc) && wrap)
254 {
255 mCurrentDirIndex = 0;
256 }
257
258 if(mCurrentDirIndex < (int)g.gl_pathc)
259 {
260// llinfos << "getNextFileInDir: returning number " << mCurrentDirIndex << ", path is " << g.gl_pathv[mCurrentDirIndex] << llendl;
261
262 // The API wants just the filename, not the full path.
263 //fname = g.gl_pathv[mCurrentDirIndex];
264
265 char *s = strrchr(g.gl_pathv[mCurrentDirIndex], '/');
266
267 if(s == NULL)
268 s = g.gl_pathv[mCurrentDirIndex];
269 else if(s[0] == '/')
270 s++;
271
272 fname = s;
273
274 result = TRUE;
275 }
276 }
277
278 globfree(&g);
279 }
280
281 return(result);
282}
283
284
285// get a random file in the directory
286// automatically wrap if we've hit the end
287void LLDir_Linux::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname)
288{
289 U32 num_files;
290 U32 which_file;
291 DIR *dirp;
292 dirent *entryp = NULL;
293
294 fname = "";
295
296 num_files = countFilesInDir(dirname,mask);
297 if (!num_files)
298 {
299 return;
300 }
301
302 which_file = gLindenLabRandomNumber.llrand() % num_files;
303
304// llinfos << "Random select file #" << which_file << llendl;
305
306 // which_file now indicates the (zero-based) index to which file to play
307
308 if (!((dirp = opendir(dirname.c_str()))))
309 {
310 while (which_file--)
311 {
312 if (!((entryp = readdir(dirp))))
313 {
314 return;
315 }
316 }
317
318 if ((!which_file) && entryp)
319 {
320 fname = entryp->d_name;
321 }
322
323 closedir(dirp);
324 }
325}
326
327std::string LLDir_Linux::getCurPath()
328{
329 char tmp_str[LL_MAX_PATH];
330 getcwd(tmp_str, LL_MAX_PATH);
331 return tmp_str;
332}
333
334
335BOOL LLDir_Linux::fileExists(const std::string &filename)
336{
337 struct stat stat_data;
338 // Check the age of the file
339 // Now, we see if the files we've gathered are recent...
340 int res = stat(filename.c_str(), &stat_data);
341 if (!res)
342 {
343 return TRUE;
344 }
345 else
346 {
347 return FALSE;
348 }
349}
350
diff --git a/linden/indra/llvfs/lldir_linux.h b/linden/indra/llvfs/lldir_linux.h
new file mode 100644
index 0000000..488fda0
--- /dev/null
+++ b/linden/indra/llvfs/lldir_linux.h
@@ -0,0 +1,60 @@
1/**
2 * @file lldir_linux.h
3 * @brief Definition of directory utilities class for linux
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_LLDIR_LINUX_H
29#define LL_LLDIR_LINUX_H
30
31#include "lldir.h"
32
33#include <stdio.h>
34#include <dirent.h>
35#include <errno.h>
36
37class LLDir_Linux : public LLDir
38{
39public:
40 LLDir_Linux();
41 virtual ~LLDir_Linux();
42
43 virtual void initAppDirs(const std::string &app_name);
44public:
45 virtual std::string getCurPath();
46 virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask);
47 virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap);
48 virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname);
49 /*virtual*/ BOOL fileExists(const std::string &filename);
50
51private:
52 DIR *mDirp;
53 int mCurrentDirIndex;
54 int mCurrentDirCount;
55 std::string mCurrentDir;
56};
57
58#endif // LL_LLDIR_LINUX_H
59
60
diff --git a/linden/indra/llvfs/lldir_mac.cpp b/linden/indra/llvfs/lldir_mac.cpp
new file mode 100644
index 0000000..683b961
--- /dev/null
+++ b/linden/indra/llvfs/lldir_mac.cpp
@@ -0,0 +1,381 @@
1/**
2 * @file lldir_mac.cpp
3 * @brief Implementation of directory utilities for linux
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#if LL_DARWIN
29
30#include "linden_common.h"
31
32#include "lldir_mac.h"
33#include "llerror.h"
34#include "llrand.h"
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <unistd.h>
38#include <glob.h>
39
40#include <Carbon/Carbon.h>
41
42// --------------------------------------------------------------------------------
43
44static OSStatus CFCreateDirectory(FSRef *parentRef, CFStringRef name, FSRef *newRef)
45{
46 OSStatus result = noErr;
47 HFSUniStr255 uniStr;
48
49 uniStr.length = CFStringGetLength(name);
50 CFStringGetCharacters(name, CFRangeMake(0, uniStr.length), uniStr.unicode);
51 result = FSMakeFSRefUnicode(parentRef, uniStr.length, uniStr.unicode, kTextEncodingMacRoman, newRef);
52 if (result != noErr)
53 {
54 result = FSCreateDirectoryUnicode(parentRef, uniStr.length, uniStr.unicode, 0, NULL, newRef, NULL, NULL);
55 }
56
57 return result;
58}
59
60// --------------------------------------------------------------------------------
61
62static void CFStringRefToLLString(CFStringRef stringRef, std::string &llString, bool releaseWhenDone)
63{
64 if (stringRef)
65 {
66 long bufferSize = CFStringGetLength(stringRef) + 1;
67 char* buffer = new char[bufferSize];
68 memset(buffer, 0, bufferSize);
69 if (CFStringGetCString(stringRef, buffer, bufferSize, kCFStringEncodingUTF8))
70 llString = buffer;
71 delete[] buffer;
72 if (releaseWhenDone)
73 CFRelease(stringRef);
74 }
75}
76
77// --------------------------------------------------------------------------------
78
79static void CFURLRefToLLString(CFURLRef urlRef, std::string &llString, bool releaseWhenDone)
80{
81 if (urlRef)
82 {
83 CFURLRef absoluteURLRef = CFURLCopyAbsoluteURL(urlRef);
84 if (absoluteURLRef)
85 {
86 CFStringRef stringRef = CFURLCopyFileSystemPath(absoluteURLRef, kCFURLPOSIXPathStyle);
87 CFStringRefToLLString(stringRef, llString, true);
88 CFRelease(absoluteURLRef);
89 }
90 if (releaseWhenDone)
91 CFRelease(urlRef);
92 }
93}
94
95// --------------------------------------------------------------------------------
96
97static void FSRefToLLString(FSRef *fsRef, std::string &llString)
98{
99 OSStatus error = noErr;
100 char path[MAX_PATH];
101
102 error = FSRefMakePath(fsRef, (UInt8*) path, sizeof(path));
103 if (error == noErr)
104 llString = path;
105}
106
107// --------------------------------------------------------------------------------
108
109LLDir_Mac::LLDir_Mac()
110{
111 mDirDelimiter = "/";
112 mCurrentDirIndex = -1;
113 mCurrentDirCount = -1;
114
115 CFBundleRef mainBundleRef = NULL;
116 CFURLRef executableURLRef = NULL;
117 CFStringRef stringRef = NULL;
118 OSStatus error = noErr;
119 FSRef fileRef;
120 CFStringRef secondLifeString = CFSTR("SecondLife");
121
122 mainBundleRef = CFBundleGetMainBundle();
123
124 executableURLRef = CFBundleCopyExecutableURL(mainBundleRef);
125
126 if (executableURLRef != NULL)
127 {
128 // mExecutablePathAndName
129 CFURLRefToLLString(executableURLRef, mExecutablePathAndName, false);
130
131 // mExecutableFilename
132 stringRef = CFURLCopyLastPathComponent(executableURLRef);
133 CFStringRefToLLString(stringRef, mExecutableFilename, true);
134
135 // mExecutableDir
136 CFURLRef executableParentURLRef = CFURLCreateCopyDeletingLastPathComponent(NULL, executableURLRef);
137 CFURLRefToLLString(executableParentURLRef, mExecutableDir, true);
138
139 // mAppRODataDir
140 CFURLRef resourcesURLRef = CFBundleCopyResourcesDirectoryURL(mainBundleRef);
141 CFURLRefToLLString(resourcesURLRef, mAppRODataDir, true);
142
143 // mOSUserDir
144 error = FSFindFolder(kUserDomain, kApplicationSupportFolderType, true, &fileRef);
145 if (error == noErr)
146 {
147 FSRef newFileRef;
148
149 // Create the directory
150 error = CFCreateDirectory(&fileRef, secondLifeString, &newFileRef);
151 if (error == noErr)
152 {
153 // Save the full path to the folder
154 FSRefToLLString(&newFileRef, mOSUserDir);
155
156 // Create our sub-dirs
157 (void) CFCreateDirectory(&newFileRef, CFSTR("data"), NULL);
158 (void) CFCreateDirectory(&newFileRef, CFSTR("cache"), NULL);
159 (void) CFCreateDirectory(&newFileRef, CFSTR("logs"), NULL);
160 (void) CFCreateDirectory(&newFileRef, CFSTR("user_settings"), NULL);
161 (void) CFCreateDirectory(&newFileRef, CFSTR("browser_profile"), NULL);
162 }
163 }
164
165 // mOSUserAppDir
166 mOSUserAppDir = mOSUserDir;
167
168 // mTempDir
169 error = FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType, true, &fileRef);
170 if (error == noErr)
171 {
172 FSRef tempRef;
173 error = CFCreateDirectory(&fileRef, secondLifeString, &tempRef);
174 if (error == noErr)
175 FSRefToLLString(&tempRef, mTempDir);
176 }
177
178 // Set the working dir to <bundle>/Contents/Resources
179 (void) chdir(mAppRODataDir.c_str());
180
181 // Canonically, since we set it here...
182 mWorkingDir = mAppRODataDir;
183
184 CFRelease(executableURLRef);
185 executableURLRef = NULL;
186 }
187}
188
189LLDir_Mac::~LLDir_Mac()
190{
191}
192
193// Implementation
194
195
196void LLDir_Mac::initAppDirs(const std::string &app_name)
197{
198 mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem");
199
200 //dumpCurrentDirectories();
201}
202
203U32 LLDir_Mac::countFilesInDir(const std::string &dirname, const std::string &mask)
204{
205 U32 file_count = 0;
206 glob_t g;
207
208 std::string tmp_str;
209 tmp_str = dirname;
210 tmp_str += mask;
211
212 if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
213 {
214 file_count = g.gl_pathc;
215
216 globfree(&g);
217 }
218
219 return (file_count);
220}
221
222// get the next file in the directory
223// automatically wrap if we've hit the end
224BOOL LLDir_Mac::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap)
225{
226 glob_t g;
227 BOOL result = FALSE;
228 fname = "";
229
230 if(!(dirname == mCurrentDir))
231 {
232 // different dir specified, close old search
233 mCurrentDirIndex = -1;
234 mCurrentDirCount = -1;
235 mCurrentDir = dirname;
236 }
237
238 std::string tmp_str;
239 tmp_str = dirname;
240 tmp_str += mask;
241
242 if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
243 {
244 if(g.gl_pathc > 0)
245 {
246 if(g.gl_pathc != mCurrentDirCount)
247 {
248 // Number of matches has changed since the last search, meaning a file has been added or deleted.
249 // Reset the index.
250 mCurrentDirIndex = -1;
251 mCurrentDirCount = g.gl_pathc;
252 }
253
254 mCurrentDirIndex++;
255
256 if((mCurrentDirIndex >= g.gl_pathc) && wrap)
257 {
258 mCurrentDirIndex = 0;
259 }
260
261 if(mCurrentDirIndex < g.gl_pathc)
262 {
263// llinfos << "getNextFileInDir: returning number " << mCurrentDirIndex << ", path is " << g.gl_pathv[mCurrentDirIndex] << llendl;
264
265 // The API wants just the filename, not the full path.
266 //fname = g.gl_pathv[mCurrentDirIndex];
267
268 char *s = strrchr(g.gl_pathv[mCurrentDirIndex], '/');
269
270 if(s == NULL)
271 s = g.gl_pathv[mCurrentDirIndex];
272 else if(s[0] == '/')
273 s++;
274
275 fname = s;
276
277 result = TRUE;
278 }
279 }
280
281 globfree(&g);
282 }
283
284 return(result);
285}
286
287// get a random file in the directory
288void LLDir_Mac::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname)
289{
290 U32 which_file;
291 glob_t g;
292 fname = "";
293
294 std::string tmp_str;
295 tmp_str = dirname;
296 tmp_str += mask;
297
298 if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
299 {
300 if(g.gl_pathc > 0)
301 {
302
303 which_file = gLindenLabRandomNumber.llrand() % g.gl_pathc;
304
305// llinfos << "getRandomFileInDir: returning number " << which_file << ", path is " << g.gl_pathv[which_file] << llendl;
306 // The API wants just the filename, not the full path.
307 //fname = g.gl_pathv[which_file];
308
309 char *s = strrchr(g.gl_pathv[which_file], '/');
310
311 if(s == NULL)
312 s = g.gl_pathv[which_file];
313 else if(s[0] == '/')
314 s++;
315
316 fname = s;
317 }
318
319 globfree(&g);
320 }
321}
322
323S32 LLDir_Mac::deleteFilesInDir(const std::string &dirname, const std::string &mask)
324{
325 glob_t g;
326 S32 result = 0;
327
328 std::string tmp_str;
329 tmp_str = dirname;
330 tmp_str += mask;
331
332 if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
333 {
334 int i;
335
336 for(i = 0; i < g.gl_pathc; i++)
337 {
338// llinfos << "deleteFilesInDir: deleting number " << i << ", path is " << g.gl_pathv[i] << llendl;
339
340 if(unlink(g.gl_pathv[i]) != 0)
341 {
342 result = errno;
343
344 llwarns << "Problem removing " << g.gl_pathv[i] << " - errorcode: "
345 << result << llendl;
346 }
347 }
348
349 globfree(&g);
350 }
351
352 return(result);
353}
354
355std::string LLDir_Mac::getCurPath()
356{
357 char tmp_str[LL_MAX_PATH];
358 getcwd(tmp_str, LL_MAX_PATH);
359 return tmp_str;
360}
361
362
363
364BOOL LLDir_Mac::fileExists(const std::string &filename)
365{
366 struct stat stat_data;
367 // Check the age of the file
368 // Now, we see if the files we've gathered are recent...
369 int res = stat(filename.c_str(), &stat_data);
370 if (!res)
371 {
372 return TRUE;
373 }
374 else
375 {
376 return FALSE;
377 }
378}
379
380
381#endif // LL_DARWIN
diff --git a/linden/indra/llvfs/lldir_mac.h b/linden/indra/llvfs/lldir_mac.h
new file mode 100644
index 0000000..83c22b3
--- /dev/null
+++ b/linden/indra/llvfs/lldir_mac.h
@@ -0,0 +1,59 @@
1/**
2 * @file lldir_mac.h
3 * @brief Definition of directory utilities class for Mac OS X
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_LLDIR_MAC_H
29#define LL_LLDIR_MAC_H
30
31#include "lldir.h"
32
33#include <stdio.h>
34#include <dirent.h>
35
36class LLDir_Mac : public LLDir
37{
38public:
39 LLDir_Mac();
40 virtual ~LLDir_Mac();
41
42 virtual void initAppDirs(const std::string &app_name);
43public:
44 virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask);
45 virtual std::string getCurPath();
46 virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask);
47 virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap);
48 virtual void getRandomFileInDir(const std::string &dirname, const std::string &ask, std::string &fname);
49 virtual BOOL fileExists(const std::string &filename);
50
51private:
52 int mCurrentDirIndex;
53 int mCurrentDirCount;
54 std::string mCurrentDir;
55};
56
57#endif // LL_LLDIR_MAC_H
58
59
diff --git a/linden/indra/llvfs/lldir_win32.cpp b/linden/indra/llvfs/lldir_win32.cpp
new file mode 100644
index 0000000..8dcaa5c
--- /dev/null
+++ b/linden/indra/llvfs/lldir_win32.cpp
@@ -0,0 +1,401 @@
1/**
2 * @file lldir_win32.cpp
3 * @brief Implementation of directory utilities for windows
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#if LL_WINDOWS
29
30#include "linden_common.h"
31
32#include "lldir_win32.h"
33#include "llerror.h"
34#include "llrand.h" // for gLindenLabRandomNumber
35#include "shlobj.h"
36
37#include <direct.h>
38#include <sys/types.h>
39#include <sys/stat.h>
40
41// Utility stuff to get versions of the sh
42#define PACKVERSION(major,minor) MAKELONG(minor,major)
43DWORD GetDllVersion(LPCTSTR lpszDllName);
44
45LLDir_Win32::LLDir_Win32()
46{
47 mDirDelimiter = "\\";
48
49 WCHAR w_str[MAX_PATH];
50
51 // Application Data is where user settings go
52 SHGetSpecialFolderPath(NULL, w_str, CSIDL_APPDATA, TRUE);
53
54 mOSUserDir = utf16str_to_utf8str(llutf16string(w_str));
55
56 // Local Settings\Application Data is where cache files should
57 // go, they don't get copied to the server if the user moves his
58 // profile around on the network. JC
59 //
60 // TODO: patch the installer to remove old cache files on update, then
61 // enable this code.
62 //SHGetSpecialFolderPath(NULL, w_str, CSIDL_LOCAL_APPDATA, TRUE);
63 //mOSUserCacheDir = utf16str_to_utf8str(llutf16string(w_str));
64
65 if (GetTempPath(MAX_PATH, w_str))
66 {
67 if (wcslen(w_str))
68 {
69 w_str[wcslen(w_str)-1] = '\0'; // remove trailing slash
70 }
71 mTempDir = utf16str_to_utf8str(llutf16string(w_str));
72 }
73 else
74 {
75 mTempDir = mOSUserDir;
76 }
77
78// fprintf(stderr, "mTempDir = <%s>",mTempDir);
79
80#if 1
81 // Don't use the real app path for now, as we'll have to add parsing to detect if
82 // we're in a developer tree, which has a different structure from the installed product.
83
84 S32 size = GetModuleFileName(NULL, w_str, MAX_PATH);
85 if (size)
86 {
87 w_str[size] = '\0';
88 mExecutablePathAndName = utf16str_to_utf8str(llutf16string(w_str));
89 S32 path_end = mExecutablePathAndName.find_last_of('\\');
90 if (path_end != std::string::npos)
91 {
92 mExecutableDir = mExecutablePathAndName.substr(0, path_end);
93 mExecutableFilename = mExecutablePathAndName.substr(path_end+1, std::string::npos);
94 }
95 else
96 {
97 mExecutableFilename = mExecutablePathAndName;
98 }
99 GetCurrentDirectory(MAX_PATH, w_str);
100 mWorkingDir = utf16str_to_utf8str(llutf16string(w_str));
101
102 }
103 else
104 {
105 fprintf(stderr, "Couldn't get APP path, assuming current directory!");
106 GetCurrentDirectory(MAX_PATH, w_str);
107 mExecutableDir = utf16str_to_utf8str(llutf16string(w_str));
108 // Assume it's the current directory
109 }
110#else
111 GetCurrentDirectory(MAX_PATH, w_str);
112 mExecutableDir = utf16str_to_utf8str(llutf16string(w_str));
113#endif
114 if (strstr(mExecutableDir.c_str(), "indra\\newview"))
115 mAppRODataDir = getCurPath();
116 else
117 mAppRODataDir = mExecutableDir;
118}
119
120LLDir_Win32::~LLDir_Win32()
121{
122}
123
124// Implementation
125
126void LLDir_Win32::initAppDirs(const std::string &app_name)
127{
128 mAppName = app_name;
129 mOSUserAppDir = mOSUserDir;
130 mOSUserAppDir += "\\";
131 mOSUserAppDir += app_name;
132
133 int res = LLFile::mkdir(mOSUserAppDir.c_str());
134 if (res == -1)
135 {
136 if (errno != EEXIST)
137 {
138 llwarns << "Couldn't create app user dir " << mOSUserAppDir << llendl;
139 llwarns << "Default to base dir" << mOSUserDir << llendl;
140 mOSUserAppDir = mOSUserDir;
141 }
142 }
143 //dumpCurrentDirectories();
144
145 res = LLFile::mkdir(getExpandedFilename(LL_PATH_LOGS,"").c_str());
146 if (res == -1)
147 {
148 if (errno != EEXIST)
149 {
150 llwarns << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << llendl;
151 }
152 }
153
154 res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS,"").c_str());
155 if (res == -1)
156 {
157 if (errno != EEXIST)
158 {
159 llwarns << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << llendl;
160 }
161 }
162
163 res = LLFile::mkdir(getExpandedFilename(LL_PATH_CACHE,"").c_str());
164 if (res == -1)
165 {
166 if (errno != EEXIST)
167 {
168 llwarns << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << llendl;
169 }
170 }
171
172 res = LLFile::mkdir(getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"").c_str());
173 if (res == -1)
174 {
175 if (errno != EEXIST)
176 {
177 llwarns << "Couldn't create LL_PATH_MOZILLA_PROFILE dir " << getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"") << llendl;
178 }
179 }
180
181 mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem");
182}
183
184U32 LLDir_Win32::countFilesInDir(const std::string &dirname, const std::string &mask)
185{
186 HANDLE count_search_h;
187 U32 file_count;
188
189 file_count = 0;
190
191 WIN32_FIND_DATA FileData;
192
193 llutf16string pathname = utf8str_to_utf16str(dirname);
194 pathname += utf8str_to_utf16str(mask);
195
196 if ((count_search_h = FindFirstFile(pathname.c_str(), &FileData)) != INVALID_HANDLE_VALUE)
197 {
198 file_count++;
199
200 while (FindNextFile(count_search_h, &FileData))
201 {
202 file_count++;
203 }
204
205 FindClose(count_search_h);
206 }
207
208 return (file_count);
209}
210
211
212// get the next file in the directory
213// automatically wrap if we've hit the end
214BOOL LLDir_Win32::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap)
215{
216 llutf16string dirnamew = utf8str_to_utf16str(dirname);
217 return getNextFileInDir(dirnamew, mask, fname, wrap);
218
219}
220
221BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname, BOOL wrap)
222{
223 WIN32_FIND_DATAW FileData;
224
225 fname = "";
226 llutf16string pathname = dirname;
227 pathname += utf8str_to_utf16str(mask);
228
229 if (pathname != mCurrentDir)
230 {
231 // different dir specified, close old search
232 if (mCurrentDir[0])
233 {
234 FindClose(mDirSearch_h);
235 }
236 mCurrentDir = pathname;
237
238 // and open new one
239 // Check error opening Directory structure
240 if ((mDirSearch_h = FindFirstFile(pathname.c_str(), &FileData)) == INVALID_HANDLE_VALUE)
241 {
242// llinfos << "Unable to locate first file" << llendl;
243 return(FALSE);
244 }
245 }
246 else // get next file in list
247 {
248 // Find next entry
249 if (!FindNextFile(mDirSearch_h, &FileData))
250 {
251 if (GetLastError() == ERROR_NO_MORE_FILES)
252 {
253 // No more files, so reset to beginning of directory
254 FindClose(mDirSearch_h);
255 mCurrentDir[0] = NULL;
256
257 if (wrap)
258 {
259 return(getNextFileInDir(pathname,"",fname,TRUE));
260 }
261 else
262 {
263 fname[0] = 0;
264 return(FALSE);
265 }
266 }
267 else
268 {
269 // Error
270// llinfos << "Unable to locate next file" << llendl;
271 return(FALSE);
272 }
273 }
274 }
275
276 // convert from TCHAR to char
277 fname = utf16str_to_utf8str(FileData.cFileName);
278
279 // fname now first name in list
280 return(TRUE);
281}
282
283
284// get a random file in the directory
285// automatically wrap if we've hit the end
286void LLDir_Win32::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname)
287{
288 U32 num_files;
289 U32 which_file;
290 HANDLE random_search_h;
291
292 fname = "";
293
294 llutf16string pathname = utf8str_to_utf16str(dirname);
295 pathname += utf8str_to_utf16str(mask);
296
297 WIN32_FIND_DATA FileData;
298 fname[0] = NULL;
299
300 num_files = countFilesInDir(dirname,mask);
301 if (!num_files)
302 {
303 return;
304 }
305
306 which_file = gLindenLabRandomNumber.llrand() % num_files;
307
308// llinfos << "Random select mp3 #" << which_file << llendl;
309
310 // which_file now indicates the (zero-based) index to which file to play
311
312 if ((random_search_h = FindFirstFile(pathname.c_str(), &FileData)) != INVALID_HANDLE_VALUE)
313 {
314 while (which_file--)
315 {
316 if (!FindNextFile(random_search_h, &FileData))
317 {
318 return;
319 }
320 }
321 FindClose(random_search_h);
322
323 fname = utf16str_to_utf8str(llutf16string(FileData.cFileName));
324 }
325}
326
327std::string LLDir_Win32::getCurPath()
328{
329 WCHAR w_str[MAX_PATH];
330 GetCurrentDirectory(MAX_PATH, w_str);
331
332 return utf16str_to_utf8str(llutf16string(w_str));
333}
334
335
336BOOL LLDir_Win32::fileExists(const std::string &filename)
337{
338 llstat stat_data;
339 // Check the age of the file
340 // Now, we see if the files we've gathered are recent...
341 int res = LLFile::stat(filename.c_str(), &stat_data);
342 if (!res)
343 {
344 return TRUE;
345 }
346 else
347 {
348 return FALSE;
349 }
350}
351
352
353#if 0
354// Utility function to get version number of a DLL
355
356#define PACKVERSION(major,minor) MAKELONG(minor,major)
357
358DWORD GetDllVersion(LPCTSTR lpszDllName)
359{
360
361 HINSTANCE hinstDll;
362 DWORD dwVersion = 0;
363
364 hinstDll = LoadLibrary(lpszDllName);
365
366 if(hinstDll)
367 {
368 DLLGETVERSIONPROC pDllGetVersion;
369
370 pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hinstDll, "DllGetVersion");
371
372/*Because some DLLs might not implement this function, you
373 must test for it explicitly. Depending on the particular
374 DLL, the lack of a DllGetVersion function can be a useful
375 indicator of the version.
376*/
377 if(pDllGetVersion)
378 {
379 DLLVERSIONINFO dvi;
380 HRESULT hr;
381
382 ZeroMemory(&dvi, sizeof(dvi));
383 dvi.cbSize = sizeof(dvi);
384
385 hr = (*pDllGetVersion)(&dvi);
386
387 if(SUCCEEDED(hr))
388 {
389 dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
390 }
391 }
392
393 FreeLibrary(hinstDll);
394 }
395 return dwVersion;
396}
397#endif
398
399#endif
400
401
diff --git a/linden/indra/llvfs/lldir_win32.h b/linden/indra/llvfs/lldir_win32.h
new file mode 100644
index 0000000..40eac36
--- /dev/null
+++ b/linden/indra/llvfs/lldir_win32.h
@@ -0,0 +1,56 @@
1/**
2 * @file lldir_win32.h
3 * @brief Definition of directory utilities class for windows
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_LLDIR_WIN32_H
29#define LL_LLDIR_WIN32_H
30
31#include "lldir.h"
32
33class LLDir_Win32 : public LLDir
34{
35public:
36 LLDir_Win32();
37 virtual ~LLDir_Win32();
38
39 /*virtual*/ void initAppDirs(const std::string &app_name);
40
41 /*virtual*/ std::string getCurPath();
42 /*virtual*/ U32 countFilesInDir(const std::string &dirname, const std::string &mask);
43 /*virtual*/ BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap);
44 /*virtual*/ void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname);
45 /*virtual*/ BOOL fileExists(const std::string &filename);
46
47private:
48 BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname, BOOL wrap);
49
50 void* mDirSearch_h;
51 llutf16string mCurrentDir;
52};
53
54#endif // LL_LLDIR_WIN32_H
55
56
diff --git a/linden/indra/llvfs/lllfsthread.cpp b/linden/indra/llvfs/lllfsthread.cpp
new file mode 100644
index 0000000..b7335e2
--- /dev/null
+++ b/linden/indra/llvfs/lllfsthread.cpp
@@ -0,0 +1,327 @@
1/**
2 * @file lllfsthread.cpp
3 * @brief LLLFSThread base class
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29#include "llmath.h"
30#include "lllfsthread.h"
31#include "llstl.h"
32#include "llapr.h"
33
34//============================================================================
35
36/*static*/ LLLFSThread* LLLFSThread::sLocal = NULL;
37
38//============================================================================
39// Run on MAIN thread
40//static
41void LLLFSThread::initClass(bool local_is_threaded, bool local_run_always)
42{
43 llassert(sLocal == NULL);
44 sLocal = new LLLFSThread(local_is_threaded, local_run_always);
45}
46
47//static
48S32 LLLFSThread::updateClass(U32 ms_elapsed)
49{
50 sLocal->update(ms_elapsed);
51 return sLocal->getPending();
52}
53
54//static
55void LLLFSThread::cleanupClass()
56{
57 sLocal->setQuitting();
58 while (sLocal->getPending())
59 {
60 sLocal->update(0);
61 }
62 delete sLocal;
63 sLocal = 0;
64}
65
66//----------------------------------------------------------------------------
67
68LLLFSThread::LLLFSThread(bool threaded, bool runalways) :
69 LLQueuedThread("LFS", threaded, runalways)
70{
71}
72
73LLLFSThread::~LLLFSThread()
74{
75 // ~LLQueuedThread() will be called here
76}
77
78//----------------------------------------------------------------------------
79
80LLLFSThread::handle_t LLLFSThread::read(const LLString& filename,
81 U8* buffer, S32 offset, S32 numbytes, U32 priority, U32 flags)
82{
83 handle_t handle = generateHandle();
84
85 priority = llmax(priority, (U32)PRIORITY_LOW); // All reads are at least PRIORITY_LOW
86 Request* req = new Request(handle, priority, flags,
87 FILE_READ, filename,
88 buffer, offset, numbytes);
89
90 bool res = addRequest(req);
91 if (!res)
92 {
93 llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl;
94 req->deleteRequest();
95 handle = nullHandle();
96 }
97
98 return handle;
99}
100
101S32 LLLFSThread::readImmediate(const LLString& filename,
102 U8* buffer, S32 offset, S32 numbytes)
103{
104 handle_t handle = generateHandle();
105
106 Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0,
107 FILE_READ, filename,
108 buffer, offset, numbytes);
109
110 S32 res = addRequest(req) ? 1 : 0;
111 if (res == 0)
112 {
113 llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl;
114 req->deleteRequest();
115 }
116 else
117 {
118 llverify(waitForResult(handle, false) == true);
119 res = req->getBytesRead();
120 completeRequest(handle);
121 }
122 return res;
123}
124
125LLLFSThread::handle_t LLLFSThread::write(const LLString& filename,
126 U8* buffer, S32 offset, S32 numbytes, U32 flags)
127{
128 handle_t handle = generateHandle();
129
130 Request* req = new Request(handle, 0, flags,
131 FILE_WRITE, filename,
132 buffer, offset, numbytes);
133
134 bool res = addRequest(req);
135 if (!res)
136 {
137 llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl;
138 req->deleteRequest();
139 handle = nullHandle();
140 }
141
142 return handle;
143}
144
145S32 LLLFSThread::writeImmediate(const LLString& filename,
146 U8* buffer, S32 offset, S32 numbytes)
147{
148 handle_t handle = generateHandle();
149
150 Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0,
151 FILE_WRITE, filename,
152 buffer, offset, numbytes);
153
154 S32 res = addRequest(req) ? 1 : 0;
155 if (res == 0)
156 {
157 llerrs << "LLLFSThread::write called after LLLFSThread::cleanupClass()" << llendl;
158 req->deleteRequest();
159 }
160 else
161 {
162 llverify(waitForResult(handle, false) == true);
163 res = req->getBytesRead();
164 completeRequest(handle);
165 }
166 return res;
167}
168
169
170LLLFSThread::handle_t LLLFSThread::rename(const LLString& filename, const LLString& newname, U32 flags)
171{
172 handle_t handle = generateHandle();
173
174 LLString* new_name_str = new LLString(newname); // deleted with Request
175 Request* req = new Request(handle, 0, flags,
176 FILE_RENAME, filename,
177 (U8*)new_name_str, 0, 0);
178
179 bool res = addRequest(req);
180 if (!res)
181 {
182 llerrs << "LLLFSThread::rename called after LLLFSThread::cleanupClass()" << llendl;
183 req->deleteRequest();
184 handle = nullHandle();
185 }
186
187 return handle;
188}
189
190LLLFSThread::handle_t LLLFSThread::remove(const LLString& filename, U32 flags)
191{
192 handle_t handle = generateHandle();
193
194 Request* req = new Request(handle, 0, flags,
195 FILE_RENAME, filename,
196 NULL, 0, 0);
197
198 bool res = addRequest(req);
199 if (!res)
200 {
201 llerrs << "LLLFSThread::remove called after LLLFSThread::cleanupClass()" << llendl;
202 req->deleteRequest();
203 handle = nullHandle();
204 }
205
206 return handle;
207}
208
209//============================================================================
210// Runs on its OWN thread
211
212bool LLLFSThread::processRequest(QueuedRequest* qreq)
213{
214 Request *req = (Request*)qreq;
215
216 bool complete = req->processIO();
217
218 return complete;
219}
220
221//============================================================================
222
223LLLFSThread::Request::Request(handle_t handle, U32 priority, U32 flags,
224 operation_t op, const LLString& filename,
225 U8* buffer, S32 offset, S32 numbytes) :
226 QueuedRequest(handle, priority, flags),
227 mOperation(op),
228 mFileName(filename),
229 mBuffer(buffer),
230 mOffset(offset),
231 mBytes(numbytes),
232 mBytesRead(0)
233{
234 llassert(mBuffer);
235
236 if (numbytes <= 0 && mOperation != FILE_RENAME && mOperation != FILE_REMOVE)
237 {
238 llwarns << "LLLFSThread: Request with numbytes = " << numbytes << llendl;
239 }
240}
241
242void LLLFSThread::Request::finishRequest()
243{
244}
245
246void LLLFSThread::Request::deleteRequest()
247{
248 if (getStatus() == STATUS_QUEUED || getStatus() == STATUS_ABORT)
249 {
250 llerrs << "Attempt to delete a queued LLLFSThread::Request!" << llendl;
251 }
252 if (mOperation == FILE_WRITE)
253 {
254 if (mFlags & AUTO_DELETE)
255 {
256 delete mBuffer;
257 }
258 }
259 else if (mOperation == FILE_RENAME)
260 {
261 LLString* new_name = (LLString*)mBuffer;
262 delete new_name;
263 }
264 LLQueuedThread::QueuedRequest::deleteRequest();
265}
266
267bool LLLFSThread::Request::processIO()
268{
269 bool complete = false;
270 if (mOperation == FILE_READ)
271 {
272 llassert(mOffset >= 0);
273 apr_file_t* filep = ll_apr_file_open(mFileName, LL_APR_RB);
274 if (!filep)
275 {
276 llwarns << "LLLFS: Unable to read file: " << mFileName << llendl;
277 mBytesRead = 0; // fail
278 return true;
279 }
280 if (mOffset < 0)
281 ll_apr_file_seek(filep, APR_END, 0);
282 else
283 ll_apr_file_seek(filep, APR_SET, mOffset);
284 mBytesRead = ll_apr_file_read(filep, mBuffer, mBytes );
285 apr_file_close(filep);
286 complete = true;
287 //llinfos << llformat("LLLFSThread::READ '%s': %d bytes",mFileName.c_str(),mBytesRead) << llendl;
288 }
289 else if (mOperation == FILE_WRITE)
290 {
291 apr_file_t* filep = ll_apr_file_open(mFileName, LL_APR_WB);
292 if (!filep)
293 {
294 llwarns << "LLLFS: Unable to write file: " << mFileName << llendl;
295 mBytesRead = 0; // fail
296 return true;
297 }
298 if (mOffset < 0)
299 ll_apr_file_seek(filep, APR_END, 0);
300 else
301 ll_apr_file_seek(filep, APR_SET, mOffset);
302 mBytesRead = ll_apr_file_write(filep, mBuffer, mBytes );
303 complete = true;
304 apr_file_close(filep);
305 //llinfos << llformat("LLLFSThread::WRITE '%s': %d bytes",mFileName.c_str(),mBytesRead) << llendl;
306 }
307 else if (mOperation == FILE_RENAME)
308 {
309 LLString* new_name = (LLString*)mBuffer;
310 ll_apr_file_rename(mFileName, *new_name);
311 complete = true;
312 //llinfos << llformat("LLLFSThread::RENAME '%s': '%s'",mFileName.c_str(),new_name->c_str()) << llendl;
313 }
314 else if (mOperation == FILE_REMOVE)
315 {
316 ll_apr_file_remove(mFileName);
317 complete = true;
318 //llinfos << llformat("LLLFSThread::REMOVE '%s'",mFileName.c_str()) << llendl;
319 }
320 else
321 {
322 llerrs << llformat("LLLFSThread::unknown operation: %d", mOperation) << llendl;
323 }
324 return complete;
325}
326
327//============================================================================
diff --git a/linden/indra/llvfs/lllfsthread.h b/linden/indra/llvfs/lllfsthread.h
new file mode 100644
index 0000000..8af66b1
--- /dev/null
+++ b/linden/indra/llvfs/lllfsthread.h
@@ -0,0 +1,138 @@
1/**
2 * @file lllfsthread.h
3 * @brief LLLFSThread base class
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_LLLFSTHREAD_H
29#define LL_LLLFSTHREAD_H
30
31#include <queue>
32#include <string>
33#include <map>
34#include <set>
35
36#include "llapr.h"
37
38#include "llqueuedthread.h"
39
40//============================================================================
41// Threaded Local File System
42//============================================================================
43
44class LLLFSThread : public LLQueuedThread
45{
46 //------------------------------------------------------------------------
47public:
48 enum operation_t {
49 FILE_READ,
50 FILE_WRITE,
51 FILE_RENAME,
52 FILE_REMOVE
53 };
54
55 //------------------------------------------------------------------------
56public:
57
58 class Request : public QueuedRequest
59 {
60 protected:
61 ~Request() {}; // use deleteRequest()
62
63 public:
64 Request(handle_t handle, U32 priority, U32 flags,
65 operation_t op, const LLString& filename,
66 U8* buffer, S32 offset, S32 numbytes);
67
68 S32 getBytes()
69 {
70 return mBytes;
71 }
72 S32 getBytesRead()
73 {
74 return mBytesRead;
75 }
76 S32 getOperation()
77 {
78 return mOperation;
79 }
80 U8* getBuffer()
81 {
82 return mBuffer;
83 }
84 const LLString& getFilename()
85 {
86 return mFileName;
87 }
88
89 /*virtual*/ void finishRequest();
90 /*virtual*/ void deleteRequest();
91
92 bool processIO();
93
94 private:
95 operation_t mOperation;
96
97 LLString mFileName;
98
99 U8* mBuffer; // dest for reads, source for writes, new UUID for rename
100 S32 mOffset; // offset into file, -1 = append (WRITE only)
101 S32 mBytes; // bytes to read from file, -1 = all
102 S32 mBytesRead; // bytes read from file
103 };
104
105 //------------------------------------------------------------------------
106public:
107 LLLFSThread(bool threaded = TRUE, bool runalways = TRUE);
108 ~LLLFSThread();
109
110 // Return a Request handle
111 handle_t read(const LLString& filename,
112 U8* buffer, S32 offset, S32 numbytes, U32 pri=PRIORITY_NORMAL, U32 flags = 0);
113 handle_t write(const LLString& filename,
114 U8* buffer, S32 offset, S32 numbytes, U32 flags = 0);
115 handle_t rename(const LLString& filename, const LLString& newname, U32 flags = 0);
116 handle_t remove(const LLString& filename, U32 flags = 0);
117
118 // Return number of bytes read
119 S32 readImmediate(const LLString& filename,
120 U8* buffer, S32 offset, S32 numbytes);
121 S32 writeImmediate(const LLString& filename,
122 U8* buffer, S32 offset, S32 numbytes);
123
124 static void initClass(bool local_is_threaded = TRUE, bool run_always = TRUE); // Setup sLocal
125 static S32 updateClass(U32 ms_elapsed);
126 static void cleanupClass(); // Delete sLocal
127
128protected:
129 /*virtual*/ bool processRequest(QueuedRequest* req);
130
131public:
132 static LLLFSThread* sLocal; // Default local file thread
133};
134
135//============================================================================
136
137
138#endif // LL_LLLFSTHREAD_H
diff --git a/linden/indra/llvfs/llvfile.cpp b/linden/indra/llvfs/llvfile.cpp
new file mode 100644
index 0000000..2faa5c3
--- /dev/null
+++ b/linden/indra/llvfs/llvfile.cpp
@@ -0,0 +1,434 @@
1/**
2 * @file llvfile.cpp
3 * @brief Implementation of virtual file
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include "llvfile.h"
31
32#include "llerror.h"
33#include "llthread.h"
34#include "llvfs.h"
35
36const S32 LLVFile::READ = 0x00000001;
37const S32 LLVFile::WRITE = 0x00000002;
38const S32 LLVFile::READ_WRITE = 0x00000003; // LLVFile::READ & LLVFile::WRITE
39const S32 LLVFile::APPEND = 0x00000006; // 0x00000004 & LLVFile::WRITE
40
41//----------------------------------------------------------------------------
42LLVFSThread* LLVFile::sVFSThread = NULL;
43BOOL LLVFile::sAllocdVFSThread = FALSE;
44BOOL LLVFile::ALLOW_ASYNC = TRUE;
45//----------------------------------------------------------------------------
46
47//============================================================================
48
49LLVFile::LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode)
50{
51 mFileType = file_type;
52
53 mFileID = file_id;
54 mPosition = 0;
55 mMode = mode;
56 mVFS = vfs;
57
58 mBytesRead = 0;
59 mHandle = LLVFSThread::nullHandle();
60 mPriority = 128.f;
61
62 mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN);
63}
64
65LLVFile::~LLVFile()
66{
67 if (!isReadComplete())
68 {
69 if (mHandle != LLVFSThread::nullHandle())
70 {
71 if (!(mMode & LLVFile::WRITE))
72 {
73 // llwarns << "Destroying LLVFile with pending async read/write, aborting..." << llendl;
74 sVFSThread->abortRequest(mHandle, LLVFSThread::AUTO_COMPLETE);
75 }
76 else // WRITE
77 {
78 sVFSThread->setFlags(mHandle, LLVFSThread::AUTO_COMPLETE);
79 }
80 }
81 }
82 mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
83}
84
85BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority)
86{
87 if (! (mMode & READ))
88 {
89 llwarns << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
90 return FALSE;
91 }
92
93 if (mHandle != LLVFSThread::nullHandle())
94 {
95 llwarns << "Attempt to read from vfile object " << mFileID << " with pending async operation" << llendl;
96 return FALSE;
97 }
98 mPriority = priority;
99
100 BOOL success = TRUE;
101
102 // We can't do a read while there are pending async writes
103 waitForLock(VFSLOCK_APPEND);
104
105 // *FIX: (???)
106 if (async)
107 {
108 mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri());
109 }
110 else
111 {
112 // We can't do a read while there are pending async writes on this file
113 mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes);
114 mPosition += mBytesRead;
115 if (! mBytesRead)
116 {
117 success = FALSE;
118 }
119 }
120
121 return success;
122}
123
124//static
125U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read)
126{
127 U8 *data;
128 LLVFile file(vfs, uuid, type, LLVFile::READ);
129 S32 file_size = file.getSize();
130 if (file_size == 0)
131 {
132 // File is empty.
133 data = NULL;
134 }
135 else
136 {
137 data = new U8[file_size];
138 file.read(data, file_size);
139
140 if (file.getLastBytesRead() != (S32)file_size)
141 {
142 delete[] data;
143 data = NULL;
144 file_size = 0;
145 }
146 }
147 if (bytes_read)
148 {
149 *bytes_read = file_size;
150 }
151 return data;
152}
153
154void LLVFile::setReadPriority(const F32 priority)
155{
156 mPriority = priority;
157 if (mHandle != LLVFSThread::nullHandle())
158 {
159 sVFSThread->setPriority(mHandle, threadPri());
160 }
161}
162
163BOOL LLVFile::isReadComplete()
164{
165 BOOL res = TRUE;
166 if (mHandle != LLVFSThread::nullHandle())
167 {
168 LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle);
169 LLVFSThread::status_t status = req->getStatus();
170 if (status == LLVFSThread::STATUS_COMPLETE)
171 {
172 mBytesRead = req->getBytesRead();
173 mPosition += mBytesRead;
174 sVFSThread->completeRequest(mHandle);
175 mHandle = LLVFSThread::nullHandle();
176 }
177 else
178 {
179 res = FALSE;
180 }
181 }
182 return res;
183}
184
185S32 LLVFile::getLastBytesRead()
186{
187 return mBytesRead;
188}
189
190BOOL LLVFile::eof()
191{
192 return mPosition >= getSize();
193}
194
195BOOL LLVFile::write(const U8 *buffer, S32 bytes)
196{
197 if (! (mMode & WRITE))
198 {
199 llwarns << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
200 }
201 if (mHandle != LLVFSThread::nullHandle())
202 {
203 llerrs << "Attempt to write to vfile object " << mFileID << " with pending async operation" << llendl;
204 return FALSE;
205 }
206 BOOL success = TRUE;
207
208 // *FIX: allow async writes? potential problem wit mPosition...
209 if (mMode == APPEND) // all appends are async (but WRITEs are not)
210 {
211 U8* writebuf = new U8[bytes];
212 memcpy(writebuf, buffer, bytes);
213 S32 offset = -1;
214 mHandle = sVFSThread->write(mVFS, mFileID, mFileType,
215 writebuf, offset, bytes,
216 LLVFSThread::AUTO_COMPLETE | LLVFSThread::AUTO_DELETE);
217 mHandle = LLVFSThread::nullHandle(); // AUTO_COMPLETE means we don't track this
218 }
219 else
220 {
221 // We can't do a write while there are pending reads or writes on this file
222 waitForLock(VFSLOCK_READ);
223 waitForLock(VFSLOCK_APPEND);
224
225 S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition;
226
227 S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes);
228
229 mPosition += wrote;
230
231 if (wrote < bytes)
232 {
233 llwarns << "Tried to write " << bytes << " bytes, actually wrote " << wrote << llendl;
234
235 success = FALSE;
236 }
237 }
238 return success;
239}
240
241//static
242BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
243{
244 LLVFile file(vfs, uuid, type, LLVFile::WRITE);
245 file.setMaxSize(bytes);
246 return file.write(buffer, bytes);
247}
248
249BOOL LLVFile::seek(S32 offset, S32 origin)
250{
251 if (mMode == APPEND)
252 {
253 llwarns << "Attempt to seek on append-only file" << llendl;
254 return FALSE;
255 }
256
257 if (-1 == origin)
258 {
259 origin = mPosition;
260 }
261
262 S32 new_pos = origin + offset;
263
264 S32 size = getSize(); // Calls waitForLock(VFSLOCK_APPEND)
265
266 if (new_pos > size)
267 {
268 llwarns << "Attempt to seek past end of file" << llendl;
269
270 mPosition = size;
271 return FALSE;
272 }
273 else if (new_pos < 0)
274 {
275 llwarns << "Attempt to seek past beginning of file" << llendl;
276
277 mPosition = 0;
278 return FALSE;
279 }
280
281 mPosition = new_pos;
282 return TRUE;
283}
284
285S32 LLVFile::tell() const
286{
287 return mPosition;
288}
289
290S32 LLVFile::getSize()
291{
292 waitForLock(VFSLOCK_APPEND);
293 S32 size = mVFS->getSize(mFileID, mFileType);
294
295 return size;
296}
297
298S32 LLVFile::getMaxSize()
299{
300 S32 size = mVFS->getMaxSize(mFileID, mFileType);
301
302 return size;
303}
304
305BOOL LLVFile::setMaxSize(S32 size)
306{
307 if (! (mMode & WRITE))
308 {
309 llwarns << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
310
311 return FALSE;
312 }
313
314 if (!mVFS->checkAvailable(size))
315 {
316 LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT);
317 S32 count = 0;
318 while (sVFSThread->getPending() > 1000)
319 {
320 if (count % 100 == 0)
321 {
322 llinfos << "VFS catching up... Pending: " << sVFSThread->getPending() << llendl;
323 }
324 if (sVFSThread->isPaused())
325 {
326 sVFSThread->updateQueue(0);
327 }
328 ms_sleep(10);
329 }
330 }
331 return mVFS->setMaxSize(mFileID, mFileType, size);
332}
333
334BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type)
335{
336 if (! (mMode & WRITE))
337 {
338 llwarns << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
339
340 return FALSE;
341 }
342
343 if (mHandle != LLVFSThread::nullHandle())
344 {
345 llwarns << "Renaming file with pending async read" << llendl;
346 }
347
348 waitForLock(VFSLOCK_READ);
349 waitForLock(VFSLOCK_APPEND);
350
351 // we need to release / replace our own lock
352 // since the renamed file will inherit locks from the new name
353 mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
354 mVFS->renameFile(mFileID, mFileType, new_id, new_type);
355 mVFS->incLock(new_id, new_type, VFSLOCK_OPEN);
356
357 mFileID = new_id;
358 mFileType = new_type;
359
360 return TRUE;
361}
362
363BOOL LLVFile::remove()
364{
365// llinfos << "Removing file " << mFileID << llendl;
366
367 if (! (mMode & WRITE))
368 {
369 // Leaving paranoia warning just because this should be a very infrequent
370 // operation.
371 llwarns << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
372 }
373
374 if (mHandle != LLVFSThread::nullHandle())
375 {
376 llwarns << "Removing file with pending async read" << llendl;
377 }
378
379 // why not seek back to the beginning of the file too?
380 mPosition = 0;
381
382 waitForLock(VFSLOCK_READ);
383 waitForLock(VFSLOCK_APPEND);
384 mVFS->removeFile(mFileID, mFileType);
385
386 return TRUE;
387}
388
389// static
390void LLVFile::initClass(LLVFSThread* vfsthread)
391{
392 if (!vfsthread)
393 {
394 if (LLVFSThread::sLocal != NULL)
395 {
396 vfsthread = LLVFSThread::sLocal;
397 }
398 else
399 {
400 vfsthread = new LLVFSThread();
401 sAllocdVFSThread = TRUE;
402 }
403 }
404 sVFSThread = vfsthread;
405}
406
407// static
408void LLVFile::cleanupClass()
409{
410 if (sAllocdVFSThread)
411 {
412 delete sVFSThread;
413 }
414 sVFSThread = NULL;
415}
416
417bool LLVFile::isLocked(EVFSLock lock)
418{
419 return mVFS->isLocked(mFileID, mFileType, lock) ? true : false;
420}
421
422void LLVFile::waitForLock(EVFSLock lock)
423{
424 LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT);
425 // spin until the lock clears
426 while (isLocked(lock))
427 {
428 if (sVFSThread->isPaused())
429 {
430 sVFSThread->updateQueue(0);
431 }
432 ms_sleep(1);
433 }
434}
diff --git a/linden/indra/llvfs/llvfile.h b/linden/indra/llvfs/llvfile.h
new file mode 100644
index 0000000..dcc3cb5
--- /dev/null
+++ b/linden/indra/llvfs/llvfile.h
@@ -0,0 +1,94 @@
1/**
2 * @file llvfile.h
3 * @brief Definition of virtual file
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_LLVFILE_H
29#define LL_LLVFILE_H
30
31#include "lluuid.h"
32#include "llassettype.h"
33#include "llvfs.h"
34#include "llvfsthread.h"
35
36class LLVFile
37{
38public:
39 LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLVFile::READ);
40 ~LLVFile();
41
42 BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f);
43 static U8* readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read = 0);
44 void setReadPriority(const F32 priority);
45 BOOL isReadComplete();
46 S32 getLastBytesRead();
47 BOOL eof();
48
49 BOOL write(const U8 *buffer, S32 bytes);
50 static BOOL writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type);
51 BOOL seek(S32 offset, S32 origin = -1);
52 S32 tell() const;
53
54 S32 getSize();
55 S32 getMaxSize();
56 BOOL setMaxSize(S32 size);
57 BOOL rename(const LLUUID &new_id, const LLAssetType::EType new_type);
58 BOOL remove();
59
60 bool isLocked(EVFSLock lock);
61 void waitForLock(EVFSLock lock);
62
63 static void initClass(LLVFSThread* vfsthread = NULL);
64 static void cleanupClass();
65 static LLVFSThread* getVFSThread() { return sVFSThread; }
66
67protected:
68 static LLVFSThread* sVFSThread;
69 static BOOL sAllocdVFSThread;
70 U32 threadPri() { return LLVFSThread::PRIORITY_NORMAL + llmin((U32)mPriority,(U32)0xfff); }
71
72public:
73 static const S32 READ;
74 static const S32 WRITE;
75 static const S32 READ_WRITE;
76 static const S32 APPEND;
77
78 static BOOL ALLOW_ASYNC;
79
80protected:
81 LLAssetType::EType mFileType;
82
83 LLUUID mFileID;
84 S32 mPosition;
85 S32 mMode;
86 LLVFS *mVFS;
87 F32 mPriority;
88 BOOL mOnReadQueue;
89
90 S32 mBytesRead;
91 LLVFSThread::handle_t mHandle;
92};
93
94#endif
diff --git a/linden/indra/llvfs/llvfs.cpp b/linden/indra/llvfs/llvfs.cpp
new file mode 100644
index 0000000..ed1c313
--- /dev/null
+++ b/linden/indra/llvfs/llvfs.cpp
@@ -0,0 +1,2071 @@
1/**
2 * @file llvfs.cpp
3 * @brief Implementation of virtual file system
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include <stdio.h>
31#include <sys/stat.h>
32#include <time.h>
33#include <set>
34#include <map>
35#if LL_WINDOWS
36#include <share.h>
37#else
38#include <sys/file.h>
39#endif
40
41#include "llvfs.h"
42#include "llstl.h"
43
44const S32 FILE_BLOCK_MASK = 0x000003FF; // 1024-byte blocks
45const S32 VFS_CLEANUP_SIZE = 5242880; // how much space we free up in a single stroke
46const S32 BLOCK_LENGTH_INVALID = -1; // mLength for invalid LLVFSFileBlocks
47
48LLVFS *gVFS = NULL;
49
50// internal class definitions
51class LLVFSBlock
52{
53public:
54 LLVFSBlock()
55 {
56 mLocation = 0;
57 mLength = 0;
58 }
59
60 LLVFSBlock(U32 loc, S32 size)
61 {
62 mLocation = loc;
63 mLength = size;
64 }
65
66 static BOOL insertFirstLL(LLVFSBlock *first, LLVFSBlock *second)
67 {
68 return first->mLocation != second->mLocation
69 ? first->mLocation < second->mLocation
70 : first->mLength < second->mLength;
71
72 }
73
74public:
75 U32 mLocation;
76 S32 mLength; // allocated block size
77};
78
79LLVFSFileSpecifier::LLVFSFileSpecifier()
80: mFileID(),
81 mFileType( LLAssetType::AT_NONE )
82{
83}
84
85LLVFSFileSpecifier::LLVFSFileSpecifier(const LLUUID &file_id, const LLAssetType::EType file_type)
86{
87 mFileID = file_id;
88 mFileType = file_type;
89}
90
91bool LLVFSFileSpecifier::operator<(const LLVFSFileSpecifier &rhs) const
92{
93 return (mFileID == rhs.mFileID)
94 ? mFileType < rhs.mFileType
95 : mFileID < rhs.mFileID;
96}
97
98bool LLVFSFileSpecifier::operator==(const LLVFSFileSpecifier &rhs) const
99{
100 return (mFileID == rhs.mFileID &&
101 mFileType == rhs.mFileType);
102}
103
104
105class LLVFSFileBlock : public LLVFSBlock, public LLVFSFileSpecifier
106{
107public:
108 LLVFSFileBlock() : LLVFSBlock(), LLVFSFileSpecifier()
109 {
110 init();
111 }
112
113 LLVFSFileBlock(const LLUUID &file_id, LLAssetType::EType file_type, U32 loc = 0, S32 size = 0)
114 : LLVFSBlock(loc, size), LLVFSFileSpecifier( file_id, file_type )
115 {
116 init();
117 }
118
119 void init()
120 {
121 mSize = 0;
122 mIndexLocation = -1;
123 mAccessTime = (U32)time(NULL);
124
125 for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++)
126 {
127 mLocks[(EVFSLock)i] = 0;
128 }
129 }
130
131 #ifdef LL_LITTLE_ENDIAN
132 inline void swizzleCopy(void *dst, void *src, int size) { memcpy(dst, src, size); }
133
134 #else
135
136 inline U32 swizzle32(U32 x)
137 {
138 return(((x >> 24) & 0x000000FF) | ((x >> 8) & 0x0000FF00) | ((x << 8) & 0x00FF0000) |((x << 24) & 0xFF000000));
139 }
140
141 inline U16 swizzle16(U16 x)
142 {
143 return( ((x >> 8) & 0x000000FF) | ((x << 8) & 0x0000FF00) );
144 }
145
146 inline void swizzleCopy(void *dst, void *src, int size)
147 {
148 if(size == 4)
149 {
150 ((U32*)dst)[0] = swizzle32(((U32*)src)[0]);
151 }
152 else if(size == 2)
153 {
154 ((U16*)dst)[0] = swizzle16(((U16*)src)[0]);
155 }
156 else
157 {
158 // Perhaps this should assert...
159 memcpy(dst, src, size);
160 }
161 }
162
163 #endif
164
165 void serialize(U8 *buffer)
166 {
167 swizzleCopy(buffer, &mLocation, 4);
168 buffer += 4;
169 swizzleCopy(buffer, &mLength, 4);
170 buffer +=4;
171 swizzleCopy(buffer, &mAccessTime, 4);
172 buffer +=4;
173 memcpy(buffer, &mFileID.mData, 16);
174 buffer += 16;
175 S16 temp_type = mFileType;
176 swizzleCopy(buffer, &temp_type, 2);
177 buffer += 2;
178 swizzleCopy(buffer, &mSize, 4);
179 }
180
181 void deserialize(U8 *buffer, const S32 index_loc)
182 {
183 mIndexLocation = index_loc;
184
185 swizzleCopy(&mLocation, buffer, 4);
186 buffer += 4;
187 swizzleCopy(&mLength, buffer, 4);
188 buffer += 4;
189 swizzleCopy(&mAccessTime, buffer, 4);
190 buffer += 4;
191 memcpy(&mFileID.mData, buffer, 16);
192 buffer += 16;
193 S16 temp_type;
194 swizzleCopy(&temp_type, buffer, 2);
195 mFileType = (LLAssetType::EType)temp_type;
196 buffer += 2;
197 swizzleCopy(&mSize, buffer, 4);
198 }
199
200 static BOOL insertLRU(LLVFSFileBlock* const& first,
201 LLVFSFileBlock* const& second)
202 {
203 return (first->mAccessTime == second->mAccessTime)
204 ? *first < *second
205 : first->mAccessTime < second->mAccessTime;
206 }
207
208public:
209 S32 mSize;
210 S32 mIndexLocation; // location of index entry
211 U32 mAccessTime;
212 BOOL mLocks[VFSLOCK_COUNT]; // number of outstanding locks of each type
213
214 static const S32 SERIAL_SIZE;
215};
216
217// Helper structure for doing lru w/ stl... is there a simpler way?
218struct LLVFSFileBlock_less
219{
220 bool operator()(LLVFSFileBlock* const& lhs, LLVFSFileBlock* const& rhs) const
221 {
222 return (LLVFSFileBlock::insertLRU(lhs, rhs)) ? true : false;
223 }
224};
225
226
227const S32 LLVFSFileBlock::SERIAL_SIZE = 34;
228
229
230LLVFS::LLVFS(const char *index_filename, const char *data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash)
231: mRemoveAfterCrash(remove_after_crash)
232{
233 mDataMutex = new LLMutex(0);
234
235 S32 i;
236 for (i = 0; i < VFSLOCK_COUNT; i++)
237 {
238 mLockCounts[i] = 0;
239 }
240 mValid = VFSVALID_OK;
241 mReadOnly = read_only;
242 mIndexFilename = new char[strlen(index_filename) + 1];
243 mDataFilename = new char[strlen(data_filename) + 1];
244 strcpy(mIndexFilename, index_filename);
245 strcpy(mDataFilename, data_filename);
246
247 const char *file_mode = mReadOnly ? "rb" : "r+b";
248
249 if (! (mDataFP = openAndLock(mDataFilename, file_mode, mReadOnly)))
250 {
251
252 if (mReadOnly)
253 {
254 llwarns << "Can't find " << mDataFilename << " to open read-only VFS" << llendl;
255 mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY;
256 return;
257 }
258
259 if((mDataFP = openAndLock(mDataFilename, "w+b", FALSE)))
260 {
261 // Since we're creating this data file, assume any index file is bogus
262 // remove the index, since this vfs is now blank
263 LLFile::remove(mIndexFilename);
264 }
265 else
266 {
267 llwarns << "Can't open VFS data file " << mDataFilename << " attempting to use alternate" << llendl;
268
269 char *temp_index = new char[strlen(mIndexFilename) + 10];
270 char *temp_data = new char[strlen(mDataFilename) + 10];
271
272 for (U32 count = 0; count < 256; count++)
273 {
274 sprintf(temp_index, "%s.%u", mIndexFilename, count);
275 sprintf(temp_data, "%s.%u", mDataFilename, count);
276
277 // try just opening, then creating, each alternate
278 if ((mDataFP = openAndLock(temp_data, "r+b", FALSE)))
279 {
280 break;
281 }
282
283 if ((mDataFP = openAndLock(temp_data, "w+b", FALSE)))
284 {
285 // we're creating the datafile, so nuke the indexfile
286 LLFile::remove(temp_index);
287 break;
288 }
289 }
290
291 if (! mDataFP)
292 {
293 llwarns << "Couldn't open vfs data file after trying many alternates" << llendl;
294 mValid = VFSVALID_BAD_CANNOT_CREATE;
295 return;
296 }
297
298 delete[] mIndexFilename;
299 delete[] mDataFilename;
300
301 mIndexFilename = temp_index;
302 mDataFilename = temp_data;
303 }
304
305 if (presize)
306 {
307 presizeDataFile(presize);
308 }
309 }
310
311 // Did we leave this file open for writing last time?
312 // If so, close it and start over.
313 if (!mReadOnly && mRemoveAfterCrash)
314 {
315 llstat marker_info;
316 char* marker = new char[strlen(mDataFilename) + strlen(".open") + 1];
317 sprintf(marker, "%s.open", mDataFilename);
318 if (!LLFile::stat(marker, &marker_info))
319 {
320 // marker exists, kill the lock and the VFS files
321 unlockAndClose(mDataFP);
322 mDataFP = NULL;
323
324 llwarns << "VFS: File left open on last run, removing old VFS file " << mDataFilename << llendl;
325 LLFile::remove(mIndexFilename);
326 LLFile::remove(mDataFilename);
327 LLFile::remove(marker);
328
329 mDataFP = openAndLock(mDataFilename, "w+b", FALSE);
330 if (!mDataFP)
331 {
332 llwarns << "Can't open VFS data file in crash recovery" << llendl;
333 mValid = VFSVALID_BAD_CANNOT_CREATE;
334 return;
335 }
336
337 if (presize)
338 {
339 presizeDataFile(presize);
340 }
341 }
342 delete [] marker;
343 marker = NULL;
344 }
345
346 // determine the real file size
347 fseek(mDataFP, 0, SEEK_END);
348 U32 data_size = ftell(mDataFP);
349
350 // read the index file
351 // make sure there's at least one file in it too
352 // if not, we'll treat this as a new vfs
353 llstat fbuf;
354 if (! LLFile::stat(mIndexFilename, &fbuf) &&
355 fbuf.st_size >= LLVFSFileBlock::SERIAL_SIZE &&
356 (mIndexFP = openAndLock(mIndexFilename, file_mode, mReadOnly))
357 )
358 {
359 U8 *buffer = new U8[fbuf.st_size];
360 fread(buffer, fbuf.st_size, 1, mIndexFP);
361
362 U8 *tmp_ptr = buffer;
363
364 LLLinkedList<LLVFSBlock> files_by_loc;
365 files_by_loc.setInsertBefore(LLVFSBlock::insertFirstLL);
366
367 while (tmp_ptr < buffer + fbuf.st_size)
368 {
369 LLVFSFileBlock *block = new LLVFSFileBlock();
370
371 block->deserialize(tmp_ptr, (S32)(tmp_ptr - buffer));
372
373 // Do sanity check on this block.
374 // Note that this skips zero size blocks, which helps VFS
375 // to heal after some errors. JC
376 if (block->mLength > 0 &&
377 (U32)block->mLength <= data_size &&
378 block->mLocation >= 0 &&
379 block->mLocation < data_size &&
380 block->mSize > 0 &&
381 block->mSize <= block->mLength &&
382 block->mFileType >= LLAssetType::AT_NONE &&
383 block->mFileType < LLAssetType::AT_COUNT)
384 {
385 mFileBlocks.insert(fileblock_map::value_type(*block, block));
386 files_by_loc.addDataSorted(block);
387 }
388 else
389 if (block->mLength && block->mSize > 0)
390 {
391 // this is corrupt, not empty
392 llwarns << "VFS corruption: " << block->mFileID << " (" << block->mFileType << ") at index " << block->mIndexLocation << " DS: " << data_size << llendl;
393 llwarns << "Length: " << block->mLength << "\tLocation: " << block->mLocation << "\tSize: " << block->mSize << llendl;
394 llwarns << "File has bad data - VFS removed" << llendl;
395
396 delete[] buffer;
397 delete block;
398
399 unlockAndClose( mIndexFP );
400 mIndexFP = NULL;
401 LLFile::remove( mIndexFilename );
402
403 unlockAndClose( mDataFP );
404 mDataFP = NULL;
405 LLFile::remove( mDataFilename );
406
407 mValid = VFSVALID_BAD_CORRUPT;
408 return;
409 }
410 else
411 {
412 // this is a null or bad entry, skip it
413 S32 index_loc = (S32)(tmp_ptr - buffer);
414 mIndexHoles.push_back(index_loc);
415
416 delete block;
417 }
418
419 tmp_ptr += block->SERIAL_SIZE;
420 }
421 delete[] buffer;
422
423 // discover all the free blocks
424 LLVFSFileBlock *last_file_block = (LLVFSFileBlock*)files_by_loc.getFirstData();
425
426 if (last_file_block)
427 {
428 // check for empty space at the beginning
429 if (last_file_block->mLocation > 0)
430 {
431 LLVFSBlock *block = new LLVFSBlock(0, last_file_block->mLocation);
432 addFreeBlock(block);
433 }
434
435 LLVFSFileBlock *cur_file_block;
436 while ((cur_file_block = (LLVFSFileBlock*)files_by_loc.getNextData()))
437 {
438 if (cur_file_block->mLocation == last_file_block->mLocation
439 && cur_file_block->mLength == last_file_block->mLength)
440 {
441 llwarns << "VFS: removing duplicate entry"
442 << " at " << cur_file_block->mLocation
443 << " length " << cur_file_block->mLength
444 << " size " << cur_file_block->mSize
445 << " ID " << cur_file_block->mFileID
446 << " type " << cur_file_block->mFileType
447 << llendl;
448
449 // Duplicate entries. Nuke them both for safety.
450 mFileBlocks.erase(*cur_file_block); // remove ID/type entry
451 if (cur_file_block->mLength > 0)
452 {
453 // convert to hole
454 LLVFSBlock* block = new LLVFSBlock(cur_file_block->mLocation,
455 cur_file_block->mLength);
456 addFreeBlock(block);
457 }
458 lockData(); // needed for sync()
459 sync(cur_file_block, TRUE); // remove first on disk
460 sync(last_file_block, TRUE); // remove last on disk
461 unlockData(); // needed for sync()
462 last_file_block = cur_file_block;
463 continue;
464 }
465
466 U32 loc = last_file_block->mLocation + last_file_block->mLength;
467 S32 length = cur_file_block->mLocation - loc;
468
469 if (length < 0 || loc < 0 || loc > data_size)
470 {
471 // Invalid VFS
472 unlockAndClose( mIndexFP );
473 mIndexFP = NULL;
474 LLFile::remove( mIndexFilename );
475
476 unlockAndClose( mDataFP );
477 mDataFP = NULL;
478 LLFile::remove( mDataFilename );
479
480 llwarns << "VFS: overlapping entries"
481 << " at " << cur_file_block->mLocation
482 << " length " << cur_file_block->mLength
483 << " ID " << cur_file_block->mFileID
484 << " type " << cur_file_block->mFileType
485 << llendl;
486 mValid = VFSVALID_BAD_CORRUPT;
487 return;
488 }
489
490 if (length > 0)
491 {
492 LLVFSBlock *block = new LLVFSBlock(loc, length);
493 addFreeBlock(block);
494 }
495
496 last_file_block = cur_file_block;
497 }
498
499 // also note any empty space at the end
500 U32 loc = last_file_block->mLocation + last_file_block->mLength;
501 if (loc < data_size)
502 {
503 LLVFSBlock *block = new LLVFSBlock(loc, data_size - loc);
504 addFreeBlock(block);
505 }
506 }
507 else
508 {
509 LLVFSBlock *first_block = new LLVFSBlock(0, data_size);
510 addFreeBlock(first_block);
511 }
512 }
513 else
514 {
515 if (mReadOnly)
516 {
517 llwarns << "Can't find " << mIndexFilename << " to open read-only VFS" << llendl;
518 mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY;
519 return;
520 }
521
522
523 mIndexFP = openAndLock(mIndexFilename, "w+b", FALSE);
524 if (!mIndexFP)
525 {
526 llwarns << "Couldn't open an index file for the VFS, probably a sharing violation!" << llendl;
527
528 unlockAndClose( mDataFP );
529 mDataFP = NULL;
530 LLFile::remove( mDataFilename );
531
532 mValid = VFSVALID_BAD_CANNOT_CREATE;
533 return;
534 }
535
536 // no index file, start from scratch w/ 1GB allocation
537 LLVFSBlock *first_block = new LLVFSBlock(0, data_size ? data_size : 0x40000000);
538 addFreeBlock(first_block);
539 }
540
541 // Open marker file to look for bad shutdowns
542 if (!mReadOnly && mRemoveAfterCrash)
543 {
544 char* marker = new char[strlen(mDataFilename) + strlen(".open") + 1];
545 sprintf(marker, "%s.open", mDataFilename);
546 FILE* marker_fp = LLFile::fopen(marker, "w");
547 if (marker_fp)
548 {
549 fclose(marker_fp);
550 marker_fp = NULL;
551 }
552 delete [] marker;
553 marker = NULL;
554 }
555
556 llinfos << "VFS: Using index file " << mIndexFilename << " and data file " << mDataFilename << llendl;
557
558 mValid = VFSVALID_OK;
559}
560
561LLVFS::~LLVFS()
562{
563 if (mDataMutex->isLocked())
564 {
565 llerrs << "LLVFS destroyed with mutex locked" << llendl;
566 }
567
568 unlockAndClose(mIndexFP);
569 mIndexFP = NULL;
570
571 fileblock_map::const_iterator it;
572 for (it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
573 {
574 delete (*it).second;
575 }
576 mFileBlocks.clear();
577
578 mFreeBlocksByLength.clear();
579
580 for_each(mFreeBlocksByLocation.begin(), mFreeBlocksByLocation.end(), DeletePairedPointer());
581
582 unlockAndClose(mDataFP);
583 mDataFP = NULL;
584
585 // Remove marker file
586 if (!mReadOnly && mRemoveAfterCrash)
587 {
588 char* marker_file = new char[strlen(mDataFilename) + strlen(".open") + 1];
589 sprintf(marker_file, "%s.open", mDataFilename);
590 LLFile::remove(marker_file);
591 delete [] marker_file;
592 marker_file = NULL;
593 }
594
595 delete[] mIndexFilename;
596 mIndexFilename = NULL;
597 delete[] mDataFilename;
598 mDataFilename = NULL;
599
600 delete mDataMutex;
601}
602
603void LLVFS::presizeDataFile(const U32 size)
604{
605 if (!mDataFP)
606 {
607 llerrs << "LLVFS::presizeDataFile() with no data file open" << llendl;
608 }
609
610 // we're creating this file for the first time, size it
611 fseek(mDataFP, size-1, SEEK_SET);
612 S32 tmp = 0;
613 tmp = (S32)fwrite(&tmp, 1, 1, mDataFP);
614 // fflush(mDataFP);
615
616 // also remove any index, since this vfs is now blank
617 LLFile::remove(mIndexFilename);
618
619 if (tmp)
620 {
621 llinfos << "Pre-sized VFS data file to " << ftell(mDataFP) << " bytes" << llendl;
622 }
623 else
624 {
625 llwarns << "Failed to pre-size VFS data file" << llendl;
626 }
627}
628
629BOOL LLVFS::getExists(const LLUUID &file_id, const LLAssetType::EType file_type)
630{
631 LLVFSFileBlock *block = NULL;
632
633 if (!isValid())
634 {
635 llerrs << "Attempting to use invalid VFS!" << llendl;
636 }
637
638 lockData();
639
640 LLVFSFileSpecifier spec(file_id, file_type);
641 fileblock_map::iterator it = mFileBlocks.find(spec);
642 if (it != mFileBlocks.end())
643 {
644 block = (*it).second;
645 block->mAccessTime = (U32)time(NULL);
646 }
647
648 BOOL res = (block && block->mLength > 0) ? TRUE : FALSE;
649
650 unlockData();
651
652 return res;
653}
654
655S32 LLVFS::getSize(const LLUUID &file_id, const LLAssetType::EType file_type)
656{
657 S32 size = 0;
658
659 if (!isValid())
660 {
661 llerrs << "Attempting to use invalid VFS!" << llendl;
662
663 }
664
665 lockData();
666
667 LLVFSFileSpecifier spec(file_id, file_type);
668 fileblock_map::iterator it = mFileBlocks.find(spec);
669 if (it != mFileBlocks.end())
670 {
671 LLVFSFileBlock *block = (*it).second;
672
673 block->mAccessTime = (U32)time(NULL);
674 size = block->mSize;
675 }
676
677 unlockData();
678
679 return size;
680}
681
682S32 LLVFS::getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type)
683{
684 S32 size = 0;
685
686 if (!isValid())
687 {
688 llerrs << "Attempting to use invalid VFS!" << llendl;
689 }
690
691 lockData();
692
693 LLVFSFileSpecifier spec(file_id, file_type);
694 fileblock_map::iterator it = mFileBlocks.find(spec);
695 if (it != mFileBlocks.end())
696 {
697 LLVFSFileBlock *block = (*it).second;
698
699 block->mAccessTime = (U32)time(NULL);
700 size = block->mLength;
701 }
702
703 unlockData();
704
705 return size;
706}
707
708BOOL LLVFS::checkAvailable(S32 max_size)
709{
710 blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(max_size); // first entry >= size
711 return (iter == mFreeBlocksByLength.end()) ? FALSE : TRUE;
712}
713
714BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type, S32 max_size)
715{
716 if (!isValid())
717 {
718 llerrs << "Attempting to use invalid VFS!" << llendl;
719 }
720 if (mReadOnly)
721 {
722 llerrs << "Attempt to write to read-only VFS" << llendl;
723 }
724 if (max_size <= 0)
725 {
726 llwarns << "VFS: Attempt to assign size " << max_size << " to vfile " << file_id << llendl;
727 return FALSE;
728 }
729
730 lockData();
731
732 LLVFSFileSpecifier spec(file_id, file_type);
733 LLVFSFileBlock *block = NULL;
734 fileblock_map::iterator it = mFileBlocks.find(spec);
735 if (it != mFileBlocks.end())
736 {
737 block = (*it).second;
738 }
739
740 // round all sizes upward to KB increments
741 if (max_size & FILE_BLOCK_MASK)
742 {
743 max_size += FILE_BLOCK_MASK;
744 max_size &= ~FILE_BLOCK_MASK;
745 }
746
747 if (block && block->mLength > 0)
748 {
749 block->mAccessTime = (U32)time(NULL);
750
751 if (max_size == block->mLength)
752 {
753 unlockData();
754 return TRUE;
755 }
756 else if (max_size < block->mLength)
757 {
758 // this file is shrinking
759 LLVFSBlock *free_block = new LLVFSBlock(block->mLocation + max_size, block->mLength - max_size);
760
761 addFreeBlock(free_block);
762
763 block->mLength = max_size;
764
765 if (block->mLength < block->mSize)
766 {
767 // JC: Was a warning, but Ian says it's bad.
768 llerrs << "Truncating virtual file " << file_id << " to " << block->mLength << " bytes" << llendl;
769 block->mSize = block->mLength;
770 }
771
772 sync(block);
773 //mergeFreeBlocks();
774
775 unlockData();
776 return TRUE;
777 }
778 else if (max_size > block->mLength)
779 {
780 // this file is growing
781 // first check for an adjacent free block to grow into
782 S32 size_increase = max_size - block->mLength;
783
784 // Find the first free block with and addres > block->mLocation
785 LLVFSBlock *free_block;
786 blocks_location_map_t::iterator iter = mFreeBlocksByLocation.upper_bound(block->mLocation);
787 if (iter != mFreeBlocksByLocation.end())
788 {
789 free_block = iter->second;
790
791 if (free_block->mLocation == block->mLocation + block->mLength &&
792 free_block->mLength >= size_increase)
793 {
794 // this free block is at the end of the file and is large enough
795
796 // Must call useFreeSpace before sync(), as sync()
797 // unlocks data structures.
798 useFreeSpace(free_block, size_increase);
799 block->mLength += size_increase;
800 sync(block);
801
802 unlockData();
803 return TRUE;
804 }
805 }
806
807 // no adjecent free block, find one in the list
808 free_block = findFreeBlock(max_size, block);
809
810 if (free_block)
811 {
812 if (block->mLength > 0)
813 {
814 // create a new free block where this file used to be
815 LLVFSBlock *new_free_block = new LLVFSBlock(block->mLocation, block->mLength);
816
817 addFreeBlock(new_free_block);
818
819 if (block->mSize > 0)
820 {
821 // move the file into the new block
822 U8 *buffer = new U8[block->mSize];
823 fseek(mDataFP, block->mLocation, SEEK_SET);
824 fread(buffer, block->mSize, 1, mDataFP);
825 fseek(mDataFP, free_block->mLocation, SEEK_SET);
826 fwrite(buffer, block->mSize, 1, mDataFP);
827 // fflush(mDataFP);
828
829 delete[] buffer;
830 }
831 }
832
833 block->mLocation = free_block->mLocation;
834
835 block->mLength = max_size;
836
837 // Must call useFreeSpace before sync(), as sync()
838 // unlocks data structures.
839 useFreeSpace(free_block, max_size);
840
841 sync(block);
842
843 unlockData();
844 return TRUE;
845 }
846 else
847 {
848 llwarns << "VFS: No space (" << max_size << ") to resize existing vfile " << file_id << llendl;
849 //dumpMap();
850 unlockData();
851 dumpStatistics();
852 return FALSE;
853 }
854 }
855 }
856 else
857 {
858 // find a free block in the list
859 LLVFSBlock *free_block = findFreeBlock(max_size);
860
861 if (free_block)
862 {
863 if (block)
864 {
865 block->mLocation = free_block->mLocation;
866 block->mLength = max_size;
867 }
868 else
869 {
870 // this file doesn't exist, create it
871 block = new LLVFSFileBlock(file_id, file_type, free_block->mLocation, max_size);
872 mFileBlocks.insert(fileblock_map::value_type(spec, block));
873 }
874
875 // Must call useFreeSpace before sync(), as sync()
876 // unlocks data structures.
877 useFreeSpace(free_block, max_size);
878 block->mAccessTime = (U32)time(NULL);
879
880 sync(block);
881 }
882 else
883 {
884 llwarns << "VFS: No space (" << max_size << ") for new virtual file " << file_id << llendl;
885 //dumpMap();
886 unlockData();
887 dumpStatistics();
888 return FALSE;
889 }
890 }
891 unlockData();
892 return TRUE;
893}
894
895
896// WARNING: HERE BE DRAGONS!
897// rename is the weirdest VFS op, because the file moves but the locks don't!
898void LLVFS::renameFile(const LLUUID &file_id, const LLAssetType::EType file_type,
899 const LLUUID &new_id, const LLAssetType::EType &new_type)
900{
901 if (!isValid())
902 {
903 llerrs << "Attempting to use invalid VFS!" << llendl;
904 }
905 if (mReadOnly)
906 {
907 llerrs << "Attempt to write to read-only VFS" << llendl;
908 }
909
910 lockData();
911
912 LLVFSFileSpecifier new_spec(new_id, new_type);
913 LLVFSFileSpecifier old_spec(file_id, file_type);
914
915 fileblock_map::iterator it = mFileBlocks.find(old_spec);
916 if (it != mFileBlocks.end())
917 {
918 LLVFSFileBlock *src_block = (*it).second;
919
920 // this will purge the data but leave the file block in place, w/ locks, if any
921 // WAS: removeFile(new_id, new_type); NOW uses removeFileBlock() to avoid mutex lock recursion
922 fileblock_map::iterator new_it = mFileBlocks.find(new_spec);
923 if (new_it != mFileBlocks.end())
924 {
925 LLVFSFileBlock *new_block = (*new_it).second;
926 removeFileBlock(new_block);
927 }
928
929 // if there's something in the target location, remove it but inherit its locks
930 it = mFileBlocks.find(new_spec);
931 if (it != mFileBlocks.end())
932 {
933 LLVFSFileBlock *dest_block = (*it).second;
934
935 for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++)
936 {
937 if(dest_block->mLocks[i])
938 {
939 llerrs << "Renaming VFS block to a locked file." << llendl;
940 }
941 dest_block->mLocks[i] = src_block->mLocks[i];
942 }
943
944 mFileBlocks.erase(new_spec);
945 delete dest_block;
946 }
947
948 src_block->mFileID = new_id;
949 src_block->mFileType = new_type;
950 src_block->mAccessTime = (U32)time(NULL);
951
952 mFileBlocks.erase(old_spec);
953 mFileBlocks.insert(fileblock_map::value_type(new_spec, src_block));
954
955 sync(src_block);
956 }
957 else
958 {
959 llwarns << "VFS: Attempt to rename nonexistent vfile " << file_id << ":" << file_type << llendl;
960 }
961 unlockData();
962}
963
964// mDataMutex must be LOCKED before calling this
965void LLVFS::removeFileBlock(LLVFSFileBlock *fileblock)
966{
967 // convert this into an unsaved, dummy fileblock to preserve locks
968 // a more rubust solution would store the locks in a seperate data structure
969 sync(fileblock, TRUE);
970
971 if (fileblock->mLength > 0)
972 {
973 // turn this file into an empty block
974 LLVFSBlock *free_block = new LLVFSBlock(fileblock->mLocation, fileblock->mLength);
975
976 addFreeBlock(free_block);
977 }
978
979 fileblock->mLocation = 0;
980 fileblock->mSize = 0;
981 fileblock->mLength = BLOCK_LENGTH_INVALID;
982 fileblock->mIndexLocation = -1;
983
984 //mergeFreeBlocks();
985}
986
987void LLVFS::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type)
988{
989 if (!isValid())
990 {
991 llerrs << "Attempting to use invalid VFS!" << llendl;
992 }
993 if (mReadOnly)
994 {
995 llerrs << "Attempt to write to read-only VFS" << llendl;
996 }
997
998 lockData();
999
1000 LLVFSFileSpecifier spec(file_id, file_type);
1001 fileblock_map::iterator it = mFileBlocks.find(spec);
1002 if (it != mFileBlocks.end())
1003 {
1004 LLVFSFileBlock *block = (*it).second;
1005 removeFileBlock(block);
1006 }
1007 else
1008 {
1009 llwarns << "VFS: attempting to remove nonexistent file " << file_id << " type " << file_type << llendl;
1010 }
1011
1012 unlockData();
1013}
1014
1015
1016S32 LLVFS::getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 *buffer, S32 location, S32 length)
1017{
1018 S32 bytesread = 0;
1019
1020 if (!isValid())
1021 {
1022 llerrs << "Attempting to use invalid VFS!" << llendl;
1023 }
1024 llassert(location >= 0);
1025 llassert(length >= 0);
1026
1027 BOOL do_read = FALSE;
1028
1029 lockData();
1030
1031 LLVFSFileSpecifier spec(file_id, file_type);
1032 fileblock_map::iterator it = mFileBlocks.find(spec);
1033 if (it != mFileBlocks.end())
1034 {
1035 LLVFSFileBlock *block = (*it).second;
1036
1037 block->mAccessTime = (U32)time(NULL);
1038
1039 if (location > block->mSize)
1040 {
1041 llwarns << "VFS: Attempt to read location " << location << " in file " << file_id << " of length " << block->mSize << llendl;
1042 }
1043 else
1044 {
1045 if (length > block->mSize - location)
1046 {
1047 length = block->mSize - location;
1048 }
1049 location += block->mLocation;
1050 do_read = TRUE;
1051 }
1052 }
1053
1054 unlockData();
1055
1056 if (do_read)
1057 {
1058 fseek(mDataFP, location, SEEK_SET);
1059 bytesread = (S32)fread(buffer, 1, length, mDataFP);
1060 }
1061
1062 return bytesread;
1063}
1064
1065S32 LLVFS::storeData(const LLUUID &file_id, const LLAssetType::EType file_type, const U8 *buffer, S32 location, S32 length)
1066{
1067 if (!isValid())
1068 {
1069 llerrs << "Attempting to use invalid VFS!" << llendl;
1070 }
1071 if (mReadOnly)
1072 {
1073 llerrs << "Attempt to write to read-only VFS" << llendl;
1074 }
1075
1076 llassert(length > 0);
1077
1078 lockData();
1079
1080 LLVFSFileSpecifier spec(file_id, file_type);
1081 fileblock_map::iterator it = mFileBlocks.find(spec);
1082 if (it != mFileBlocks.end())
1083 {
1084 LLVFSFileBlock *block = (*it).second;
1085
1086 S32 in_loc = location;
1087 if (location == -1)
1088 {
1089 location = block->mSize;
1090 }
1091 llassert(location >= 0);
1092
1093 block->mAccessTime = (U32)time(NULL);
1094
1095 if (block->mLength == BLOCK_LENGTH_INVALID)
1096 {
1097 // Block was removed, ignore write
1098 llwarns << "VFS: Attempt to write to invalid block"
1099 << " in file " << file_id
1100 << " location: " << in_loc
1101 << " bytes: " << length
1102 << llendl;
1103 unlockData();
1104 return length;
1105 }
1106 else if (location > block->mLength)
1107 {
1108 llwarns << "VFS: Attempt to write to location " << location
1109 << " in file " << file_id
1110 << " type " << S32(file_type)
1111 << " of size " << block->mSize
1112 << " block length " << block->mLength
1113 << llendl;
1114 unlockData();
1115 return length;
1116 }
1117 else
1118 {
1119 if (length > block->mLength - location )
1120 {
1121 llwarns << "VFS: Truncating write to virtual file " << file_id << " type " << S32(file_type) << llendl;
1122 length = block->mLength - location;
1123 }
1124 U32 file_location = location + block->mLocation;
1125
1126 unlockData();
1127
1128 fseek(mDataFP, file_location, SEEK_SET);
1129 S32 write_len = (S32)fwrite(buffer, 1, length, mDataFP);
1130 if (write_len != length)
1131 {
1132 llwarns << llformat("VFS Write Error: %d != %d",write_len,length) << llendl;
1133 }
1134 // fflush(mDataFP);
1135
1136 lockData();
1137 if (location + length > block->mSize)
1138 {
1139 block->mSize = location + write_len;
1140 sync(block);
1141 }
1142 unlockData();
1143
1144 return write_len;
1145 }
1146 }
1147 else
1148 {
1149 unlockData();
1150 return 0;
1151 }
1152}
1153
1154void LLVFS::incLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
1155{
1156 lockData();
1157
1158 LLVFSFileSpecifier spec(file_id, file_type);
1159 LLVFSFileBlock *block;
1160
1161 fileblock_map::iterator it = mFileBlocks.find(spec);
1162 if (it != mFileBlocks.end())
1163 {
1164 block = (*it).second;
1165 }
1166 else
1167 {
1168 // Create a dummy block which isn't saved
1169 block = new LLVFSFileBlock(file_id, file_type, 0, BLOCK_LENGTH_INVALID);
1170 block->mAccessTime = (U32)time(NULL);
1171 mFileBlocks.insert(fileblock_map::value_type(spec, block));
1172 }
1173
1174 block->mLocks[lock]++;
1175 mLockCounts[lock]++;
1176
1177 unlockData();
1178}
1179
1180void LLVFS::decLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
1181{
1182 lockData();
1183
1184 LLVFSFileSpecifier spec(file_id, file_type);
1185 fileblock_map::iterator it = mFileBlocks.find(spec);
1186 if (it != mFileBlocks.end())
1187 {
1188 LLVFSFileBlock *block = (*it).second;
1189
1190 if (block->mLocks[lock] > 0)
1191 {
1192 block->mLocks[lock]--;
1193 }
1194 else
1195 {
1196 llwarns << "VFS: Decrementing zero-value lock " << lock << llendl;
1197 }
1198 mLockCounts[lock]--;
1199 }
1200
1201 unlockData();
1202}
1203
1204BOOL LLVFS::isLocked(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
1205{
1206 lockData();
1207
1208 BOOL res = FALSE;
1209
1210 LLVFSFileSpecifier spec(file_id, file_type);
1211 fileblock_map::iterator it = mFileBlocks.find(spec);
1212 if (it != mFileBlocks.end())
1213 {
1214 LLVFSFileBlock *block = (*it).second;
1215 res = (block->mLocks[lock] > 0);
1216 }
1217
1218 unlockData();
1219
1220 return res;
1221}
1222
1223//============================================================================
1224// protected
1225//============================================================================
1226
1227void LLVFS::eraseBlockLength(LLVFSBlock *block)
1228{
1229 // find the corresponding map entry in the length map and erase it
1230 S32 length = block->mLength;
1231 blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(length);
1232 blocks_length_map_t::iterator end = mFreeBlocksByLength.end();
1233 while(iter != end)
1234 {
1235 LLVFSBlock *tblock = iter->second;
1236 llassert(tblock->mLength == length); // there had -better- be an entry with our length!
1237 if (tblock == block)
1238 {
1239 mFreeBlocksByLength.erase(iter);
1240 break;
1241 }
1242 ++iter;
1243 }
1244 if (iter == end)
1245 {
1246 llerrs << "eraseBlock could not find block" << llendl;
1247 }
1248}
1249
1250
1251// Remove block from both free lists (by location and by length).
1252void LLVFS::eraseBlock(LLVFSBlock *block)
1253{
1254 eraseBlockLength(block);
1255 // find the corresponding map entry in the location map and erase it
1256 U32 location = block->mLocation;
1257 llverify(mFreeBlocksByLocation.erase(location) == 1); // we should only have one entry per location.
1258}
1259
1260
1261// Add the region specified by block location and length to the free lists.
1262// Also incrementally defragment by merging with previous and next free blocks.
1263void LLVFS::addFreeBlock(LLVFSBlock *block)
1264{
1265#if LL_DEBUG
1266 size_t dbgcount = mFreeBlocksByLocation.count(block->mLocation);
1267 if(dbgcount > 0)
1268 {
1269 llerrs << "addFreeBlock called with block already in list" << llendl;
1270 }
1271#endif
1272
1273 // Get a pointer to the next free block (by location).
1274 blocks_location_map_t::iterator next_free_it = mFreeBlocksByLocation.lower_bound(block->mLocation);
1275
1276 // We can merge with previous if it ends at our requested location.
1277 LLVFSBlock* prev_block = NULL;
1278 bool merge_prev = false;
1279 if (next_free_it != mFreeBlocksByLocation.begin())
1280 {
1281 blocks_location_map_t::iterator prev_free_it = next_free_it;
1282 --prev_free_it;
1283 prev_block = prev_free_it->second;
1284 merge_prev = (prev_block->mLocation + prev_block->mLength == block->mLocation);
1285 }
1286
1287 // We can merge with next if our block ends at the next block's location.
1288 LLVFSBlock* next_block = NULL;
1289 bool merge_next = false;
1290 if (next_free_it != mFreeBlocksByLocation.end())
1291 {
1292 next_block = next_free_it->second;
1293 merge_next = (block->mLocation + block->mLength == next_block->mLocation);
1294 }
1295
1296 if (merge_prev && merge_next)
1297 {
1298 // llinfos << "VFS merge BOTH" << llendl;
1299 // Previous block is changing length (a lot), so only need to update length map.
1300 // Next block is going away completely. JC
1301 eraseBlockLength(prev_block);
1302 eraseBlock(next_block);
1303 prev_block->mLength += block->mLength + next_block->mLength;
1304 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block));
1305 delete block;
1306 block = NULL;
1307 delete next_block;
1308 next_block = NULL;
1309 }
1310 else if (merge_prev)
1311 {
1312 // llinfos << "VFS merge previous" << llendl;
1313 // Previous block is maintaining location, only changing length,
1314 // therefore only need to update the length map. JC
1315 eraseBlockLength(prev_block);
1316 prev_block->mLength += block->mLength;
1317 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block)); // multimap insert
1318 delete block;
1319 block = NULL;
1320 }
1321 else if (merge_next)
1322 {
1323 // llinfos << "VFS merge next" << llendl;
1324 // Next block is changing both location and length,
1325 // so both free lists must update. JC
1326 eraseBlock(next_block);
1327 next_block->mLocation = block->mLocation;
1328 next_block->mLength += block->mLength;
1329 // Don't hint here, next_free_it iterator may be invalid.
1330 mFreeBlocksByLocation.insert(blocks_location_map_t::value_type(next_block->mLocation, next_block)); // multimap insert
1331 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(next_block->mLength, next_block)); // multimap insert
1332 delete block;
1333 block = NULL;
1334 }
1335 else
1336 {
1337 // Can't merge with other free blocks.
1338 // Hint that insert should go near next_free_it.
1339 mFreeBlocksByLocation.insert(next_free_it, blocks_location_map_t::value_type(block->mLocation, block)); // multimap insert
1340 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(block->mLength, block)); // multimap insert
1341 }
1342}
1343
1344// Superceeded by new addFreeBlock which does incremental free space merging.
1345// Incremental is faster overall.
1346//void LLVFS::mergeFreeBlocks()
1347//{
1348// if (!isValid())
1349// {
1350// llerrs << "Attempting to use invalid VFS!" << llendl;
1351// }
1352// // TODO: could we optimize this with hints from the calling code?
1353// blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin();
1354// blocks_location_map_t::iterator end = mFreeBlocksByLocation.end();
1355// LLVFSBlock *first_block = iter->second;
1356// while(iter != end)
1357// {
1358// blocks_location_map_t::iterator first_iter = iter; // save for if we do a merge
1359// if (++iter == end)
1360// break;
1361// LLVFSBlock *second_block = iter->second;
1362// if (first_block->mLocation + first_block->mLength == second_block->mLocation)
1363// {
1364// // remove the first block from the length map
1365// eraseBlockLength(first_block);
1366// // merge first_block with second_block, since they're adjacent
1367// first_block->mLength += second_block->mLength;
1368// // add the first block to the length map (with the new size)
1369// mFreeBlocksByLength.insert(blocks_length_map_t::value_type(first_block->mLength, first_block)); // multimap insert
1370//
1371// // erase and delete the second block
1372// eraseBlock(second_block);
1373// delete second_block;
1374//
1375// // reset iterator
1376// iter = first_iter; // haven't changed first_block, so corresponding iterator is still valid
1377// end = mFreeBlocksByLocation.end();
1378// }
1379// first_block = second_block;
1380// }
1381//}
1382
1383
1384void LLVFS::useFreeSpace(LLVFSBlock *free_block, S32 length)
1385{
1386 if (free_block->mLength == length)
1387 {
1388 eraseBlock(free_block);
1389 delete free_block;
1390 }
1391 else
1392 {
1393 eraseBlock(free_block);
1394
1395 free_block->mLocation += length;
1396 free_block->mLength -= length;
1397
1398 addFreeBlock(free_block);
1399 }
1400}
1401
1402// NOTE! mDataMutex must be LOCKED before calling this
1403// sync this index entry out to the index file
1404// we need to do this constantly to avoid corruption on viewer crash
1405void LLVFS::sync(LLVFSFileBlock *block, BOOL remove)
1406{
1407 if (!isValid())
1408 {
1409 llerrs << "Attempting to use invalid VFS!" << llendl;
1410 }
1411 if (mReadOnly)
1412 {
1413 llwarns << "Attempt to sync read-only VFS" << llendl;
1414 return;
1415 }
1416 if (block->mLength == BLOCK_LENGTH_INVALID)
1417 {
1418 // This is a dummy file, don't save
1419 return;
1420 }
1421 if (block->mLength == 0)
1422 {
1423 llerrs << "VFS syncing zero-length block" << llendl;
1424 }
1425
1426 BOOL set_index_to_end = FALSE;
1427 S32 seek_pos = block->mIndexLocation;
1428
1429 if (-1 == seek_pos)
1430 {
1431 if (!mIndexHoles.empty())
1432 {
1433 seek_pos = mIndexHoles.front();
1434 mIndexHoles.pop_front();
1435 }
1436 else
1437 {
1438 set_index_to_end = TRUE;
1439 }
1440 }
1441
1442 if (set_index_to_end)
1443 {
1444 // Need fseek/ftell to update the seek_pos and hence data
1445 // structures, so can't unlockData() before this.
1446 fseek(mIndexFP, 0, SEEK_END);
1447 seek_pos = ftell(mIndexFP);
1448 }
1449
1450 block->mIndexLocation = seek_pos;
1451 if (remove)
1452 {
1453 mIndexHoles.push_back(seek_pos);
1454 }
1455
1456 U8 buffer[LLVFSFileBlock::SERIAL_SIZE];
1457 if (remove)
1458 {
1459 memset(buffer, 0, LLVFSFileBlock::SERIAL_SIZE);
1460 }
1461 else
1462 {
1463 block->serialize(buffer);
1464 }
1465
1466 unlockData();
1467
1468 // If set_index_to_end, file pointer is already at seek_pos
1469 // and we don't need to do anything. Only seek if not at end.
1470 if (!set_index_to_end)
1471 {
1472 fseek(mIndexFP, seek_pos, SEEK_SET);
1473 }
1474
1475 fwrite(buffer, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP);
1476 // fflush(mIndexFP);
1477
1478 lockData();
1479
1480 return;
1481}
1482
1483// mDataMutex must be LOCKED before calling this
1484// Can initiate LRU-based file removal to make space.
1485// The immune file block will not be removed.
1486LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune)
1487{
1488 if (!isValid())
1489 {
1490 llerrs << "Attempting to use invalid VFS!" << llendl;
1491 }
1492
1493 LLVFSBlock *block = NULL;
1494 BOOL have_lru_list = FALSE;
1495
1496 typedef std::set<LLVFSFileBlock*, LLVFSFileBlock_less> lru_set;
1497 lru_set lru_list;
1498
1499 LLTimer timer;
1500
1501 while (! block)
1502 {
1503 // look for a suitable free block
1504 blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(size); // first entry >= size
1505 if (iter != mFreeBlocksByLength.end())
1506 block = iter->second;
1507
1508 // no large enough free blocks, time to clean out some junk
1509 if (! block)
1510 {
1511 // create a list of files sorted by usage time
1512 // this is far faster than sorting a linked list
1513 if (! have_lru_list)
1514 {
1515 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
1516 {
1517 LLVFSFileBlock *tmp = (*it).second;
1518
1519 if (tmp != immune &&
1520 tmp->mLength > 0 &&
1521 ! tmp->mLocks[VFSLOCK_READ] &&
1522 ! tmp->mLocks[VFSLOCK_APPEND] &&
1523 ! tmp->mLocks[VFSLOCK_OPEN])
1524 {
1525 lru_list.insert(tmp);
1526 }
1527 }
1528
1529 have_lru_list = TRUE;
1530 }
1531
1532 if (lru_list.size() == 0)
1533 {
1534 // No more files to delete, and still not enough room!
1535 llwarns << "VFS: Can't make " << size << " bytes of free space in VFS, giving up" << llendl;
1536 break;
1537 }
1538
1539 // is the oldest file big enough? (Should be about half the time)
1540 lru_set::iterator it = lru_list.begin();
1541 LLVFSFileBlock *file_block = *it;
1542 if (file_block->mLength >= size && file_block != immune)
1543 {
1544 // ditch this file and look again for a free block - should find it
1545 // TODO: it'll be faster just to assign the free block and break
1546 llinfos << "LRU: Removing " << file_block->mFileID << ":" << file_block->mFileType << llendl;
1547 lru_list.erase(it);
1548 removeFileBlock(file_block);
1549 file_block = NULL;
1550 continue;
1551 }
1552
1553
1554 llinfos << "VFS: LRU: Aggressive: " << (S32)lru_list.size() << " files remain" << llendl;
1555 dumpLockCounts();
1556
1557 // Now it's time to aggressively make more space
1558 // Delete the oldest 5MB of the vfs or enough to hold the file, which ever is larger
1559 // This may yield too much free space, but we'll use it up soon enough
1560 U32 cleanup_target = (size > VFS_CLEANUP_SIZE) ? size : VFS_CLEANUP_SIZE;
1561 U32 cleaned_up = 0;
1562 for (it = lru_list.begin();
1563 it != lru_list.end() && cleaned_up < cleanup_target;
1564 )
1565 {
1566 file_block = *it;
1567
1568 // TODO: it would be great to be able to batch all these sync() calls
1569 // llinfos << "LRU2: Removing " << file_block->mFileID << ":" << file_block->mFileType << " last accessed" << file_block->mAccessTime << llendl;
1570
1571 cleaned_up += file_block->mLength;
1572 lru_list.erase(it++);
1573 removeFileBlock(file_block);
1574 file_block = NULL;
1575 }
1576 //mergeFreeBlocks();
1577 }
1578 }
1579
1580 F32 time = timer.getElapsedTimeF32();
1581 if (time > 0.5f)
1582 {
1583 llwarns << "VFS: Spent " << time << " seconds in findFreeBlock!" << llendl;
1584 }
1585
1586 return block;
1587}
1588
1589//============================================================================
1590// public
1591//============================================================================
1592
1593void LLVFS::pokeFiles()
1594{
1595 if (!isValid())
1596 {
1597 llerrs << "Attempting to use invalid VFS!" << llendl;
1598 }
1599 U32 word;
1600
1601 // only write data if we actually read 4 bytes
1602 // otherwise we're writing garbage and screwing up the file
1603 fseek(mDataFP, 0, SEEK_SET);
1604 if (fread(&word, 1, 4, mDataFP) == 4)
1605 {
1606 fseek(mDataFP, 0, SEEK_SET);
1607 fwrite(&word, 1, 4, mDataFP);
1608 fflush(mDataFP);
1609 }
1610
1611 fseek(mIndexFP, 0, SEEK_SET);
1612 if (fread(&word, 1, 4, mIndexFP) == 4)
1613 {
1614 fseek(mIndexFP, 0, SEEK_SET);
1615 fwrite(&word, 1, 4, mIndexFP);
1616 fflush(mIndexFP);
1617 }
1618}
1619
1620
1621void LLVFS::dumpMap()
1622{
1623 llinfos << "Files:" << llendl;
1624 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
1625 {
1626 LLVFSFileBlock *file_block = (*it).second;
1627 llinfos << "Location: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << llendl;
1628 }
1629
1630 llinfos << "Free Blocks:" << llendl;
1631 for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(),
1632 end = mFreeBlocksByLocation.end();
1633 iter != end; iter++)
1634 {
1635 LLVFSBlock *free_block = iter->second;
1636 llinfos << "Location: " << free_block->mLocation << "\tLength: " << free_block->mLength << llendl;
1637 }
1638}
1639
1640// verify that the index file contents match the in-memory file structure
1641// Very slow, do not call routinely. JC
1642void LLVFS::audit()
1643{
1644 // Lock the mutex through this whole function.
1645 LLMutexLock lock_data(mDataMutex);
1646
1647 fflush(mIndexFP);
1648
1649 fseek(mIndexFP, 0, SEEK_END);
1650 S32 index_size = ftell(mIndexFP);
1651 fseek(mIndexFP, 0, SEEK_SET);
1652
1653 U8 *buffer = new U8[index_size];
1654 fread(buffer, index_size, 1, mIndexFP);
1655
1656 U8 *tmp_ptr = buffer;
1657
1658 std::map<LLVFSFileSpecifier, LLVFSFileBlock*> found_files;
1659 U32 cur_time = (U32)time(NULL);
1660
1661 BOOL vfs_corrupt = FALSE;
1662
1663 std::vector<LLVFSFileBlock*> audit_blocks;
1664 while (tmp_ptr < buffer + index_size)
1665 {
1666 LLVFSFileBlock *block = new LLVFSFileBlock();
1667 audit_blocks.push_back(block);
1668
1669 block->deserialize(tmp_ptr, (S32)(tmp_ptr - buffer));
1670 tmp_ptr += block->SERIAL_SIZE;
1671
1672 // do sanity check on this block
1673 if (block->mLength >= 0 &&
1674 block->mLocation >= 0 &&
1675 block->mSize >= 0 &&
1676 block->mSize <= block->mLength &&
1677 block->mFileType >= LLAssetType::AT_NONE &&
1678 block->mFileType < LLAssetType::AT_COUNT &&
1679 block->mAccessTime <= cur_time &&
1680 block->mFileID != LLUUID::null)
1681 {
1682 if (mFileBlocks.find(*block) == mFileBlocks.end())
1683 {
1684 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " on disk, not in memory, loc " << block->mIndexLocation << llendl;
1685 }
1686 else if (found_files.find(*block) != found_files.end())
1687 {
1688 std::map<LLVFSFileSpecifier, LLVFSFileBlock*>::iterator it;
1689 it = found_files.find(*block);
1690 LLVFSFileBlock* dupe = it->second;
1691 // try to keep data from being lost
1692 unlockAndClose(mIndexFP);
1693 mIndexFP = NULL;
1694 unlockAndClose(mDataFP);
1695 mDataFP = NULL;
1696 llwarns << "VFS: Original block index " << block->mIndexLocation
1697 << " location " << block->mLocation
1698 << " length " << block->mLength
1699 << " size " << block->mSize
1700 << " id " << block->mFileID
1701 << " type " << block->mFileType
1702 << llendl;
1703 llwarns << "VFS: Duplicate block index " << dupe->mIndexLocation
1704 << " location " << dupe->mLocation
1705 << " length " << dupe->mLength
1706 << " size " << dupe->mSize
1707 << " id " << dupe->mFileID
1708 << " type " << dupe->mFileType
1709 << llendl;
1710 llwarns << "VFS: Index size " << index_size << llendl;
1711 llwarns << "VFS: INDEX CORRUPT" << llendl;
1712 vfs_corrupt = TRUE;
1713 break;
1714 }
1715 else
1716 {
1717 found_files[*block] = block;
1718 }
1719 }
1720 else
1721 {
1722 if (block->mLength)
1723 {
1724 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " corrupt on disk" << llendl;
1725 }
1726 // else this is just a hole
1727 }
1728 }
1729
1730 delete[] buffer;
1731
1732 if (vfs_corrupt)
1733 {
1734 for (std::vector<LLVFSFileBlock*>::iterator iter = audit_blocks.begin();
1735 iter != audit_blocks.end(); ++iter)
1736 {
1737 delete *iter;
1738 }
1739 audit_blocks.clear();
1740 return;
1741 }
1742
1743 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
1744 {
1745 LLVFSFileBlock* block = (*it).second;
1746
1747 if (block->mSize > 0)
1748 {
1749 if (! found_files.count(*block))
1750 {
1751 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " in memory, not on disk, loc " << block->mIndexLocation<< llendl;
1752 fseek(mIndexFP, block->mIndexLocation, SEEK_SET);
1753 U8 buf[LLVFSFileBlock::SERIAL_SIZE];
1754 fread(buf, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP);
1755
1756 LLVFSFileBlock disk_block;
1757 disk_block.deserialize(buf, block->mIndexLocation);
1758
1759 llwarns << "Instead found " << disk_block.mFileID << ":" << block->mFileType << llendl;
1760 }
1761 else
1762 {
1763 block = found_files.find(*block)->second;
1764 found_files.erase(*block);
1765 delete block;
1766 }
1767 }
1768 }
1769
1770 for (std::map<LLVFSFileSpecifier, LLVFSFileBlock*>::iterator iter = found_files.begin();
1771 iter != found_files.end(); iter++)
1772 {
1773 LLVFSFileBlock* block = iter->second;
1774 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " szie:" << block->mSize << " leftover" << llendl;
1775 }
1776
1777 llinfos << "VFS: audit OK" << llendl;
1778 // mutex released by LLMutexLock() destructor.
1779}
1780
1781
1782// quick check for uninitialized blocks
1783// Slow, do not call in release.
1784void LLVFS::checkMem()
1785{
1786 lockData();
1787
1788 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
1789 {
1790 LLVFSFileBlock *block = (*it).second;
1791 llassert(block->mFileType >= LLAssetType::AT_NONE &&
1792 block->mFileType < LLAssetType::AT_COUNT &&
1793 block->mFileID != LLUUID::null);
1794
1795 for (std::deque<S32>::iterator iter = mIndexHoles.begin();
1796 iter != mIndexHoles.end(); ++iter)
1797 {
1798 S32 index_loc = *iter;
1799 if (index_loc == block->mIndexLocation)
1800 {
1801 llwarns << "VFile block " << block->mFileID << ":" << block->mFileType << " is marked as a hole" << llendl;
1802 }
1803 }
1804 }
1805
1806 llinfos << "VFS: mem check OK" << llendl;
1807
1808 unlockData();
1809}
1810
1811void LLVFS::dumpLockCounts()
1812{
1813 S32 i;
1814 for (i = 0; i < VFSLOCK_COUNT; i++)
1815 {
1816 llinfos << "LockType: " << i << ": " << mLockCounts[i] << llendl;
1817 }
1818}
1819
1820void LLVFS::dumpStatistics()
1821{
1822 lockData();
1823
1824 // Investigate file blocks.
1825 std::map<S32, S32> size_counts;
1826 std::map<U32, S32> location_counts;
1827 std::map<LLAssetType::EType, std::pair<S32,S32> > filetype_counts;
1828
1829 S32 max_file_size = 0;
1830 S32 total_file_size = 0;
1831 S32 invalid_file_count = 0;
1832 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
1833 {
1834 LLVFSFileBlock *file_block = (*it).second;
1835 if (file_block->mLength == BLOCK_LENGTH_INVALID)
1836 {
1837 invalid_file_count++;
1838 }
1839 else if (file_block->mLength <= 0)
1840 {
1841 llinfos << "Bad file block at: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << llendl;
1842 size_counts[file_block->mLength]++;
1843 location_counts[file_block->mLocation]++;
1844 }
1845 else
1846 {
1847 total_file_size += file_block->mLength;
1848 }
1849
1850 if (file_block->mLength > max_file_size)
1851 {
1852 max_file_size = file_block->mLength;
1853 }
1854
1855 filetype_counts[file_block->mFileType].first++;
1856 filetype_counts[file_block->mFileType].second += file_block->mLength;
1857 }
1858
1859 for (std::map<S32,S32>::iterator it = size_counts.begin(); it != size_counts.end(); ++it)
1860 {
1861 S32 size = it->first;
1862 S32 size_count = it->second;
1863 llinfos << "Bad files size " << size << " count " << size_count << llendl;
1864 }
1865 for (std::map<U32,S32>::iterator it = location_counts.begin(); it != location_counts.end(); ++it)
1866 {
1867 U32 location = it->first;
1868 S32 location_count = it->second;
1869 llinfos << "Bad files location " << location << " count " << location_count << llendl;
1870 }
1871
1872 // Investigate free list.
1873 S32 max_free_size = 0;
1874 S32 total_free_size = 0;
1875 std::map<S32, S32> free_length_counts;
1876 for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(),
1877 end = mFreeBlocksByLocation.end();
1878 iter != end; iter++)
1879 {
1880 LLVFSBlock *free_block = iter->second;
1881 if (free_block->mLength <= 0)
1882 {
1883 llinfos << "Bad free block at: " << free_block->mLocation << "\tLength: " << free_block->mLength << llendl;
1884 }
1885 else
1886 {
1887 llinfos << "Block: " << free_block->mLocation
1888 << "\tLength: " << free_block->mLength
1889 << "\tEnd: " << free_block->mLocation + free_block->mLength
1890 << llendl;
1891 total_free_size += free_block->mLength;
1892 }
1893
1894 if (free_block->mLength > max_free_size)
1895 {
1896 max_free_size = free_block->mLength;
1897 }
1898
1899 free_length_counts[free_block->mLength]++;
1900 }
1901
1902 // Dump histogram of free block sizes
1903 for (std::map<S32,S32>::iterator it = free_length_counts.begin(); it != free_length_counts.end(); ++it)
1904 {
1905 llinfos << "Free length " << it->first << " count " << it->second << llendl;
1906 }
1907
1908 llinfos << "Invalid blocks: " << invalid_file_count << llendl;
1909 llinfos << "File blocks: " << mFileBlocks.size() << llendl;
1910
1911 S32 length_list_count = (S32)mFreeBlocksByLength.size();
1912 S32 location_list_count = (S32)mFreeBlocksByLocation.size();
1913 if (length_list_count == location_list_count)
1914 {
1915 llinfos << "Free list lengths match, free blocks: " << location_list_count << llendl;
1916 }
1917 else
1918 {
1919 llwarns << "Free list lengths do not match!" << llendl;
1920 llwarns << "By length: " << length_list_count << llendl;
1921 llwarns << "By location: " << location_list_count << llendl;
1922 }
1923 llinfos << "Max file: " << max_file_size/1024 << "K" << llendl;
1924 llinfos << "Max free: " << max_free_size/1024 << "K" << llendl;
1925 llinfos << "Total file size: " << total_file_size/1024 << "K" << llendl;
1926 llinfos << "Total free size: " << total_free_size/1024 << "K" << llendl;
1927 llinfos << "Sum: " << (total_file_size + total_free_size) << " bytes" << llendl;
1928 llinfos << llformat("%.0f%% full",((F32)(total_file_size)/(F32)(total_file_size+total_free_size))*100.f) << llendl;
1929
1930 llinfos << " " << llendl;
1931 for (std::map<LLAssetType::EType, std::pair<S32,S32> >::iterator iter = filetype_counts.begin();
1932 iter != filetype_counts.end(); ++iter)
1933 {
1934 llinfos << "Type: " << LLAssetType::getDesc(iter->first)
1935 << " Count: " << iter->second.first
1936 << " Bytes: " << (iter->second.second>>20) << " MB" << llendl;
1937 }
1938
1939 // Look for potential merges
1940 {
1941 blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin();
1942 blocks_location_map_t::iterator end = mFreeBlocksByLocation.end();
1943 LLVFSBlock *first_block = iter->second;
1944 while(iter != end)
1945 {
1946 if (++iter == end)
1947 break;
1948 LLVFSBlock *second_block = iter->second;
1949 if (first_block->mLocation + first_block->mLength == second_block->mLocation)
1950 {
1951 llinfos << "Potential merge at " << first_block->mLocation << llendl;
1952 }
1953 first_block = second_block;
1954 }
1955 }
1956 unlockData();
1957}
1958
1959// Debug Only!
1960#include "llapr.h"
1961void LLVFS::dumpFiles()
1962{
1963 lockData();
1964
1965 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
1966 {
1967 LLVFSFileSpecifier file_spec = it->first;
1968 LLVFSFileBlock *file_block = it->second;
1969 S32 length = file_block->mLength;
1970 S32 size = file_block->mSize;
1971 if (length != BLOCK_LENGTH_INVALID && size > 0)
1972 {
1973 LLUUID id = file_spec.mFileID;
1974 LLAssetType::EType type = file_spec.mFileType;
1975 U8* buffer = new U8[size];
1976
1977 unlockData();
1978 getData(id, type, buffer, 0, size);
1979 lockData();
1980
1981 LLString extention = ".data";
1982 switch(type)
1983 {
1984 case LLAssetType::AT_TEXTURE:
1985 extention = ".jp2"; // ".j2c"; // IrfanView recognizes .jp2 -sjb
1986 break;
1987 default:
1988 break;
1989 }
1990 LLString filename = id.getString() + extention;
1991 llinfos << " Writing " << filename << llendl;
1992 apr_file_t* file = ll_apr_file_open(filename, LL_APR_WB);
1993 ll_apr_file_write(file, buffer, size);
1994 apr_file_close(file);
1995 delete[] buffer;
1996 }
1997 }
1998
1999 unlockData();
2000}
2001
2002//============================================================================
2003// protected
2004//============================================================================
2005
2006// static
2007FILE *LLVFS::openAndLock(const char *filename, const char *mode, BOOL read_lock)
2008{
2009#if LL_WINDOWS
2010
2011 return LLFile::_fsopen(filename, mode, (read_lock ? _SH_DENYWR : _SH_DENYRW));
2012
2013#else
2014
2015 FILE *fp;
2016 int fd;
2017
2018 // first test the lock in a non-destructive way
2019 if (strstr(mode, "w"))
2020 {
2021 fp = LLFile::fopen(filename, "rb");
2022 if (fp)
2023 {
2024 fd = fileno(fp);
2025 if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1)
2026 {
2027 fclose(fp);
2028 return NULL;
2029 }
2030
2031 fclose(fp);
2032 }
2033 }
2034
2035 // now actually open the file for use
2036 fp = LLFile::fopen(filename, mode);
2037 if (fp)
2038 {
2039 fd = fileno(fp);
2040 if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1)
2041 {
2042 fclose(fp);
2043 fp = NULL;
2044 }
2045 }
2046
2047 return fp;
2048
2049#endif
2050}
2051
2052// static
2053void LLVFS::unlockAndClose(FILE *fp)
2054{
2055 if (fp)
2056 {
2057 // IW: we don't actually want to unlock on linux
2058 // this is because a forked process can kill the parent's lock
2059 // with an explicit unlock
2060 // however, fclose() will implicitly remove the lock
2061 // but only once both parent and child have closed the file
2062 /*
2063 #if !LL_WINDOWS
2064 int fd = fileno(fp);
2065 flock(fd, LOCK_UN);
2066 #endif
2067 */
2068
2069 fclose(fp);
2070 }
2071}
diff --git a/linden/indra/llvfs/llvfs.h b/linden/indra/llvfs/llvfs.h
new file mode 100644
index 0000000..f7affd4
--- /dev/null
+++ b/linden/indra/llvfs/llvfs.h
@@ -0,0 +1,170 @@
1/**
2 * @file llvfs.h
3 * @brief Definition of virtual file system
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_LLVFS_H
29#define LL_LLVFS_H
30
31#include <stdio.h>
32#include <map>
33#include <deque>
34#include "lluuid.h"
35#include "linked_lists.h"
36#include "llassettype.h"
37#include "llthread.h"
38
39enum EVFSValid
40{
41 VFSVALID_UNKNOWN = 0,
42 VFSVALID_OK = 1,
43 VFSVALID_BAD_CORRUPT = 2,
44 VFSVALID_BAD_CANNOT_OPEN_READONLY = 3,
45 VFSVALID_BAD_CANNOT_CREATE = 4
46};
47
48// Lock types for open vfiles, pending async reads, and pending async appends
49// (There are no async normal writes, currently)
50enum EVFSLock
51{
52 VFSLOCK_OPEN = 0,
53 VFSLOCK_READ = 1,
54 VFSLOCK_APPEND = 2,
55
56 VFSLOCK_COUNT = 3
57};
58
59// internal classes
60class LLVFSBlock;
61class LLVFSFileBlock;
62class LLVFSFileSpecifier
63{
64public:
65 LLVFSFileSpecifier();
66 LLVFSFileSpecifier(const LLUUID &file_id, const LLAssetType::EType file_type);
67 bool operator<(const LLVFSFileSpecifier &rhs) const;
68 bool operator==(const LLVFSFileSpecifier &rhs) const;
69
70public:
71 LLUUID mFileID;
72 LLAssetType::EType mFileType;
73};
74
75class LLVFS
76{
77public:
78 // Pass 0 to not presize
79 LLVFS(const char *index_filename, const char *data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash);
80 ~LLVFS();
81
82 BOOL isValid() const { return (VFSVALID_OK == mValid); }
83 EVFSValid getValidState() const { return mValid; }
84
85 // ---------- The following fucntions lock/unlock mDataMutex ----------
86 BOOL getExists(const LLUUID &file_id, const LLAssetType::EType file_type);
87 S32 getSize(const LLUUID &file_id, const LLAssetType::EType file_type);
88
89 BOOL checkAvailable(S32 max_size);
90
91 S32 getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type);
92 BOOL setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type, S32 max_size);
93
94 void renameFile(const LLUUID &file_id, const LLAssetType::EType file_type,
95 const LLUUID &new_id, const LLAssetType::EType &new_type);
96 void removeFile(const LLUUID &file_id, const LLAssetType::EType file_type);
97
98 S32 getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 *buffer, S32 location, S32 length);
99 S32 storeData(const LLUUID &file_id, const LLAssetType::EType file_type, const U8 *buffer, S32 location, S32 length);
100
101 void incLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock);
102 void decLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock);
103 BOOL isLocked(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock);
104 // ----------------------------------------------------------------
105
106 // Used to trigger evil WinXP behavior of "preloading" entire file into memory.
107 void pokeFiles();
108
109 // Verify that the index file contents match the in-memory file structure
110 // Very slow, do not call routinely. JC
111 void audit();
112 // Check for uninitialized blocks. Slow, do not call in release. JC
113 void checkMem();
114 // for debugging, prints a map of the vfs
115 void dumpMap();
116 void dumpLockCounts();
117 void dumpStatistics();
118 void dumpFiles();
119
120protected:
121 void removeFileBlock(LLVFSFileBlock *fileblock);
122
123 void eraseBlockLength(LLVFSBlock *block);
124 void eraseBlock(LLVFSBlock *block);
125 void addFreeBlock(LLVFSBlock *block);
126 //void mergeFreeBlocks();
127 void useFreeSpace(LLVFSBlock *free_block, S32 length);
128 void sync(LLVFSFileBlock *block, BOOL remove = FALSE);
129 void presizeDataFile(const U32 size);
130
131 static FILE *openAndLock(const char *filename, const char *mode, BOOL read_lock);
132 static void unlockAndClose(FILE *fp);
133
134 // Can initiate LRU-based file removal to make space.
135 // The immune file block will not be removed.
136 LLVFSBlock *findFreeBlock(S32 size, LLVFSFileBlock *immune = NULL);
137
138 // lock/unlock data mutex (mDataMutex)
139 void lockData() { mDataMutex->lock(); }
140 void unlockData() { mDataMutex->unlock(); }
141
142protected:
143 LLMutex* mDataMutex;
144
145 typedef std::map<LLVFSFileSpecifier, LLVFSFileBlock*> fileblock_map;
146 fileblock_map mFileBlocks;
147
148 typedef std::multimap<S32, LLVFSBlock*> blocks_length_map_t;
149 blocks_length_map_t mFreeBlocksByLength;
150 typedef std::multimap<U32, LLVFSBlock*> blocks_location_map_t;
151 blocks_location_map_t mFreeBlocksByLocation;
152
153 FILE *mDataFP;
154 FILE *mIndexFP;
155
156 std::deque<S32> mIndexHoles;
157
158 char *mIndexFilename;
159 char *mDataFilename;
160 BOOL mReadOnly;
161
162 EVFSValid mValid;
163
164 S32 mLockCounts[VFSLOCK_COUNT];
165 BOOL mRemoveAfterCrash;
166};
167
168extern LLVFS *gVFS;
169
170#endif
diff --git a/linden/indra/llvfs/llvfs.vcproj b/linden/indra/llvfs/llvfs.vcproj
new file mode 100644
index 0000000..f5df235
--- /dev/null
+++ b/linden/indra/llvfs/llvfs.vcproj
@@ -0,0 +1,201 @@
1<?xml version="1.0" encoding="Windows-1252"?>
2<VisualStudioProject
3 ProjectType="Visual C++"
4 Version="7.10"
5 Name="llvfs"
6 ProjectGUID="{D37774F4-253D-4760-BF64-372A943224A1}"
7 Keyword="Win32Proj">
8 <Platforms>
9 <Platform
10 Name="Win32"/>
11 </Platforms>
12 <Configurations>
13 <Configuration
14 Name="Debug|Win32"
15 OutputDirectory="../lib_$(ConfigurationName)/i686-win32"
16 IntermediateDirectory="Debug"
17 ConfigurationType="4"
18 CharacterSet="1">
19 <Tool
20 Name="VCCLCompilerTool"
21 Optimization="0"
22 AdditionalIncludeDirectories="..\..\libraries\i686-win32\include;..\..\libraries\include\;..\llcommon;..\llmath"
23 PreprocessorDefinitions="WIN32;_DEBUG;_LIB;LL_WINDOWS;LL_DEBUG"
24 MinimalRebuild="TRUE"
25 BasicRuntimeChecks="3"
26 RuntimeLibrary="1"
27 StructMemberAlignment="4"
28 ForceConformanceInForLoopScope="TRUE"
29 UsePrecompiledHeader="0"
30 WarningLevel="3"
31 WarnAsError="TRUE"
32 Detect64BitPortabilityProblems="FALSE"
33 DebugInformationFormat="4"/>
34 <Tool
35 Name="VCCustomBuildTool"/>
36 <Tool
37 Name="VCLibrarianTool"
38 OutputFile="$(OutDir)/llvfs.lib"/>
39 <Tool
40 Name="VCMIDLTool"/>
41 <Tool
42 Name="VCPostBuildEventTool"/>
43 <Tool
44 Name="VCPreBuildEventTool"/>
45 <Tool
46 Name="VCPreLinkEventTool"/>
47 <Tool
48 Name="VCResourceCompilerTool"/>
49 <Tool
50 Name="VCWebServiceProxyGeneratorTool"/>
51 <Tool
52 Name="VCXMLDataGeneratorTool"/>
53 <Tool
54 Name="VCManagedWrapperGeneratorTool"/>
55 <Tool
56 Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
57 </Configuration>
58 <Configuration
59 Name="Release|Win32"
60 OutputDirectory="../lib_$(ConfigurationName)/i686-win32"
61 IntermediateDirectory="Release"
62 ConfigurationType="4"
63 CharacterSet="1">
64 <Tool
65 Name="VCCLCompilerTool"
66 AdditionalIncludeDirectories="..\..\libraries\i686-win32\include;..\..\libraries\include\;..\llcommon;..\llmath"
67 PreprocessorDefinitions="WIN32;NDEBUG;_LIB;LL_WINDOWS;LL_RELEASE"
68 RuntimeLibrary="0"
69 StructMemberAlignment="0"
70 ForceConformanceInForLoopScope="TRUE"
71 UsePrecompiledHeader="0"
72 WarningLevel="3"
73 WarnAsError="TRUE"
74 Detect64BitPortabilityProblems="FALSE"
75 DebugInformationFormat="3"/>
76 <Tool
77 Name="VCCustomBuildTool"/>
78 <Tool
79 Name="VCLibrarianTool"
80 OutputFile="$(OutDir)/llvfs.lib"/>
81 <Tool
82 Name="VCMIDLTool"/>
83 <Tool
84 Name="VCPostBuildEventTool"/>
85 <Tool
86 Name="VCPreBuildEventTool"/>
87 <Tool
88 Name="VCPreLinkEventTool"/>
89 <Tool
90 Name="VCResourceCompilerTool"/>
91 <Tool
92 Name="VCWebServiceProxyGeneratorTool"/>
93 <Tool
94 Name="VCXMLDataGeneratorTool"/>
95 <Tool
96 Name="VCManagedWrapperGeneratorTool"/>
97 <Tool
98 Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
99 </Configuration>
100 <Configuration
101 Name="ReleaseNoOpt|Win32"
102 OutputDirectory="../lib_$(ConfigurationName)/i686-win32"
103 IntermediateDirectory="$(ConfigurationName)"
104 ConfigurationType="4"
105 CharacterSet="1">
106 <Tool
107 Name="VCCLCompilerTool"
108 Optimization="0"
109 AdditionalIncludeDirectories="..\..\libraries\i686-win32\include;..\..\libraries\include\;..\llcommon;..\llmath"
110 PreprocessorDefinitions="WIN32;NDEBUG;_LIB;LL_WINDOWS;LL_RELEASE"
111 RuntimeLibrary="0"
112 StructMemberAlignment="0"
113 ForceConformanceInForLoopScope="TRUE"
114 UsePrecompiledHeader="0"
115 WarningLevel="3"
116 WarnAsError="TRUE"
117 Detect64BitPortabilityProblems="FALSE"
118 DebugInformationFormat="3"/>
119 <Tool
120 Name="VCCustomBuildTool"/>
121 <Tool
122 Name="VCLibrarianTool"
123 OutputFile="$(OutDir)/llvfs.lib"/>
124 <Tool
125 Name="VCMIDLTool"/>
126 <Tool
127 Name="VCPostBuildEventTool"/>
128 <Tool
129 Name="VCPreBuildEventTool"/>
130 <Tool
131 Name="VCPreLinkEventTool"/>
132 <Tool
133 Name="VCResourceCompilerTool"/>
134 <Tool
135 Name="VCWebServiceProxyGeneratorTool"/>
136 <Tool
137 Name="VCXMLDataGeneratorTool"/>
138 <Tool
139 Name="VCManagedWrapperGeneratorTool"/>
140 <Tool
141 Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
142 </Configuration>
143 </Configurations>
144 <References>
145 </References>
146 <Files>
147 <Filter
148 Name="Source Files"
149 Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
150 UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
151 <File
152 RelativePath=".\lldir.cpp">
153 </File>
154 <File
155 RelativePath=".\lldir_win32.cpp">
156 </File>
157 <File
158 RelativePath=".\lllfsthread.cpp">
159 </File>
160 <File
161 RelativePath=".\llvfile.cpp">
162 </File>
163 <File
164 RelativePath=".\llvfs.cpp">
165 </File>
166 <File
167 RelativePath=".\llvfsthread.cpp">
168 </File>
169 </Filter>
170 <Filter
171 Name="Header Files"
172 Filter="h;hpp;hxx;hm;inl;inc;xsd"
173 UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
174 <File
175 RelativePath=".\lldir.h">
176 </File>
177 <File
178 RelativePath=".\lldir_win32.h">
179 </File>
180 <File
181 RelativePath=".\lllfsthread.h">
182 </File>
183 <File
184 RelativePath=".\llvfile.h">
185 </File>
186 <File
187 RelativePath=".\llvfs.h">
188 </File>
189 <File
190 RelativePath=".\llvfsthread.h">
191 </File>
192 </Filter>
193 <Filter
194 Name="Resource Files"
195 Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
196 UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
197 </Filter>
198 </Files>
199 <Globals>
200 </Globals>
201</VisualStudioProject>
diff --git a/linden/indra/llvfs/llvfsthread.cpp b/linden/indra/llvfs/llvfsthread.cpp
new file mode 100644
index 0000000..be23bd5
--- /dev/null
+++ b/linden/indra/llvfs/llvfsthread.cpp
@@ -0,0 +1,314 @@
1/**
2 * @file llvfsthread.cpp
3 * @brief LLVFSThread implementation
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29#include "llmath.h"
30#include "llvfsthread.h"
31#include "llstl.h"
32
33//============================================================================
34
35/*static*/ std::string LLVFSThread::sDataPath = "";
36
37/*static*/ LLVFSThread* LLVFSThread::sLocal = NULL;
38
39//============================================================================
40// Run on MAIN thread
41//static
42void LLVFSThread::initClass(bool local_is_threaded, bool local_run_always)
43{
44 llassert(sLocal == NULL);
45 sLocal = new LLVFSThread(local_is_threaded, local_run_always);
46}
47
48//static
49S32 LLVFSThread::updateClass(U32 ms_elapsed)
50{
51 sLocal->update(ms_elapsed);
52 return sLocal->getPending();
53}
54
55//static
56void LLVFSThread::cleanupClass()
57{
58 sLocal->setQuitting();
59 while (sLocal->getPending())
60 {
61 sLocal->update(0);
62 }
63 delete sLocal;
64 sLocal = 0;
65}
66
67//----------------------------------------------------------------------------
68
69LLVFSThread::LLVFSThread(bool threaded, bool runalways) :
70 LLQueuedThread("VFS", threaded, runalways)
71{
72}
73
74LLVFSThread::~LLVFSThread()
75{
76 // ~LLQueuedThread() will be called here
77}
78
79//----------------------------------------------------------------------------
80
81LLVFSThread::handle_t LLVFSThread::read(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
82 U8* buffer, S32 offset, S32 numbytes, U32 priority, U32 flags)
83{
84 handle_t handle = generateHandle();
85
86 priority = llmax(priority, (U32)PRIORITY_LOW); // All reads are at least PRIORITY_LOW
87 Request* req = new Request(handle, priority, flags, FILE_READ, vfs, file_id, file_type,
88 buffer, offset, numbytes);
89
90 bool res = addRequest(req);
91 if (!res)
92 {
93 llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
94 req->deleteRequest();
95 handle = nullHandle();
96 }
97
98 return handle;
99}
100
101S32 LLVFSThread::readImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
102 U8* buffer, S32 offset, S32 numbytes)
103{
104 handle_t handle = generateHandle();
105
106 Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0, FILE_READ, vfs, file_id, file_type,
107 buffer, offset, numbytes);
108
109 S32 res = addRequest(req) ? 1 : 0;
110 if (res == 0)
111 {
112 llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
113 req->deleteRequest();
114 }
115 else
116 {
117 llverify(waitForResult(handle, false) == true);
118 res = req->getBytesRead();
119 completeRequest(handle);
120 }
121 return res;
122}
123
124LLVFSThread::handle_t LLVFSThread::write(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
125 U8* buffer, S32 offset, S32 numbytes, U32 flags)
126{
127 handle_t handle = generateHandle();
128
129 Request* req = new Request(handle, 0, flags, FILE_WRITE, vfs, file_id, file_type,
130 buffer, offset, numbytes);
131
132 bool res = addRequest(req);
133 if (!res)
134 {
135 llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
136 req->deleteRequest();
137 handle = nullHandle();
138 }
139
140 return handle;
141}
142
143S32 LLVFSThread::writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
144 U8* buffer, S32 offset, S32 numbytes)
145{
146 handle_t handle = generateHandle();
147
148 Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0, FILE_WRITE, vfs, file_id, file_type,
149 buffer, offset, numbytes);
150
151 S32 res = addRequest(req) ? 1 : 0;
152 if (res == 0)
153 {
154 llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
155 req->deleteRequest();
156 }
157 else
158 {
159 llverify(waitForResult(handle, false) == true);
160 res = req->getBytesRead();
161 completeRequest(handle);
162 }
163 return res;
164}
165
166
167LLVFSThread::handle_t LLVFSThread::rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
168 const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags)
169{
170 handle_t handle = generateHandle();
171
172 LLUUID* new_idp = new LLUUID(new_id); // deleted with Request
173 // new_type is passed as "numbytes"
174 Request* req = new Request(handle, 0, flags, FILE_RENAME, vfs, file_id, file_type,
175 (U8*)new_idp, 0, (S32)new_type);
176
177 bool res = addRequest(req);
178 if (!res)
179 {
180 llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
181 req->deleteRequest();
182 handle = nullHandle();
183 }
184
185 return handle;
186}
187
188//============================================================================
189// Runs on its OWN thread
190
191bool LLVFSThread::processRequest(QueuedRequest* qreq)
192{
193 Request *req = (Request*)qreq;
194
195 bool complete = req->processIO();
196
197 return complete;
198}
199
200//============================================================================
201
202LLVFSThread::Request::Request(handle_t handle, U32 priority, U32 flags,
203 operation_t op, LLVFS* vfs,
204 const LLUUID &file_id, const LLAssetType::EType file_type,
205 U8* buffer, S32 offset, S32 numbytes) :
206 QueuedRequest(handle, priority, flags),
207 mOperation(op),
208 mVFS(vfs),
209 mFileID(file_id),
210 mFileType(file_type),
211 mBuffer(buffer),
212 mOffset(offset),
213 mBytes(numbytes),
214 mBytesRead(0)
215{
216 llassert(mBuffer);
217
218 if (numbytes <= 0 && mOperation != FILE_RENAME)
219 {
220 llwarns << "LLVFSThread: Request with numbytes = " << numbytes
221 << " operation = " << op
222 << " offset " << offset
223 << " file_type " << file_type << llendl;
224 }
225 if (mOperation == FILE_WRITE)
226 {
227 S32 blocksize = mVFS->getMaxSize(mFileID, mFileType);
228 if (blocksize < 0)
229 {
230 llwarns << "VFS write to temporary block (shouldn't happen)" << llendl;
231 }
232 mVFS->incLock(mFileID, mFileType, VFSLOCK_APPEND);
233 }
234 else if (mOperation == FILE_RENAME)
235 {
236 mVFS->incLock(mFileID, mFileType, VFSLOCK_APPEND);
237 }
238 else // if (mOperation == FILE_READ)
239 {
240 mVFS->incLock(mFileID, mFileType, VFSLOCK_READ);
241 }
242}
243
244// dec locks as soon as a request finishes
245void LLVFSThread::Request::finishRequest()
246{
247 if (mOperation == FILE_WRITE)
248 {
249 mVFS->decLock(mFileID, mFileType, VFSLOCK_APPEND);
250 }
251 else if (mOperation == FILE_RENAME)
252 {
253 mVFS->decLock(mFileID, mFileType, VFSLOCK_APPEND);
254 }
255 else // if (mOperation == FILE_READ)
256 {
257 mVFS->decLock(mFileID, mFileType, VFSLOCK_READ);
258 }
259}
260
261void LLVFSThread::Request::deleteRequest()
262{
263 if (getStatus() == STATUS_QUEUED || getStatus() == STATUS_ABORT)
264 {
265 llerrs << "Attempt to delete a queued LLVFSThread::Request!" << llendl;
266 }
267 if (mOperation == FILE_WRITE)
268 {
269 if (mFlags & AUTO_DELETE)
270 {
271 delete [] mBuffer;
272 }
273 }
274 else if (mOperation == FILE_RENAME)
275 {
276 LLUUID* new_idp = (LLUUID*)mBuffer;
277 delete new_idp;
278 }
279 LLQueuedThread::QueuedRequest::deleteRequest();
280}
281
282bool LLVFSThread::Request::processIO()
283{
284 bool complete = false;
285 if (mOperation == FILE_READ)
286 {
287 llassert(mOffset >= 0);
288 mBytesRead = mVFS->getData(mFileID, mFileType, mBuffer, mOffset, mBytes);
289 complete = true;
290 //llinfos << llformat("LLVFSThread::READ '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl;
291 }
292 else if (mOperation == FILE_WRITE)
293 {
294 mBytesRead = mVFS->storeData(mFileID, mFileType, mBuffer, mOffset, mBytes);
295 complete = true;
296 //llinfos << llformat("LLVFSThread::WRITE '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl;
297 }
298 else if (mOperation == FILE_RENAME)
299 {
300 LLUUID* new_idp = (LLUUID*)mBuffer;
301 LLAssetType::EType new_type = (LLAssetType::EType)mBytes;
302 mVFS->renameFile(mFileID, mFileType, *new_idp, new_type);
303 mFileID = *new_idp;
304 complete = true;
305 //llinfos << llformat("LLVFSThread::WRITE '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl;
306 }
307 else
308 {
309 llerrs << llformat("LLVFSThread::unknown operation: %d", mOperation) << llendl;
310 }
311 return complete;
312}
313
314//============================================================================
diff --git a/linden/indra/llvfs/llvfsthread.h b/linden/indra/llvfs/llvfsthread.h
new file mode 100644
index 0000000..c3a5a55
--- /dev/null
+++ b/linden/indra/llvfs/llvfsthread.h
@@ -0,0 +1,144 @@
1/**
2 * @file llvfsthread.h
3 * @brief LLVFSThread definition
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_LLVFSTHREAD_H
29#define LL_LLVFSTHREAD_H
30
31#include <queue>
32#include <string>
33#include <map>
34#include <set>
35
36#include "llapr.h"
37
38#include "llqueuedthread.h"
39
40#include "llvfs.h"
41
42//============================================================================
43
44class LLVFSThread : public LLQueuedThread
45{
46 //------------------------------------------------------------------------
47public:
48 enum operation_t {
49 FILE_READ,
50 FILE_WRITE,
51 FILE_RENAME
52 };
53
54 //------------------------------------------------------------------------
55public:
56
57 class Request : public QueuedRequest
58 {
59 protected:
60 ~Request() {}; // use deleteRequest()
61
62 public:
63 Request(handle_t handle, U32 priority, U32 flags,
64 operation_t op, LLVFS* vfs,
65 const LLUUID &file_id, const LLAssetType::EType file_type,
66 U8* buffer, S32 offset, S32 numbytes);
67
68 S32 getBytesRead()
69 {
70 return mBytesRead;
71 }
72 S32 getOperation()
73 {
74 return mOperation;
75 }
76 U8* getBuffer()
77 {
78 return mBuffer;
79 }
80 LLVFS* getVFS()
81 {
82 return mVFS;
83 }
84 std::string getFilename()
85 {
86 char tbuf[40];
87 mFileID.toString(tbuf);
88 return std::string(tbuf);
89 }
90
91 /*virtual*/ void finishRequest();
92 /*virtual*/ void deleteRequest();
93
94 bool processIO();
95
96 private:
97 operation_t mOperation;
98
99 LLVFS* mVFS;
100 LLUUID mFileID;
101 LLAssetType::EType mFileType;
102
103 U8* mBuffer; // dest for reads, source for writes, new UUID for rename
104 S32 mOffset; // offset into file, -1 = append (WRITE only)
105 S32 mBytes; // bytes to read from file, -1 = all (new mFileType for rename)
106 S32 mBytesRead; // bytes read from file
107 };
108
109 //------------------------------------------------------------------------
110public:
111 static std::string sDataPath;
112 static void setDataPath(const std::string& path) { sDataPath = path; }
113
114public:
115 LLVFSThread(bool threaded = TRUE, bool runalways = TRUE);
116 ~LLVFSThread();
117
118 // Return a Request handle
119 handle_t read(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
120 U8* buffer, S32 offset, S32 numbytes, U32 pri=PRIORITY_NORMAL, U32 flags = 0);
121 handle_t write(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
122 U8* buffer, S32 offset, S32 numbytes, U32 flags);
123 handle_t rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
124 const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags);
125 // Return number of bytes read
126 S32 readImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
127 U8* buffer, S32 offset, S32 numbytes);
128 S32 writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
129 U8* buffer, S32 offset, S32 numbytes);
130
131 /*virtual*/ bool processRequest(QueuedRequest* req);
132
133 static void initClass(bool local_is_threaded = TRUE, bool run_always = TRUE); // Setup sLocal
134 static S32 updateClass(U32 ms_elapsed);
135 static void cleanupClass(); // Delete sLocal
136
137public:
138 static LLVFSThread* sLocal; // Default worker thread
139};
140
141//============================================================================
142
143
144#endif // LL_LLVFSTHREAD_H