From 925feaaf95b43f2380a4f375ac9b6419d05732f6 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 4 Mar 2021 12:06:12 +0200 Subject: [PATCH 1/6] SL-14941 FIXED Cannot upload large images on the Simplified Cache Viewer. --- doc/contributions.txt | 1 + indra/newview/llviewerassetstorage.cpp | 2 +- indra/newview/llviewerassetupload.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/contributions.txt b/doc/contributions.txt index bbdfaf655d..120c737d7a 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -226,6 +226,7 @@ Ansariel Hiller SL-13364 SL-13858 SL-13697 + SL-14941 Aralara Rajal Arare Chantilly CHUIBUG-191 diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 5b76d57196..0f2901406a 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -291,7 +291,7 @@ void LLViewerAssetStorage::storeAssetData( legacy->mUpCallback = callback; legacy->mUserData = user_data; - LLFileSystem file(asset_id, asset_type, LLFileSystem::WRITE); + LLFileSystem file(asset_id, asset_type, LLFileSystem::APPEND); const S32 buf_size = 65536; U8 copy_buf[buf_size]; diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 1923e7d6ff..7b5229d312 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -473,7 +473,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() infile.open(filename, LL_APR_RB, NULL, &file_size); if (infile.getFileHandle()) { - LLFileSystem file(getAssetId(), assetType, LLFileSystem::WRITE); + LLFileSystem file(getAssetId(), assetType, LLFileSystem::APPEND); const S32 buf_size = 65536; U8 copy_buf[buf_size]; From 87908bab6d7a0283e7195566dd99926a4c8401b1 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 4 Mar 2021 12:23:57 +0200 Subject: [PATCH 2/6] SL-14939 Fixed the log spam --- doc/contributions.txt | 1 + indra/llfilesystem/llfilesystem.cpp | 6 +++--- indra/llfilesystem/llfilesystem.h | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/contributions.txt b/doc/contributions.txt index 120c737d7a..877d38c2a1 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -226,6 +226,7 @@ Ansariel Hiller SL-13364 SL-13858 SL-13697 + SL-14939 SL-14941 Aralara Rajal Arare Chantilly diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 64e0b9f193..053b52014e 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -72,14 +72,14 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil } // static -bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type) +bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error /*= 0*/) { std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info); - LLFile::remove(filename.c_str()); + LLFile::remove(filename.c_str(), suppress_error); return true; } @@ -98,7 +98,7 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp const std::string new_filename = LLDiskCache::getInstance()->metaDataToFilepath(new_id_str, new_file_type, extra_info); // Rename needs the new file to not exist. - LLFileSystem::removeFile(new_file_id, new_file_type); + LLFileSystem::removeFile(new_file_id, new_file_type, ENOENT); if (LLFile::rename(old_filename, new_filename) != 0) { diff --git a/indra/llfilesystem/llfilesystem.h b/indra/llfilesystem/llfilesystem.h index 89bfff5798..d934a408c2 100644 --- a/indra/llfilesystem/llfilesystem.h +++ b/indra/llfilesystem/llfilesystem.h @@ -54,7 +54,7 @@ class LLFileSystem BOOL remove(); static bool getExists(const LLUUID& file_id, const LLAssetType::EType file_type); - static bool removeFile(const LLUUID& file_id, const LLAssetType::EType file_type); + static bool removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error = 0); static bool renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type, const LLUUID& new_file_id, const LLAssetType::EType new_file_type); static S32 getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type); From ada8ad1bc21665631f030c3194a567ea6f6a2a72 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 4 Mar 2021 12:49:48 +0200 Subject: [PATCH 3/6] SL-14940 Fixed ignoring custom cache path --- doc/contributions.txt | 1 + indra/newview/llappviewer.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/contributions.txt b/doc/contributions.txt index 877d38c2a1..bf95989f19 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -227,6 +227,7 @@ Ansariel Hiller SL-13858 SL-13697 SL-14939 + SL-14940 SL-14941 Aralara Rajal Arare Chantilly diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ea0b950e62..6ede9ec0e7 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4132,8 +4132,6 @@ bool LLAppViewer::initCache() const unsigned int disk_cache_mb = cache_total_size_mb * disk_cache_percent / 100; const unsigned int disk_cache_bytes = disk_cache_mb * 1024 * 1024; const bool enable_cache_debug_info = gSavedSettings.getBOOL("EnableDiskCacheDebugInfo"); - const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); - LLDiskCache::initParamSingleton(cache_dir, disk_cache_bytes, enable_cache_debug_info); bool texture_cache_mismatch = false; if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getTextureCacheVersion()) @@ -4181,6 +4179,9 @@ bool LLAppViewer::initCache() gSavedSettings.setString("CacheLocationTopFolder", ""); } + const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); + LLDiskCache::initParamSingleton(cache_dir, disk_cache_bytes, enable_cache_debug_info); + if (!read_only) { if (mPurgeCache) From 168d177197bd7558bbe0ca13d01c984ad8638da7 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Tue, 9 Mar 2021 14:39:51 -0800 Subject: [PATCH 4/6] This set of changes reverts the merge with master (git revert c83e740) and results in a version of the DRTVWR-519 that matches what was presemt before it was deployed as a release viewer *plus* 3 small fixes from Maxim (See commits). This branch can now be used for additional fixes before eventually being used to release D-519 as normal --- README.md | 1 + indra/CMakeLists.txt | 2 +- indra/cmake/00-Common.cmake | 3 +- indra/cmake/CMakeLists.txt | 2 +- indra/cmake/LLFileSystem.cmake | 7 + indra/cmake/LLVFS.cmake | 7 - .../llimage_libtest/CMakeLists.txt | 6 +- .../llui_libtest/CMakeLists.txt | 4 +- indra/linux_crash_logger/CMakeLists.txt | 7 +- indra/llappearance/CMakeLists.txt | 20 +- indra/llappearance/lltexlayer.cpp | 2 - indra/llaudio/CMakeLists.txt | 6 +- indra/llaudio/llaudiodecodemgr.cpp | 58 +- indra/llaudio/llaudiodecodemgr.h | 1 - indra/llaudio/llaudioengine.cpp | 15 +- indra/llaudio/llaudioengine.h | 13 +- indra/llcharacter/CMakeLists.txt | 18 +- indra/llcharacter/llkeyframefallmotion.cpp | 5 + indra/llcharacter/llkeyframemotion.cpp | 21 +- indra/llcharacter/llkeyframemotion.h | 12 +- indra/llcommon/llerror.cpp | 61 +- indra/llcrashlogger/CMakeLists.txt | 4 +- indra/llcrashlogger/llcrashlock.h | 2 +- indra/{llvfs => llfilesystem}/CMakeLists.txt | 57 +- indra/{llvfs => llfilesystem}/lldir.cpp | 0 indra/{llvfs => llfilesystem}/lldir.h | 0 indra/{llvfs => llfilesystem}/lldir_linux.cpp | 0 indra/{llvfs => llfilesystem}/lldir_linux.h | 0 indra/{llvfs => llfilesystem}/lldir_mac.cpp | 2 +- indra/{llvfs => llfilesystem}/lldir_mac.h | 0 .../{llvfs => llfilesystem}/lldir_solaris.cpp | 0 indra/{llvfs => llfilesystem}/lldir_solaris.h | 0 .../lldir_utils_objc.h} | 12 +- .../lldir_utils_objc.mm} | 8 +- indra/{llvfs => llfilesystem}/lldir_win32.cpp | 0 indra/{llvfs => llfilesystem}/lldir_win32.h | 0 indra/{llvfs => llfilesystem}/lldirguard.h | 0 .../{llvfs => llfilesystem}/lldiriterator.cpp | 0 indra/{llvfs => llfilesystem}/lldiriterator.h | 0 indra/llfilesystem/lldiskcache.cpp | 327 +++ indra/llfilesystem/lldiskcache.h | 183 ++ indra/llfilesystem/llfilesystem.cpp | 283 +++ indra/llfilesystem/llfilesystem.h | 78 + indra/{llvfs => llfilesystem}/lllfsthread.cpp | 0 indra/{llvfs => llfilesystem}/lllfsthread.h | 0 .../tests/lldir_test.cpp | 0 .../tests/lldiriterator_test.cpp | 0 indra/llimage/CMakeLists.txt | 6 +- indra/llimage/llimage.cpp | 9 - indra/llinventory/CMakeLists.txt | 4 +- indra/llmessage/CMakeLists.txt | 12 +- indra/llmessage/llassetstorage.cpp | 97 +- indra/llmessage/llassetstorage.h | 26 +- indra/llmessage/llcorehttputil.cpp | 4 +- indra/llmessage/llextendedstatus.h | 14 +- indra/llmessage/lltransfersourceasset.cpp | 8 +- indra/llmessage/lltransfersourceasset.h | 4 +- indra/llmessage/lltransfertargetvfile.cpp | 9 +- indra/llmessage/lltransfertargetvfile.h | 4 +- indra/llmessage/llxfer_vfile.cpp | 57 +- indra/llmessage/llxfer_vfile.h | 12 +- indra/llmessage/llxfermanager.cpp | 35 +- indra/llmessage/llxfermanager.h | 12 +- indra/llrender/CMakeLists.txt | 12 +- indra/llui/CMakeLists.txt | 6 +- indra/llui/llviewereventrecorder.h | 1 - indra/llvfs/llpidlock.cpp | 276 -- indra/llvfs/llpidlock.h | 60 - indra/llvfs/llvfile.cpp | 437 ---- indra/llvfs/llvfile.h | 90 - indra/llvfs/llvfs.cpp | 2220 ----------------- indra/llvfs/llvfs.h | 183 -- indra/llvfs/llvfsthread.cpp | 300 --- indra/llvfs/llvfsthread.h | 140 -- indra/llwindow/CMakeLists.txt | 8 +- indra/llxml/CMakeLists.txt | 6 +- indra/mac_crash_logger/CMakeLists.txt | 7 +- indra/mac_crash_logger/mac_crash_logger.cpp | 1 - indra/newview/CMakeLists.txt | 10 +- indra/newview/app_settings/settings.xml | 81 +- indra/newview/app_settings/static_data.db2 | Bin 576578 -> 0 bytes indra/newview/app_settings/static_index.db2 | Bin 9894 -> 0 bytes indra/newview/llappviewer.cpp | 294 +-- indra/newview/llappviewer.h | 9 +- indra/newview/llappviewerwin32.cpp | 2 +- indra/newview/llcompilequeue.cpp | 12 +- indra/newview/llcompilequeue.h | 2 +- indra/newview/llfilepicker.h | 2 +- indra/newview/llfloaterauction.cpp | 11 +- indra/newview/llfloaterbvhpreview.cpp | 5 +- indra/newview/llfloatermodelpreview.cpp | 1 + indra/newview/llfloaterpreference.h | 2 +- indra/newview/llfloaterregioninfo.cpp | 11 +- indra/newview/llfloaterregioninfo.h | 4 +- indra/newview/llfloaterreporter.cpp | 12 +- indra/newview/llfloatertos.cpp | 2 +- indra/newview/llfloatertos.h | 1 - indra/newview/llgesturemgr.cpp | 20 +- indra/newview/llgesturemgr.h | 11 +- indra/newview/lllandmarklist.cpp | 5 +- indra/newview/lllandmarklist.h | 1 - indra/newview/llmeshrepository.cpp | 73 +- indra/newview/llmeshrepository.h | 1 - indra/newview/lloutfitgallery.cpp | 2 +- indra/newview/lloutfitgallery.h | 1 - indra/newview/llpostcard.cpp | 3 +- indra/newview/llpreviewgesture.cpp | 16 +- indra/newview/llpreviewgesture.h | 4 +- indra/newview/llpreviewnotecard.cpp | 16 +- indra/newview/llpreviewnotecard.h | 3 +- indra/newview/llpreviewscript.cpp | 30 +- indra/newview/llpreviewscript.h | 11 +- indra/newview/llsettingsvo.cpp | 10 +- indra/newview/llsettingsvo.h | 3 +- indra/newview/llsnapshotlivepreview.cpp | 6 +- indra/newview/llstartup.cpp | 16 +- indra/newview/lltexturecache.cpp | 46 +- indra/newview/llviewerassetstorage.cpp | 53 +- indra/newview/llviewerassetstorage.h | 8 +- indra/newview/llviewerassetupload.cpp | 19 +- indra/newview/llviewerassetupload.h | 2 +- .../newview/llviewermedia_streamingaudio.cpp | 2 - indra/newview/llviewermenufile.cpp | 2 - indra/newview/llviewermessage.cpp | 12 +- indra/newview/llviewermessage.h | 4 +- indra/newview/llviewerprecompiledheaders.h | 1 - indra/newview/llviewerstats.cpp | 3 - indra/newview/llviewerstats.h | 1 - indra/newview/llviewertexlayer.cpp | 2 - indra/newview/llviewertexture.cpp | 2 - indra/newview/llviewertexture.h | 2 +- indra/newview/llviewertexturelist.cpp | 4 +- indra/newview/llviewerwearable.cpp | 1 - indra/newview/llvoavatar.cpp | 1 - indra/newview/llvovolume.h | 7 +- .../skins/default/xui/da/floater_stats.xml | 1 - .../newview/skins/default/xui/da/strings.xml | 3 - .../xui/de/floater_scene_load_stats.xml | 1 - .../skins/default/xui/de/floater_stats.xml | 1 - .../newview/skins/default/xui/de/strings.xml | 4 - .../xui/en/floater_scene_load_stats.xml | 6 - .../skins/default/xui/en/floater_stats.xml | 4 - .../newview/skins/default/xui/en/strings.xml | 5 +- .../xui/es/floater_scene_load_stats.xml | 1 - .../skins/default/xui/es/floater_stats.xml | 1 - .../newview/skins/default/xui/es/strings.xml | 4 - .../xui/fr/floater_scene_load_stats.xml | 1 - .../skins/default/xui/fr/floater_stats.xml | 1 - .../newview/skins/default/xui/fr/strings.xml | 4 - .../xui/it/floater_scene_load_stats.xml | 1 - .../skins/default/xui/it/floater_stats.xml | 1 - .../newview/skins/default/xui/it/strings.xml | 4 - .../xui/ja/floater_scene_load_stats.xml | 1 - .../skins/default/xui/ja/floater_stats.xml | 1 - .../newview/skins/default/xui/ja/strings.xml | 4 - .../xui/pl/floater_scene_load_stats.xml | 1 - .../skins/default/xui/pl/floater_stats.xml | 1 - .../newview/skins/default/xui/pl/strings.xml | 3 - .../xui/pt/floater_scene_load_stats.xml | 1 - .../skins/default/xui/pt/floater_stats.xml | 1 - .../newview/skins/default/xui/pt/strings.xml | 4 - .../xui/ru/floater_scene_load_stats.xml | 1 - .../skins/default/xui/ru/floater_stats.xml | 1 - .../newview/skins/default/xui/ru/strings.xml | 4 - .../xui/tr/floater_scene_load_stats.xml | 1 - .../skins/default/xui/tr/floater_stats.xml | 1 - .../newview/skins/default/xui/tr/strings.xml | 4 - .../xui/zh/floater_scene_load_stats.xml | 1 - .../skins/default/xui/zh/floater_stats.xml | 1 - .../newview/skins/default/xui/zh/strings.xml | 4 - indra/newview/viewer_manifest.py | 1 - indra/test/CMakeLists.txt | 7 +- indra/win_crash_logger/CMakeLists.txt | 6 +- 173 files changed, 1455 insertions(+), 4789 deletions(-) create mode 100644 indra/cmake/LLFileSystem.cmake delete mode 100644 indra/cmake/LLVFS.cmake rename indra/{llvfs => llfilesystem}/CMakeLists.txt (51%) rename indra/{llvfs => llfilesystem}/lldir.cpp (100%) rename indra/{llvfs => llfilesystem}/lldir.h (100%) rename indra/{llvfs => llfilesystem}/lldir_linux.cpp (100%) rename indra/{llvfs => llfilesystem}/lldir_linux.h (100%) rename indra/{llvfs => llfilesystem}/lldir_mac.cpp (99%) rename indra/{llvfs => llfilesystem}/lldir_mac.h (100%) rename indra/{llvfs => llfilesystem}/lldir_solaris.cpp (100%) rename indra/{llvfs => llfilesystem}/lldir_solaris.h (100%) rename indra/{llvfs/llvfs_objc.h => llfilesystem/lldir_utils_objc.h} (85%) rename indra/{llvfs/llvfs_objc.mm => llfilesystem/lldir_utils_objc.mm} (95%) rename indra/{llvfs => llfilesystem}/lldir_win32.cpp (100%) rename indra/{llvfs => llfilesystem}/lldir_win32.h (100%) rename indra/{llvfs => llfilesystem}/lldirguard.h (100%) rename indra/{llvfs => llfilesystem}/lldiriterator.cpp (100%) rename indra/{llvfs => llfilesystem}/lldiriterator.h (100%) create mode 100644 indra/llfilesystem/lldiskcache.cpp create mode 100644 indra/llfilesystem/lldiskcache.h create mode 100644 indra/llfilesystem/llfilesystem.cpp create mode 100644 indra/llfilesystem/llfilesystem.h rename indra/{llvfs => llfilesystem}/lllfsthread.cpp (100%) rename indra/{llvfs => llfilesystem}/lllfsthread.h (100%) rename indra/{llvfs => llfilesystem}/tests/lldir_test.cpp (100%) rename indra/{llvfs => llfilesystem}/tests/lldiriterator_test.cpp (100%) delete mode 100644 indra/llvfs/llpidlock.cpp delete mode 100644 indra/llvfs/llpidlock.h delete mode 100644 indra/llvfs/llvfile.cpp delete mode 100644 indra/llvfs/llvfile.h delete mode 100644 indra/llvfs/llvfs.cpp delete mode 100644 indra/llvfs/llvfs.h delete mode 100644 indra/llvfs/llvfsthread.cpp delete mode 100644 indra/llvfs/llvfsthread.h delete mode 100644 indra/newview/app_settings/static_data.db2 delete mode 100644 indra/newview/app_settings/static_index.db2 diff --git a/README.md b/README.md index e4078770f3..729c0ae368 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,4 @@ To download the current default version, visit [the download page](https://secondlife.com/support/downloads). For even newer versions try [the Alternate Viewers page](https://wiki.secondlife.com/wiki/Linden_Lab_Official:Alternate_Viewers) + diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 53e5d7b6a5..4b39bfe332 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -40,7 +40,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llmath) add_subdirectory(${LIBS_OPEN_PREFIX}llmessage) add_subdirectory(${LIBS_OPEN_PREFIX}llprimitive) add_subdirectory(${LIBS_OPEN_PREFIX}llrender) -add_subdirectory(${LIBS_OPEN_PREFIX}llvfs) +add_subdirectory(${LIBS_OPEN_PREFIX}llfilesystem) add_subdirectory(${LIBS_OPEN_PREFIX}llwindow) add_subdirectory(${LIBS_OPEN_PREFIX}llxml) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 8aea50e02b..f4071793d5 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -66,11 +66,10 @@ if (WINDOWS) # CP changed to only append the flag for 32bit builds - on 64bit builds, # locally at least, the build output is spammed with 1000s of 'D9002' # warnings about this switch being ignored. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") if( ADDRESS_SIZE EQUAL 32 ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /p:PreferredToolArchitecture=x64") endif() - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Zo" CACHE STRING "C++ compiler release-with-debug options" FORCE) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index a17e37cd32..352dfc0641 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -69,7 +69,7 @@ set(cmake_SOURCE_FILES LLSharedLibs.cmake LLTestCommand.cmake LLUI.cmake - LLVFS.cmake + LLFileSystem.cmake LLWindow.cmake LLXML.cmake Linking.cmake diff --git a/indra/cmake/LLFileSystem.cmake b/indra/cmake/LLFileSystem.cmake new file mode 100644 index 0000000000..2e6c42c30c --- /dev/null +++ b/indra/cmake/LLFileSystem.cmake @@ -0,0 +1,7 @@ +# -*- cmake -*- + +set(LLFILESYSTEM_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/llfilesystem + ) + +set(LLFILESYSTEM_LIBRARIES llfilesystem) diff --git a/indra/cmake/LLVFS.cmake b/indra/cmake/LLVFS.cmake deleted file mode 100644 index 0fe87cdea6..0000000000 --- a/indra/cmake/LLVFS.cmake +++ /dev/null @@ -1,7 +0,0 @@ -# -*- cmake -*- - -set(LLVFS_INCLUDE_DIRS - ${LIBS_OPEN_DIR}/llvfs - ) - -set(LLVFS_LIBRARIES llvfs) diff --git a/indra/integration_tests/llimage_libtest/CMakeLists.txt b/indra/integration_tests/llimage_libtest/CMakeLists.txt index 5787d4d600..bd59f57e49 100644 --- a/indra/integration_tests/llimage_libtest/CMakeLists.txt +++ b/indra/integration_tests/llimage_libtest/CMakeLists.txt @@ -10,11 +10,11 @@ include(LLImage) include(LLMath) include(LLImageJ2COJ) include(LLKDU) -include(LLVFS) +include(LLFileSystem) include_directories( ${LLCOMMON_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLIMAGE_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ) @@ -66,7 +66,7 @@ endif (DARWIN) target_link_libraries(llimage_libtest ${LEGACY_STDIO_LIBS} ${LLCOMMON_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLKDU_LIBRARIES} diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt index 1cec660eb0..d7706e73b2 100644 --- a/indra/integration_tests/llui_libtest/CMakeLists.txt +++ b/indra/integration_tests/llui_libtest/CMakeLists.txt @@ -16,7 +16,7 @@ include(LLMessage) include(LLRender) include(LLWindow) include(LLUI) -include(LLVFS) # ugh, needed for LLDir +include(LLFileSystem) include(LLXML) include(Hunspell) include(Linking) @@ -29,7 +29,7 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLUI_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LIBS_PREBUILD_DIR}/include/hunspell diff --git a/indra/linux_crash_logger/CMakeLists.txt b/indra/linux_crash_logger/CMakeLists.txt index d789c850a0..aa82ed12cc 100644 --- a/indra/linux_crash_logger/CMakeLists.txt +++ b/indra/linux_crash_logger/CMakeLists.txt @@ -9,7 +9,7 @@ include(LLCommon) include(LLCrashLogger) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLFileSystem) include(LLXML) include(Linking) include(UI) @@ -21,7 +21,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCRASHLOGGER_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} ) @@ -62,10 +62,9 @@ set(LIBRT_LIBRARY rt) target_link_libraries(linux-crash-logger ${LLCRASHLOGGER_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLXML_LIBRARIES} ${LLMESSAGE_LIBRARIES} - ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt index 20eb4678dd..268849ad74 100644 --- a/indra/llappearance/CMakeLists.txt +++ b/indra/llappearance/CMakeLists.txt @@ -11,7 +11,7 @@ include(LLMath) include(LLMessage) include(LLCoreHttp) include(LLRender) -include(LLVFS) +include(LLFileSystem) include(LLWindow) include(LLXML) include(Linking) @@ -23,7 +23,7 @@ include_directories( ${LLINVENTORY_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) @@ -83,7 +83,7 @@ target_link_libraries(llappearance ${LLINVENTORY_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLRENDER_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} @@ -100,7 +100,7 @@ if (BUILD_HEADLESS) ${LLINVENTORY_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLRENDERHEADLESS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} @@ -109,15 +109,3 @@ if (BUILD_HEADLESS) ${LLCOMMON_LIBRARIES} ) endif (BUILD_HEADLESS) - -#add unit tests -#if (LL_TESTS) -# INCLUDE(LLAddBuildTest) -# SET(llappearance_TEST_SOURCE_FILES -# # no real unit tests yet! -# ) -# LL_ADD_PROJECT_UNIT_TESTS(llappearance "${llappearance_TEST_SOURCE_FILES}") - - #set(TEST_DEBUG on) -# set(test_libs llappearance ${LLCOMMON_LIBRARIES}) -#endif (LL_TESTS) diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp index e5039141de..a4600069ce 100644 --- a/indra/llappearance/lltexlayer.cpp +++ b/indra/llappearance/lltexlayer.cpp @@ -33,8 +33,6 @@ #include "llimagej2c.h" #include "llimagetga.h" #include "lldir.h" -#include "llvfile.h" -#include "llvfs.h" #include "lltexlayerparams.h" #include "lltexturemanagerbridge.h" #include "lllocaltextureobject.h" diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index 558ede7bf6..92a5cfe22f 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -9,14 +9,14 @@ include(OPENAL) include(LLCommon) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLFileSystem) include_directories( ${LLAUDIO_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${OGG_INCLUDE_DIRS} ${VORBISENC_INCLUDE_DIRS} ${VORBISFILE_INCLUDE_DIRS} @@ -86,7 +86,7 @@ target_link_libraries( ${LLCOMMON_LIBRARIES} ${LLMATH_LIBRARIES} ${LLMESSAGE_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${VORBISENC_LIBRARIES} ${VORBISFILE_LIBRARIES} ${VORBIS_LIBRARIES} diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index e7db84f6ab..ff0aa6e76e 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -29,7 +29,7 @@ #include "llaudioengine.h" #include "lllfsthread.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "llstring.h" #include "lldir.h" #include "llendianswizzle.h" @@ -90,19 +90,17 @@ protected: LLUUID mUUID; std::vector mWAVBuffer; -#if !defined(USE_WAV_VFILE) std::string mOutFilename; LLLFSThread::handle_t mFileHandle; -#endif - LLVFile *mInFilep; + LLFileSystem *mInFilep; OggVorbis_File mVF; S32 mCurrentSection; }; -size_t vfs_read(void *ptr, size_t size, size_t nmemb, void *datasource) +size_t cache_read(void *ptr, size_t size, size_t nmemb, void *datasource) { - LLVFile *file = (LLVFile *)datasource; + LLFileSystem *file = (LLFileSystem *)datasource; if (file->read((U8*)ptr, (S32)(size * nmemb))) /*Flawfinder: ignore*/ { @@ -115,11 +113,11 @@ size_t vfs_read(void *ptr, size_t size, size_t nmemb, void *datasource) } } -S32 vfs_seek(void *datasource, ogg_int64_t offset, S32 whence) +S32 cache_seek(void *datasource, ogg_int64_t offset, S32 whence) { - LLVFile *file = (LLVFile *)datasource; + LLFileSystem *file = (LLFileSystem *)datasource; - // vfs has 31-bit files + // cache has 31-bit files if (offset > S32_MAX) { return -1; @@ -137,7 +135,7 @@ S32 vfs_seek(void *datasource, ogg_int64_t offset, S32 whence) origin = -1; break; default: - LL_ERRS("AudioEngine") << "Invalid whence argument to vfs_seek" << LL_ENDL; + LL_ERRS("AudioEngine") << "Invalid whence argument to cache_seek" << LL_ENDL; return -1; } @@ -151,16 +149,16 @@ S32 vfs_seek(void *datasource, ogg_int64_t offset, S32 whence) } } -S32 vfs_close (void *datasource) +S32 cache_close (void *datasource) { - LLVFile *file = (LLVFile *)datasource; + LLFileSystem *file = (LLFileSystem *)datasource; delete file; return 0; } -long vfs_tell (void *datasource) +long cache_tell (void *datasource) { - LLVFile *file = (LLVFile *)datasource; + LLFileSystem *file = (LLFileSystem *)datasource; return file->tell(); } @@ -172,11 +170,10 @@ LLVorbisDecodeState::LLVorbisDecodeState(const LLUUID &uuid, const std::string & mUUID = uuid; mInFilep = NULL; mCurrentSection = 0; -#if !defined(USE_WAV_VFILE) mOutFilename = out_filename; mFileHandle = LLLFSThread::nullHandle(); -#endif - // No default value for mVF, it's an ogg structure? + + // No default value for mVF, it's an ogg structure? // Hey, let's zero it anyway, for predictability. memset(&mVF, 0, sizeof(mVF)); } @@ -193,15 +190,15 @@ LLVorbisDecodeState::~LLVorbisDecodeState() BOOL LLVorbisDecodeState::initDecode() { - ov_callbacks vfs_callbacks; - vfs_callbacks.read_func = vfs_read; - vfs_callbacks.seek_func = vfs_seek; - vfs_callbacks.close_func = vfs_close; - vfs_callbacks.tell_func = vfs_tell; + ov_callbacks cache_callbacks; + cache_callbacks.read_func = cache_read; + cache_callbacks.seek_func = cache_seek; + cache_callbacks.close_func = cache_close; + cache_callbacks.tell_func = cache_tell; LL_DEBUGS("AudioEngine") << "Initing decode from vfile: " << mUUID << LL_ENDL; - mInFilep = new LLVFile(gVFS, mUUID, LLAssetType::AT_SOUND); + mInFilep = new LLFileSystem(mUUID, LLAssetType::AT_SOUND); if (!mInFilep || !mInFilep->getSize()) { LL_WARNS("AudioEngine") << "unable to open vorbis source vfile for reading" << LL_ENDL; @@ -210,7 +207,7 @@ BOOL LLVorbisDecodeState::initDecode() return FALSE; } - S32 r = ov_open_callbacks(mInFilep, &mVF, NULL, 0, vfs_callbacks); + S32 r = ov_open_callbacks(mInFilep, &mVF, NULL, 0, cache_callbacks); if(r < 0) { LL_WARNS("AudioEngine") << r << " Input to vorbis decode does not appear to be an Ogg bitstream: " << mUUID << LL_ENDL; @@ -370,7 +367,7 @@ BOOL LLVorbisDecodeState::decodeSection() { if (!mInFilep) { - LL_WARNS("AudioEngine") << "No VFS file to decode in vorbis!" << LL_ENDL; + LL_WARNS("AudioEngine") << "No cache file to decode in vorbis!" << LL_ENDL; return TRUE; } if (mDone) @@ -420,9 +417,7 @@ BOOL LLVorbisDecodeState::finishDecode() return TRUE; // We've finished } -#if !defined(USE_WAV_VFILE) if (mFileHandle == LLLFSThread::nullHandle()) -#endif { ov_clear(&mVF); @@ -495,11 +490,9 @@ BOOL LLVorbisDecodeState::finishDecode() mValid = FALSE; return TRUE; // we've finished } -#if !defined(USE_WAV_VFILE) mBytesRead = -1; mFileHandle = LLLFSThread::sLocal->write(mOutFilename, &mWAVBuffer[0], 0, mWAVBuffer.size(), new WriteResponder(this)); -#endif } if (mFileHandle != LLLFSThread::nullHandle()) @@ -521,11 +514,6 @@ BOOL LLVorbisDecodeState::finishDecode() mDone = TRUE; -#if defined(USE_WAV_VFILE) - // write the data. - LLVFile output(gVFS, mUUID, LLAssetType::AT_SOUND_WAV); - output.write(&mWAVBuffer[0], mWAVBuffer.size()); -#endif LL_DEBUGS("AudioEngine") << "Finished decode for " << getUUID() << LL_ENDL; return TRUE; @@ -535,7 +523,7 @@ void LLVorbisDecodeState::flushBadFile() { if (mInFilep) { - LL_WARNS("AudioEngine") << "Flushing bad vorbis file from VFS for " << mUUID << LL_ENDL; + LL_WARNS("AudioEngine") << "Flushing bad vorbis file from cache for " << mUUID << LL_ENDL; mInFilep->remove(); } } diff --git a/indra/llaudio/llaudiodecodemgr.h b/indra/llaudio/llaudiodecodemgr.h index 8228e20e8c..ceaff3f2d8 100644 --- a/indra/llaudio/llaudiodecodemgr.h +++ b/indra/llaudio/llaudiodecodemgr.h @@ -33,7 +33,6 @@ #include "llassettype.h" #include "llframetimer.h" -class LLVFS; class LLVorbisDecodeState; class LLAudioDecodeMgr diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 1d447f32ae..d35f249973 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -35,7 +35,7 @@ #include "sound_ids.h" // temporary hack for min/max distances -#include "llvfs.h" +#include "llfilesystem.h" #include "lldir.h" #include "llaudiodecodemgr.h" #include "llassetstorage.h" @@ -684,13 +684,9 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid) return true; } - // At some point we need to have the audio/asset system check the static VFS - // before it goes off and fetches stuff from the server. - //LL_WARNS() << "Used internal preload for non-local sound" << LL_ENDL; return false; } - bool LLAudioEngine::isWindEnabled() { return mEnableWind; @@ -1018,13 +1014,12 @@ bool LLAudioEngine::hasDecodedFile(const LLUUID &uuid) bool LLAudioEngine::hasLocalFile(const LLUUID &uuid) { - // See if it's in the VFS. - bool have_local = gVFS->getExists(uuid, LLAssetType::AT_SOUND); - LL_DEBUGS("AudioEngine") << "sound uuid "<getNumJointMotions(); jm++) { if (!mJointStates[jm]->getJoint()) diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index cde38c8091..fe9de30f0a 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -39,14 +39,13 @@ #include "llendianswizzle.h" #include "llkeyframemotion.h" #include "llquantize.h" -#include "llvfile.h" #include "m3math.h" #include "message.h" +#include "llfilesystem.h" //----------------------------------------------------------------------------- // Static Definitions //----------------------------------------------------------------------------- -LLVFS* LLKeyframeMotion::sVFS = NULL; LLKeyframeDataCache::keyframe_data_map_t LLKeyframeDataCache::sKeyframeDataMap; //----------------------------------------------------------------------------- @@ -515,7 +514,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact return STATUS_SUCCESS; default: // we don't know what state the asset is in yet, so keep going - // check keyframe cache first then static vfs then asset request + // check keyframe cache first then file cache then asset request break; } @@ -559,13 +558,8 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact U8 *anim_data; S32 anim_file_size; - if (!sVFS) - { - LL_ERRS() << "Must call LLKeyframeMotion::setVFS() first before loading a keyframe file!" << LL_ENDL; - } - BOOL success = FALSE; - LLVFile* anim_file = new LLVFile(sVFS, mID, LLAssetType::AT_ANIMATION); + LLFileSystem* anim_file = new LLFileSystem(mID, LLAssetType::AT_ANIMATION); if (!anim_file || !anim_file->getSize()) { delete anim_file; @@ -2296,10 +2290,9 @@ void LLKeyframeMotion::setLoopOut(F32 out_point) //----------------------------------------------------------------------------- // onLoadComplete() //----------------------------------------------------------------------------- -void LLKeyframeMotion::onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void LLKeyframeMotion::onLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LLUUID* id = (LLUUID*)user_data; @@ -2331,7 +2324,7 @@ void LLKeyframeMotion::onLoadComplete(LLVFS *vfs, // asset already loaded return; } - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 size = file.getSize(); U8* buffer = new U8[size]; diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h index 15c5c7c6c0..d640556090 100644 --- a/indra/llcharacter/llkeyframemotion.h +++ b/indra/llcharacter/llkeyframemotion.h @@ -44,7 +44,6 @@ #include "llbvhconsts.h" class LLKeyframeDataCache; -class LLVFS; class LLDataPacker; #define MIN_REQUIRED_PIXEL_AREA_KEYFRAME (40.f) @@ -141,10 +140,7 @@ public: virtual void setStopTime(F32 time); - static void setVFS(LLVFS* vfs) { sVFS = vfs; } - - static void onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, + static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); @@ -416,13 +412,7 @@ public: U32 getNumJointMotions() const { return mJointMotionArray.size(); } }; - protected: - static LLVFS* sVFS; - - //------------------------------------------------------------------------- - // Member Data - //------------------------------------------------------------------------- JointMotionList* mJointMotionList; std::vector > mJointStates; LLJoint* mPelvisp; diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index f876b8ee4a..6e8b9efaf7 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -194,23 +194,64 @@ namespace { { return LLError::getEnabledLogTypesMask() & 0x04; } - + + LL_FORCE_INLINE std::string createBoldANSI() + { + std::string ansi_code; + ansi_code += '\033'; + ansi_code += "["; + ansi_code += "1"; + ansi_code += "m"; + + return ansi_code; + } + + LL_FORCE_INLINE std::string createResetANSI() + { + std::string ansi_code; + ansi_code += '\033'; + ansi_code += "["; + ansi_code += "0"; + ansi_code += "m"; + + return ansi_code; + } + LL_FORCE_INLINE std::string createANSI(const std::string& color) { std::string ansi_code; - ansi_code += '\033'; - ansi_code += "["; - ansi_code += color; + ansi_code += '\033'; + ansi_code += "["; + ansi_code += "38;5;"; + ansi_code += color; ansi_code += "m"; + return ansi_code; } virtual void recordMessage(LLError::ELevel level, const std::string& message) override { - static std::string s_ansi_error = createANSI("31"); // red - static std::string s_ansi_warn = createANSI("34"); // blue - static std::string s_ansi_debug = createANSI("35"); // magenta + // The default colors for error, warn and debug are now a bit more pastel + // and easier to read on the default (black) terminal background but you + // now have the option to set the color of each via an environment variables: + // LL_ANSI_ERROR_COLOR_CODE (default is red) + // LL_ANSI_WARN_COLOR_CODE (default is blue) + // LL_ANSI_DEBUG_COLOR_CODE (default is magenta) + // The list of color codes can be found in many places but I used this page: + // https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html#256-colors + // (Note: you may need to restart Visual Studio to pick environment changes) + char* val = nullptr; + std::string s_ansi_error_code = "160"; + if ((val = getenv("LL_ANSI_ERROR_COLOR_CODE")) != nullptr) s_ansi_error_code = std::string(val); + std::string s_ansi_warn_code = "33"; + if ((val = getenv("LL_ANSI_WARN_COLOR_CODE")) != nullptr) s_ansi_warn_code = std::string(val); + std::string s_ansi_debug_code = "177"; + if ((val = getenv("LL_ANSI_DEBUG_COLOR_CODE")) != nullptr) s_ansi_debug_code = std::string(val); + + static std::string s_ansi_error = createANSI(s_ansi_error_code); // default is red + static std::string s_ansi_warn = createANSI(s_ansi_warn_code); // default is blue + static std::string s_ansi_debug = createANSI(s_ansi_debug_code); // default is magenta if (mUseANSI) { @@ -229,11 +270,11 @@ namespace { LL_FORCE_INLINE void writeANSI(const std::string& ansi_code, const std::string& message) { - static std::string s_ansi_bold = createANSI("1"); // bold - static std::string s_ansi_reset = createANSI("0"); // reset + static std::string s_ansi_bold = createBoldANSI(); // bold text + static std::string s_ansi_reset = createResetANSI(); // reset // ANSI color code escape sequence, message, and reset in one fprintf call // Default all message levels to bold so we can distinguish our own messages from those dumped by subprocesses and libraries. - fprintf(stderr, "%s%s%s\n%s", s_ansi_bold.c_str(), ansi_code.c_str(), message.c_str(), s_ansi_reset.c_str() ); + fprintf(stderr, "%s%s\n%s", ansi_code.c_str(), message.c_str(), s_ansi_reset.c_str() ); } static bool checkANSI(void) diff --git a/indra/llcrashlogger/CMakeLists.txt b/indra/llcrashlogger/CMakeLists.txt index da23b46b7b..d70a1e0fb0 100644 --- a/indra/llcrashlogger/CMakeLists.txt +++ b/indra/llcrashlogger/CMakeLists.txt @@ -7,7 +7,7 @@ include(LLCoreHttp) include(LLCommon) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLFileSystem) include(LLXML) include_directories( @@ -15,7 +15,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) include_directories(SYSTEM diff --git a/indra/llcrashlogger/llcrashlock.h b/indra/llcrashlogger/llcrashlock.h index cde183272f..60b060b736 100644 --- a/indra/llcrashlogger/llcrashlock.h +++ b/indra/llcrashlogger/llcrashlock.h @@ -1,5 +1,5 @@ /** - * @file llpidlock.h + * @file llcrashlock.h * @brief Maintainence of disk locking files for crash reporting * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ diff --git a/indra/llvfs/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt similarity index 51% rename from indra/llvfs/CMakeLists.txt rename to indra/llfilesystem/CMakeLists.txt index 67dce8c073..09c4c33ebf 100644 --- a/indra/llvfs/CMakeLists.txt +++ b/indra/llfilesystem/CMakeLists.txt @@ -1,6 +1,6 @@ # -*- cmake -*- -project(llvfs) +project(llfilesystem) include(00-Common) include(LLCommon) @@ -11,39 +11,34 @@ include_directories( ${LLCOMMON_SYSTEM_INCLUDE_DIRS} ) -set(llvfs_SOURCE_FILES +set(llfilesystem_SOURCE_FILES lldir.cpp lldiriterator.cpp lllfsthread.cpp - llpidlock.cpp - llvfile.cpp - llvfs.cpp - llvfsthread.cpp + lldiskcache.cpp + llfilesystem.cpp ) -set(llvfs_HEADER_FILES +set(llfilesystem_HEADER_FILES CMakeLists.txt - lldir.h lldirguard.h lldiriterator.h lllfsthread.h - llpidlock.h - llvfile.h - llvfs.h - llvfsthread.h + lldiskcache.h + llfilesystem.h ) if (DARWIN) - LIST(APPEND llvfs_SOURCE_FILES lldir_mac.cpp) - LIST(APPEND llvfs_HEADER_FILES lldir_mac.h) - LIST(APPEND llvfs_SOURCE_FILES llvfs_objc.mm) - LIST(APPEND llvfs_HEADER_FILES llvfs_objc.h) + LIST(APPEND llfilesystem_SOURCE_FILES lldir_utils_objc.mm) + LIST(APPEND llfilesystem_SOURCE_FILES lldir_utils_objc.h) + LIST(APPEND llfilesystem_SOURCE_FILES lldir_mac.cpp) + LIST(APPEND llfilesystem_HEADER_FILES lldir_mac.h) endif (DARWIN) if (LINUX) - LIST(APPEND llvfs_SOURCE_FILES lldir_linux.cpp) - LIST(APPEND llvfs_HEADER_FILES lldir_linux.h) + LIST(APPEND llfilesystem_SOURCE_FILES lldir_linux.cpp) + LIST(APPEND llfilesystem_HEADER_FILES lldir_linux.h) if (INSTALL) set_source_files_properties(lldir_linux.cpp @@ -54,31 +49,31 @@ if (LINUX) endif (LINUX) if (WINDOWS) - LIST(APPEND llvfs_SOURCE_FILES lldir_win32.cpp) - LIST(APPEND llvfs_HEADER_FILES lldir_win32.h) + LIST(APPEND llfilesystem_SOURCE_FILES lldir_win32.cpp) + LIST(APPEND llfilesystem_HEADER_FILES lldir_win32.h) endif (WINDOWS) -set_source_files_properties(${llvfs_HEADER_FILES} +set_source_files_properties(${llfilesystem_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) -list(APPEND llvfs_SOURCE_FILES ${llvfs_HEADER_FILES}) +list(APPEND llfilesystem_SOURCE_FILES ${llfilesystem_HEADER_FILES}) -add_library (llvfs ${llvfs_SOURCE_FILES}) +add_library (llfilesystem ${llfilesystem_SOURCE_FILES}) -set(vfs_BOOST_LIBRARIES +set(cache_BOOST_LIBRARIES ${BOOST_FILESYSTEM_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) -target_link_libraries(llvfs +target_link_libraries(llfilesystem ${LLCOMMON_LIBRARIES} - ${vfs_BOOST_LIBRARIES} + ${cache_BOOST_LIBRARIES} ) if (DARWIN) include(CMakeFindFrameworks) find_library(COCOA_LIBRARY Cocoa) - target_link_libraries(llvfs ${COCOA_LIBRARY}) + target_link_libraries(llfilesystem ${COCOA_LIBRARY}) endif (DARWIN) @@ -86,18 +81,18 @@ endif (DARWIN) if (LL_TESTS) include(LLAddBuildTest) # UNIT TESTS - SET(llvfs_TEST_SOURCE_FILES + SET(llfilesystem_TEST_SOURCE_FILES lldiriterator.cpp ) set_source_files_properties(lldiriterator.cpp PROPERTIES - LL_TEST_ADDITIONAL_LIBRARIES "${vfs_BOOST_LIBRARIES}" + LL_TEST_ADDITIONAL_LIBRARIES "${cache_BOOST_LIBRARIES}" ) - LL_ADD_PROJECT_UNIT_TESTS(llvfs "${llvfs_TEST_SOURCE_FILES}") + LL_ADD_PROJECT_UNIT_TESTS(llfilesystem "${llfilesystem_TEST_SOURCE_FILES}") # INTEGRATION TESTS - set(test_libs llmath llcommon llvfs ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) + set(test_libs llmath llcommon llfilesystem ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) # TODO: Some of these need refactoring to be proper Unit tests rather than Integration tests. LL_ADD_INTEGRATION_TEST(lldir "" "${test_libs}") diff --git a/indra/llvfs/lldir.cpp b/indra/llfilesystem/lldir.cpp similarity index 100% rename from indra/llvfs/lldir.cpp rename to indra/llfilesystem/lldir.cpp diff --git a/indra/llvfs/lldir.h b/indra/llfilesystem/lldir.h similarity index 100% rename from indra/llvfs/lldir.h rename to indra/llfilesystem/lldir.h diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llfilesystem/lldir_linux.cpp similarity index 100% rename from indra/llvfs/lldir_linux.cpp rename to indra/llfilesystem/lldir_linux.cpp diff --git a/indra/llvfs/lldir_linux.h b/indra/llfilesystem/lldir_linux.h similarity index 100% rename from indra/llvfs/lldir_linux.h rename to indra/llfilesystem/lldir_linux.h diff --git a/indra/llvfs/lldir_mac.cpp b/indra/llfilesystem/lldir_mac.cpp similarity index 99% rename from indra/llvfs/lldir_mac.cpp rename to indra/llfilesystem/lldir_mac.cpp index 87dc1b9795..3bc4ee844e 100644 --- a/indra/llvfs/lldir_mac.cpp +++ b/indra/llfilesystem/lldir_mac.cpp @@ -36,7 +36,7 @@ #include #include #include -#include "llvfs_objc.h" +#include "lldir_utils_objc.h" // -------------------------------------------------------------------------------- diff --git a/indra/llvfs/lldir_mac.h b/indra/llfilesystem/lldir_mac.h similarity index 100% rename from indra/llvfs/lldir_mac.h rename to indra/llfilesystem/lldir_mac.h diff --git a/indra/llvfs/lldir_solaris.cpp b/indra/llfilesystem/lldir_solaris.cpp similarity index 100% rename from indra/llvfs/lldir_solaris.cpp rename to indra/llfilesystem/lldir_solaris.cpp diff --git a/indra/llvfs/lldir_solaris.h b/indra/llfilesystem/lldir_solaris.h similarity index 100% rename from indra/llvfs/lldir_solaris.h rename to indra/llfilesystem/lldir_solaris.h diff --git a/indra/llvfs/llvfs_objc.h b/indra/llfilesystem/lldir_utils_objc.h similarity index 85% rename from indra/llvfs/llvfs_objc.h rename to indra/llfilesystem/lldir_utils_objc.h index 56cdbebfc5..12019c4284 100644 --- a/indra/llvfs/llvfs_objc.h +++ b/indra/llfilesystem/lldir_utils_objc.h @@ -1,10 +1,10 @@ /** - * @file llvfs_objc.h + * @file lldir_utils_objc.h * @brief Definition of directory utilities class for Mac OS X * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * $LicenseInfo:firstyear=2020&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2020, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,8 +28,8 @@ #error This header must not be included when compiling for any target other than Mac OS. Consider including lldir.h instead. #endif // !LL_DARWIN -#ifndef LL_LLVFS_OBJC_H -#define LL_LLVFS_OBJC_H +#ifndef LL_LLDIR_UTILS_OBJC_H +#define LL_LLDIR_UTILS_OBJC_H #include @@ -40,4 +40,4 @@ std::string* getSystemResourceFolder(); std::string* getSystemExecutableFolder(); -#endif // LL_LLVFS_OBJC_H +#endif // LL_LLDIR_UTILS_OBJC_H diff --git a/indra/llvfs/llvfs_objc.mm b/indra/llfilesystem/lldir_utils_objc.mm similarity index 95% rename from indra/llvfs/llvfs_objc.mm rename to indra/llfilesystem/lldir_utils_objc.mm index 282ea41339..da55a2f897 100644 --- a/indra/llvfs/llvfs_objc.mm +++ b/indra/llfilesystem/lldir_utils_objc.mm @@ -1,10 +1,10 @@ /** - * @file llvfs_objc.cpp + * @file lldir_utils_objc.mm * @brief Cocoa implementation of directory utilities for Mac OS X * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * $LicenseInfo:firstyear=2020&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2020, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,7 +27,7 @@ //WARNING: This file CANNOT use standard linden includes due to conflicts between definitions of BOOL -#include "llvfs_objc.h" +#include "lldir_utils_objc.h" #import std::string* getSystemTempFolder() diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llfilesystem/lldir_win32.cpp similarity index 100% rename from indra/llvfs/lldir_win32.cpp rename to indra/llfilesystem/lldir_win32.cpp diff --git a/indra/llvfs/lldir_win32.h b/indra/llfilesystem/lldir_win32.h similarity index 100% rename from indra/llvfs/lldir_win32.h rename to indra/llfilesystem/lldir_win32.h diff --git a/indra/llvfs/lldirguard.h b/indra/llfilesystem/lldirguard.h similarity index 100% rename from indra/llvfs/lldirguard.h rename to indra/llfilesystem/lldirguard.h diff --git a/indra/llvfs/lldiriterator.cpp b/indra/llfilesystem/lldiriterator.cpp similarity index 100% rename from indra/llvfs/lldiriterator.cpp rename to indra/llfilesystem/lldiriterator.cpp diff --git a/indra/llvfs/lldiriterator.h b/indra/llfilesystem/lldiriterator.h similarity index 100% rename from indra/llvfs/lldiriterator.h rename to indra/llfilesystem/lldiriterator.h diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp new file mode 100644 index 0000000000..c9f7684b5a --- /dev/null +++ b/indra/llfilesystem/lldiskcache.cpp @@ -0,0 +1,327 @@ +/** + * @file lldiskcache.cpp + * @brief The disk cache implementation. + * + * Note: Rather than keep the top level function comments up + * to date in both the source and header files, I elected to + * only have explicit comments about each function and variable + * in the header - look there for details. The same is true for + * description of how this code is supposed to work. + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2020, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llassettype.h" +#include "lldir.h" +#include +#include +#include + +#include "lldiskcache.h" + +LLDiskCache::LLDiskCache(const std::string cache_dir, + const int max_size_bytes, + const bool enable_cache_debug_info) : + mCacheDir(cache_dir), + mMaxSizeBytes(max_size_bytes), + mEnableCacheDebugInfo(enable_cache_debug_info) +{ + mCacheFilenamePrefix = "sl_cache"; + + LLFile::mkdir(cache_dir); +} + +void LLDiskCache::purge() +{ + if (mEnableCacheDebugInfo) + { + LL_INFOS() << "Total dir size before purge is " << dirFileSize(mCacheDir) << LL_ENDL; + } + + auto start_time = std::chrono::high_resolution_clock::now(); + + typedef std::pair> file_info_t; + std::vector file_info; + +#if LL_WINDOWS + std::wstring cache_path(utf8str_to_utf16str(mCacheDir)); +#else + std::string cache_path(mCacheDir); +#endif + if (boost::filesystem::is_directory(cache_path)) + { + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(cache_path), {})) + { + if (boost::filesystem::is_regular_file(entry)) + { + if (entry.path().string().find(mCacheFilenamePrefix) != std::string::npos) + { + uintmax_t file_size = boost::filesystem::file_size(entry); + const std::string file_path = entry.path().string(); + const std::time_t file_time = boost::filesystem::last_write_time(entry); + + file_info.push_back(file_info_t(file_time, { file_size, file_path })); + } + } + } + } + + std::sort(file_info.begin(), file_info.end(), [](file_info_t& x, file_info_t& y) + { + return x.first > y.first; + }); + + LL_INFOS() << "Purging cache to a maximum of " << mMaxSizeBytes << " bytes" << LL_ENDL; + + uintmax_t file_size_total = 0; + for (file_info_t& entry : file_info) + { + file_size_total += entry.second.first; + + std::string action = ""; + if (file_size_total > mMaxSizeBytes) + { + action = "DELETE:"; + boost::filesystem::remove(entry.second.second); + } + else + { + action = " KEEP:"; + } + + if (mEnableCacheDebugInfo) + { + // have to do this because of LL_INFO/LL_END weirdness + std::ostringstream line; + + line << action << " "; + line << entry.first << " "; + line << entry.second.first << " "; + line << entry.second.second; + line << " (" << file_size_total << "/" << mMaxSizeBytes << ")"; + LL_INFOS() << line.str() << LL_ENDL; + } + } + + if (mEnableCacheDebugInfo) + { + auto end_time = std::chrono::high_resolution_clock::now(); + auto execute_time = std::chrono::duration_cast(end_time - start_time).count(); + LL_INFOS() << "Total dir size after purge is " << dirFileSize(mCacheDir) << LL_ENDL; + LL_INFOS() << "Cache purge took " << execute_time << " ms to execute for " << file_info.size() << " files" << LL_ENDL; + } +} + +const std::string LLDiskCache::assetTypeToString(LLAssetType::EType at) +{ + /** + * Make use of the handy C++17 feature that allows + * for inline initialization of an std::map<> + */ + typedef std::map asset_type_to_name_t; + asset_type_to_name_t asset_type_to_name = + { + { LLAssetType::AT_TEXTURE, "TEXTURE" }, + { LLAssetType::AT_SOUND, "SOUND" }, + { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" }, + { LLAssetType::AT_LANDMARK, "LANDMARK" }, + { LLAssetType::AT_SCRIPT, "SCRIPT" }, + { LLAssetType::AT_CLOTHING, "CLOTHING" }, + { LLAssetType::AT_OBJECT, "OBJECT" }, + { LLAssetType::AT_NOTECARD, "NOTECARD" }, + { LLAssetType::AT_CATEGORY, "CATEGORY" }, + { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" }, + { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" }, + { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" }, + { LLAssetType::AT_BODYPART, "BODYPART" }, + { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" }, + { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" }, + { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" }, + { LLAssetType::AT_ANIMATION, "ANIMATION" }, + { LLAssetType::AT_GESTURE, "GESTURE" }, + { LLAssetType::AT_SIMSTATE, "SIMSTATE" }, + { LLAssetType::AT_LINK, "LINK" }, + { LLAssetType::AT_LINK_FOLDER, "LINK_FOLDER" }, + { LLAssetType::AT_MARKETPLACE_FOLDER, "MARKETPLACE_FOLDER" }, + { LLAssetType::AT_WIDGET, "WIDGET" }, + { LLAssetType::AT_PERSON, "PERSON" }, + { LLAssetType::AT_MESH, "MESH" }, + { LLAssetType::AT_SETTINGS, "SETTINGS" }, + { LLAssetType::AT_UNKNOWN, "UNKNOWN" } + }; + + asset_type_to_name_t::iterator iter = asset_type_to_name.find(at); + if (iter != asset_type_to_name.end()) + { + return iter->second; + } + + return std::string("UNKNOWN"); +} + +const std::string LLDiskCache::metaDataToFilepath(const std::string id, + LLAssetType::EType at, + const std::string extra_info) +{ + std::ostringstream file_path; + + file_path << mCacheDir; + file_path << gDirUtilp->getDirDelimiter(); + file_path << mCacheFilenamePrefix; + file_path << "_"; + file_path << id; + file_path << "_"; + file_path << (extra_info.empty() ? "0" : extra_info); + //file_path << "_"; + //file_path << assetTypeToString(at); // see SL-14210 Prune descriptive tag from new cache filenames + // for details of why it was removed. Note that if you put it + // back or change the format of the filename, the cache files + // files will be invalidated (and perhaps, more importantly, + // never deleted unless you delete them manually). + file_path << ".asset"; + + return file_path.str(); +} + +void LLDiskCache::updateFileAccessTime(const std::string file_path) +{ + /** + * Threshold in time_t units that is used to decide if the last access time + * time of the file is updated or not. Added as a precaution for the concern + * outlined in SL-14582 about frequent writes on older SSDs reducing their + * lifespan. I think this is the right place for the threshold value - rather + * than it being a pref - do comment on that Jira if you disagree... + * + * Let's start with 1 hour in time_t units and see how that unfolds + */ + const std::time_t time_threshold = 1 * 60 * 60; + + // current time + const std::time_t cur_time = std::time(nullptr); + +#if LL_WINDOWS + // file last write time + const std::time_t last_write_time = boost::filesystem::last_write_time(utf8str_to_utf16str(file_path)); + + // delta between cur time and last time the file was written + const std::time_t delta_time = cur_time - last_write_time; + + // we only write the new value if the time in time_threshold has elapsed + // before the last one + if (delta_time > time_threshold) + { + boost::filesystem::last_write_time(utf8str_to_utf16str(file_path), cur_time); + } +#else + // file last write time + const std::time_t last_write_time = boost::filesystem::last_write_time(file_path); + + // delta between cur time and last time the file was written + const std::time_t delta_time = cur_time - last_write_time; + + // we only write the new value if the time in time_threshold has elapsed + // before the last one + if (delta_time > time_threshold) + { + boost::filesystem::last_write_time(file_path, cur_time); + } +#endif +} + +const std::string LLDiskCache::getCacheInfo() +{ + std::ostringstream cache_info; + + F32 max_in_mb = (F32)mMaxSizeBytes / (1024.0 * 1024.0); + F32 percent_used = ((F32)dirFileSize(mCacheDir) / (F32)mMaxSizeBytes) * 100.0; + + cache_info << std::fixed; + cache_info << std::setprecision(1); + cache_info << "Max size " << max_in_mb << " MB "; + cache_info << "(" << percent_used << "% used)"; + + return cache_info.str(); +} + +void LLDiskCache::clearCache() +{ + /** + * See notes on performance in dirFileSize(..) - there may be + * a quicker way to do this by operating on the parent dir vs + * the component files but it's called infrequently so it's + * likely just fine + */ +#if LL_WINDOWS + std::wstring cache_path(utf8str_to_utf16str(mCacheDir)); +#else + std::string cache_path(mCacheDir); +#endif + if (boost::filesystem::is_directory(cache_path)) + { + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(cache_path), {})) + { + if (boost::filesystem::is_regular_file(entry)) + { + if (entry.path().string().find(mCacheFilenamePrefix) != std::string::npos) + { + boost::filesystem::remove(entry); + } + } + } + } +} + +uintmax_t LLDiskCache::dirFileSize(const std::string dir) +{ + uintmax_t total_file_size = 0; + + /** + * There may be a better way that works directly on the folder (similar to + * right clicking on a folder in the OS and asking for size vs right clicking + * on all files and adding up manually) but this is very fast - less than 100ms + * for 10,000 files in my testing so, so long as it's not called frequently, + * it should be okay. Note that's it's only currently used for logging/debugging + * so if performance is ever an issue, optimizing this or removing it altogether, + * is an easy win. + */ +#if LL_WINDOWS + std::wstring dir_path(utf8str_to_utf16str(dir)); +#else + std::string dir_path(dir); +#endif + if (boost::filesystem::is_directory(dir_path)) + { + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(dir_path), {})) + { + if (boost::filesystem::is_regular_file(entry)) + { + if (entry.path().string().find(mCacheFilenamePrefix) != std::string::npos) + { + total_file_size += boost::filesystem::file_size(entry); + } + } + } + } + + return total_file_size; +} diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h new file mode 100644 index 0000000000..997884da31 --- /dev/null +++ b/indra/llfilesystem/lldiskcache.h @@ -0,0 +1,183 @@ +/** + * @file lldiskcache.h + * @brief The disk cache implementation declarations. + * + * @Description: + * This code implements a disk cache using the following ideas: + * 1/ The metadata for a file can be encapsulated in the filename. + The filenames will be composed of the following fields: + Prefix: Used to identify the file as a part of the cache. + An additional reason for using a prefix is that it + might be possible, either accidentally or maliciously + to end up with the cache dir set to a non-cache + location such as your OS system dir or a work folder. + Purging files from that would obviously be a disaster + so this is an extra step to help avoid that scenario. + ID: Typically the asset ID (UUID) of the asset being + saved but can be anything valid for a filename + Extra Info: A field for use in the future that can be used + to store extra identifiers - e.g. the discard + level of a JPEG2000 file + Asset Type: A text string created from the LLAssetType enum + that identifies the type of asset being stored. + .asset A file extension of .asset is used to help + identify this as a Viewer asset file + * 2/ The time of last access for a file can be updated instantly + * for file reads and automatically as part of the file writes. + * 3/ The purge algorithm collects a list of all files in the + * directory, sorts them by date of last access (write) and then + * deletes any files based on age until the total size of all + * the files is less than the maximum size specified. + * 4/ An LLSingleton idiom is used since there will only ever be + * a single cache and we want to access it from numerous places. + * 5/ Performance on my modest system seems very acceptable. For + * example, in testing, I was able to purge a directory of + * 10,000 files, deleting about half of them in ~ 1700ms. For + * the same sized directory of files, writing the last updated + * time to each took less than 600ms indicating that this + * important part of the mechanism has almost no overhead. + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2020, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLDISKCACHE +#define _LLDISKCACHE + +#include "llsingleton.h" + +class LLDiskCache : + public LLParamSingleton +{ + public: + /** + * Since this is using the LLSingleton pattern but we + * want to allow the constructor to be called first + * with various parameters, we also invoke the + * LLParamSingleton idiom and use it to initialize + * the class via a call in LLAppViewer. + */ + LLSINGLETON(LLDiskCache, + /** + * The full name of the cache folder - typically a + * a child of the main Viewer cache directory. Defined + * by the setting at 'DiskCacheDirName' + */ + const std::string cache_dir, + /** + * The maximum size of the cache in bytes - Based on the + * setting at 'CacheSize' and 'DiskCachePercentOfTotal' + */ + const int max_size_bytes, + /** + * A flag that enables extra cache debugging so that + * if there are bugs, we can ask uses to enable this + * setting and send us their logs + */ + const bool enable_cache_debug_info); + + virtual ~LLDiskCache() = default; + + public: + /** + * Construct a filename and path to it based on the file meta data + * (id, asset type, additional 'extra' info like discard level perhaps) + * Worth pointing out that this function used to be in LLFileSystem but + * so many things had to be pushed back there to accomodate it, that I + * decided to move it here. Still not sure that's completely right. + */ + const std::string metaDataToFilepath(const std::string id, + LLAssetType::EType at, + const std::string extra_info); + + /** + * Update the "last write time" of a file to "now". This must be called whenever a + * file in the cache is read (not written) so that the last time the file was + * accessed is up to date (This is used in the mechanism for purging the cache) + */ + void updateFileAccessTime(const std::string file_path); + + /** + * Purge the oldest items in the cache so that the combined size of all files + * is no bigger than mMaxSizeBytes. + */ + void purge(); + + /** + * Clear the cache by removing all the files in the specified cache + * directory individually. Only the files that contain a prefix defined + * by mCacheFilenamePrefix will be removed. + */ + void clearCache(); + + /** + * Return some information about the cache for use in About Box etc. + */ + const std::string getCacheInfo(); + + private: + /** + * Utility function to gather the total size the files in a given + * directory. Primarily used here to determine the directory size + * before and after the cache purge + */ + uintmax_t dirFileSize(const std::string dir); + + /** + * Utility function to convert an LLAssetType enum into a + * string that we use as part of the cache file filename + */ + const std::string assetTypeToString(LLAssetType::EType at); + + private: + /** + * The maximum size of the cache in bytes. After purge is called, the + * total size of the cache files in the cache directory will be + * less than this value + */ + uintmax_t mMaxSizeBytes; + + /** + * The folder that holds the cached files. The consumer of this + * class must avoid letting the user set this location as a malicious + * setting could potentially point it at a non-cache directory (for example, + * the Windows System dir) with disastrous results. + */ + std::string mCacheDir; + + /** + * The prefix inserted at the start of a cache file filename to + * help identify it as a cache file. It's probably not required + * (just the presence in the cache folder is enough) but I am + * paranoid about the cache folder being set to something bad + * like the users' OS system dir by mistake or maliciously and + * this will help to offset any damage if that happens. + */ + std::string mCacheFilenamePrefix; + + /** + * When enabled, displays additional debugging information in + * various parts of the code + */ + bool mEnableCacheDebugInfo; +}; + +#endif // _LLDISKCACHE diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp new file mode 100644 index 0000000000..053b52014e --- /dev/null +++ b/indra/llfilesystem/llfilesystem.cpp @@ -0,0 +1,283 @@ +/** + * @file filesystem.h + * @brief Simulate local file system operations. + * @Note The initial implementation does actually use standard C++ + * file operations but eventually, there will be another + * layer that caches and manages file meta data too. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "lldir.h" +#include "llfilesystem.h" +#include "llfasttimer.h" +#include "lldiskcache.h" + +const S32 LLFileSystem::READ = 0x00000001; +const S32 LLFileSystem::WRITE = 0x00000002; +const S32 LLFileSystem::READ_WRITE = 0x00000003; // LLFileSystem::READ & LLFileSystem::WRITE +const S32 LLFileSystem::APPEND = 0x00000006; // 0x00000004 & LLFileSystem::WRITE + +static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait"); + +LLFileSystem::LLFileSystem(const LLUUID& file_id, const LLAssetType::EType file_type, S32 mode) +{ + mFileType = file_type; + mFileID = file_id; + mPosition = 0; + mBytesRead = 0; + mMode = mode; +} + +LLFileSystem::~LLFileSystem() +{ +} + +// static +bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType file_type) +{ + std::string id_str; + file_id.toString(id_str); + const std::string extra_info = ""; + const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info); + + llifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(0, std::ios::end); + return file.tellg() > 0; + } + return false; +} + +// static +bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error /*= 0*/) +{ + std::string id_str; + file_id.toString(id_str); + const std::string extra_info = ""; + const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info); + + LLFile::remove(filename.c_str(), suppress_error); + + return true; +} + +// static +bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type, + const LLUUID& new_file_id, const LLAssetType::EType new_file_type) +{ + std::string old_id_str; + old_file_id.toString(old_id_str); + const std::string extra_info = ""; + const std::string old_filename = LLDiskCache::getInstance()->metaDataToFilepath(old_id_str, old_file_type, extra_info); + + std::string new_id_str; + new_file_id.toString(new_id_str); + const std::string new_filename = LLDiskCache::getInstance()->metaDataToFilepath(new_id_str, new_file_type, extra_info); + + // Rename needs the new file to not exist. + LLFileSystem::removeFile(new_file_id, new_file_type, ENOENT); + + if (LLFile::rename(old_filename, new_filename) != 0) + { + // We would like to return FALSE here indicating the operation + // failed but the original code does not and doing so seems to + // break a lot of things so we go with the flow... + //return FALSE; + LL_WARNS() << "Failed to rename " << old_file_id << " to " << new_id_str << " reason: " << strerror(errno) << LL_ENDL; + } + + return TRUE; +} + +// static +S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) +{ + std::string id_str; + file_id.toString(id_str); + const std::string extra_info = ""; + const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info); + + S32 file_size = 0; + llifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(0, std::ios::end); + file_size = file.tellg(); + } + + return file_size; +} + +BOOL LLFileSystem::read(U8* buffer, S32 bytes) +{ + BOOL success = TRUE; + + std::string id; + mFileID.toString(id); + const std::string extra_info = ""; + const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id, mFileType, extra_info); + + llifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(mPosition, std::ios::beg); + + file.read((char*)buffer, bytes); + + if (file) + { + mBytesRead = bytes; + } + else + { + mBytesRead = file.gcount(); + } + + file.close(); + + mPosition += mBytesRead; + if (!mBytesRead) + { + success = FALSE; + } + } + + // update the last access time for the file - this is required + // even though we are reading and not writing because this is the + // way the cache works - it relies on a valid "last accessed time" for + // each file so it knows how to remove the oldest, unused files + LLDiskCache::getInstance()->updateFileAccessTime(filename); + + return success; +} + +S32 LLFileSystem::getLastBytesRead() +{ + return mBytesRead; +} + +BOOL LLFileSystem::eof() +{ + return mPosition >= getSize(); +} + +BOOL LLFileSystem::write(const U8* buffer, S32 bytes) +{ + std::string id_str; + mFileID.toString(id_str); + const std::string extra_info = ""; + const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, mFileType, extra_info); + + BOOL success = FALSE; + + if (mMode == APPEND) + { + llofstream ofs(filename, std::ios::app | std::ios::binary); + if (ofs) + { + ofs.write((const char*)buffer, bytes); + + success = TRUE; + } + } + else + { + llofstream ofs(filename, std::ios::binary); + if (ofs) + { + ofs.write((const char*)buffer, bytes); + + mPosition += bytes; + + success = TRUE; + } + } + + return success; +} + +BOOL LLFileSystem::seek(S32 offset, S32 origin) +{ + if (-1 == origin) + { + origin = mPosition; + } + + S32 new_pos = origin + offset; + + S32 size = getSize(); + + if (new_pos > size) + { + LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL; + + mPosition = size; + return FALSE; + } + else if (new_pos < 0) + { + LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL; + + mPosition = 0; + return FALSE; + } + + mPosition = new_pos; + return TRUE; +} + +S32 LLFileSystem::tell() const +{ + return mPosition; +} + +S32 LLFileSystem::getSize() +{ + return LLFileSystem::getFileSize(mFileID, mFileType); +} + +S32 LLFileSystem::getMaxSize() +{ + // offer up a huge size since we don't care what the max is + return INT_MAX; +} + +BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_type) +{ + LLFileSystem::renameFile(mFileID, mFileType, new_id, new_type); + + mFileID = new_id; + mFileType = new_type; + + return TRUE; +} + +BOOL LLFileSystem::remove() +{ + LLFileSystem::removeFile(mFileID, mFileType); + + return TRUE; +} diff --git a/indra/llfilesystem/llfilesystem.h b/indra/llfilesystem/llfilesystem.h new file mode 100644 index 0000000000..d934a408c2 --- /dev/null +++ b/indra/llfilesystem/llfilesystem.h @@ -0,0 +1,78 @@ +/** + * @file filesystem.h + * @brief Simulate local file system operations. + * @Note The initial implementation does actually use standard C++ + * file operations but eventually, there will be another + * layer that caches and manages file meta data too. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_FILESYSTEM_H +#define LL_FILESYSTEM_H + +#include "lluuid.h" +#include "llassettype.h" +#include "lldiskcache.h" + +class LLFileSystem +{ + public: + LLFileSystem(const LLUUID& file_id, const LLAssetType::EType file_type, S32 mode = LLFileSystem::READ); + ~LLFileSystem(); + + BOOL read(U8* buffer, S32 bytes); + S32 getLastBytesRead(); + BOOL eof(); + + BOOL write(const U8* buffer, S32 bytes); + BOOL seek(S32 offset, S32 origin = -1); + S32 tell() const; + + S32 getSize(); + S32 getMaxSize(); + BOOL rename(const LLUUID& new_id, const LLAssetType::EType new_type); + BOOL remove(); + + static bool getExists(const LLUUID& file_id, const LLAssetType::EType file_type); + static bool removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error = 0); + static bool renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type, + const LLUUID& new_file_id, const LLAssetType::EType new_file_type); + static S32 getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type); + + public: + static const S32 READ; + static const S32 WRITE; + static const S32 READ_WRITE; + static const S32 APPEND; + + protected: + LLAssetType::EType mFileType; + LLUUID mFileID; + S32 mPosition; + S32 mMode; + S32 mBytesRead; +//private: +// static const std::string idToFilepath(const std::string id, LLAssetType::EType at); +}; + +#endif // LL_FILESYSTEM_H diff --git a/indra/llvfs/lllfsthread.cpp b/indra/llfilesystem/lllfsthread.cpp similarity index 100% rename from indra/llvfs/lllfsthread.cpp rename to indra/llfilesystem/lllfsthread.cpp diff --git a/indra/llvfs/lllfsthread.h b/indra/llfilesystem/lllfsthread.h similarity index 100% rename from indra/llvfs/lllfsthread.h rename to indra/llfilesystem/lllfsthread.h diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp similarity index 100% rename from indra/llvfs/tests/lldir_test.cpp rename to indra/llfilesystem/tests/lldir_test.cpp diff --git a/indra/llvfs/tests/lldiriterator_test.cpp b/indra/llfilesystem/tests/lldiriterator_test.cpp similarity index 100% rename from indra/llvfs/tests/lldiriterator_test.cpp rename to indra/llfilesystem/tests/lldiriterator_test.cpp diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt index 293ada7548..dc8e9f7c2f 100644 --- a/indra/llimage/CMakeLists.txt +++ b/indra/llimage/CMakeLists.txt @@ -6,7 +6,7 @@ include(00-Common) include(LLCommon) include(LLImage) include(LLMath) -include(LLVFS) +include(LLFileSystem) include(LLKDU) include(LLImageJ2COJ) include(ZLIB) @@ -17,7 +17,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCOMMON_SYSTEM_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ) @@ -68,7 +68,7 @@ else (USE_KDU) endif (USE_KDU) target_link_libraries(llimage - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${JPEG_LIBRARIES} diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index aed8943439..350a8eb120 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -2219,20 +2219,11 @@ bool LLImageFormatted::save(const std::string &filename) return true; } -// bool LLImageFormatted::save(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type) -// Depricated to remove VFS dependency. -// Use: -// LLVFile::writeFile(image->getData(), image->getDataSize(), vfs, uuid, type); - -//---------------------------------------------------------------------------- - S8 LLImageFormatted::getCodec() const { return mCodec; } -//============================================================================ - static void avg4_colors4(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst) { dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2); diff --git a/indra/llinventory/CMakeLists.txt b/indra/llinventory/CMakeLists.txt index e829788c91..04975940aa 100644 --- a/indra/llinventory/CMakeLists.txt +++ b/indra/llinventory/CMakeLists.txt @@ -7,7 +7,7 @@ include(LLCommon) include(LLCoreHttp) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLFileSystem) include(LLXML) include_directories( @@ -81,7 +81,7 @@ if (LL_TESTS) LL_ADD_PROJECT_UNIT_TESTS(llinventory "${llinventory_TEST_SOURCE_FILES}") #set(TEST_DEBUG on) - set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) + set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) LL_ADD_INTEGRATION_TEST(inventorymisc "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llparcel "" "${test_libs}") endif (LL_TESTS) diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 2f99ca069e..f0a1dfe940 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -9,7 +9,7 @@ include(LLCommon) include(LLCoreHttp) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLFileSystem) include(LLAddBuildTest) include(Python) include(Tut) @@ -23,7 +23,7 @@ include_directories( ${LLCOREHTTP_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${JSONCPP_INCLUDE_DIR} ) @@ -209,7 +209,7 @@ target_link_libraries( llmessage ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} @@ -227,7 +227,7 @@ target_link_libraries( llmessage ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} @@ -257,7 +257,7 @@ if (LL_TESTS) if (LINUX) set(test_libs ${WINDOWS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${CURL_LIBRARIES} ${NGHTTP2_LIBRARIES} @@ -273,7 +273,7 @@ if (LINUX) else (LINUX) set(test_libs ${WINDOWS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${CURL_LIBRARIES} ${NGHTTP2_LIBRARIES} diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index d7801b6ddc..f38a5e663e 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -42,8 +42,7 @@ // this library includes #include "message.h" #include "llxfermanager.h" -#include "llvfile.h" -#include "llvfs.h" +#include "llfilesystem.h" #include "lldbstrings.h" #include "lltransfersourceasset.h" @@ -202,7 +201,7 @@ LLBaseDownloadRequest::LLBaseDownloadRequest(const LLUUID &uuid, const LLAssetTy mIsTemp(FALSE), mIsPriority(FALSE), mDataSentInFirstPacket(FALSE), - mDataIsInVFS(FALSE) + mDataIsInCache(FALSE) { // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been // running a message system loop. @@ -266,7 +265,8 @@ LLSD LLAssetRequest::getFullDetails() const sd["is_local"] = mIsLocal; sd["is_priority"] = mIsPriority; sd["data_send_in_first_packet"] = mDataSentInFirstPacket; - sd["data_is_in_vfs"] = mDataIsInVFS; + // Note: cannot change this (easily) since it is consumed by server + sd["data_is_in_vfs"] = mDataIsInCache; return sd; } @@ -330,28 +330,23 @@ LLBaseDownloadRequest* LLEstateAssetRequest::getCopy() // TODO: rework tempfile handling? -LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, LLVFS *static_vfs, const LLHost &upstream_host) +LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host) { - _init(msg, xfer, vfs, static_vfs, upstream_host); + _init(msg, xfer, upstream_host); } -LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs) +LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer) { - _init(msg, xfer, vfs, static_vfs, LLHost()); + _init(msg, xfer, LLHost()); } void LLAssetStorage::_init(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, - LLVFS *static_vfs, const LLHost &upstream_host) { mShutDown = FALSE; mMessageSys = msg; mXferManager = xfer; - mVFS = vfs; - mStaticVFS = static_vfs; setUpstream(upstream_host); msg->setHandlerFuncFast(_PREHASH_AssetUploadComplete, processUploadComplete, (void **)this); @@ -430,7 +425,7 @@ void LLAssetStorage::_cleanupRequests(BOOL all, S32 error) } if (tmp->mDownCallback) { - tmp->mDownCallback(mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, error, LLExtStat::NONE); + tmp->mDownCallback(tmp->getUUID(), tmp->getType(), tmp->mUserData, error, LLExtStat::NONE); } if (tmp->mInfoCallback) { @@ -443,10 +438,10 @@ void LLAssetStorage::_cleanupRequests(BOOL all, S32 error) BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType type) { - return mStaticVFS->getExists(uuid, type) || mVFS->getExists(uuid, type); + return LLFileSystem::getExists(uuid, type); } -bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, +bool LLAssetStorage::findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback, void *user_data) { if (user_data) @@ -455,17 +450,17 @@ bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAsse llassert(callback != NULL); } - BOOL exists = mStaticVFS->getExists(uuid, type); + BOOL exists = LLFileSystem::getExists(uuid, type); if (exists) { - LLVFile file(mStaticVFS, uuid, type); + LLFileSystem file(uuid, type); U32 size = file.getSize(); if (size > 0) { // we've already got the file if (callback) { - callback(mStaticVFS, uuid, type, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); + callback(uuid, type, user_data, LL_ERR_NOERR, LLExtStat::CACHE_CACHED); } return true; } @@ -506,7 +501,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_FAILED, LLExtStat::NONE); + callback(uuid, type, user_data, LL_ERR_ASSET_REQUEST_FAILED, LLExtStat::NONE); } return; } @@ -517,20 +512,19 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LLExtStat::NULL_UUID); + callback(uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LLExtStat::NULL_UUID); } return; } - // Try static VFS first. - if (findInStaticVFSAndInvokeCallback(uuid,type,callback,user_data)) + if (findInCacheAndInvokeCallback(uuid,type,callback,user_data)) { - LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in static VFS" << LL_ENDL; + LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in cache" << LL_ENDL; return; } - BOOL exists = mVFS->getExists(uuid, type); - LLVFile file(mVFS, uuid, type); + BOOL exists = LLFileSystem::getExists(uuid, type); + LLFileSystem file(uuid, type); U32 size = exists ? file.getSize() : 0; if (size > 0) @@ -540,10 +534,10 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, // unless there's a weird error if (callback) { - callback(mVFS, uuid, type, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); + callback(uuid, type, user_data, LL_ERR_NOERR, LLExtStat::CACHE_CACHED); } - LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in VFS" << LL_ENDL; + LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in cache" << LL_ENDL; } else { @@ -616,7 +610,7 @@ void LLAssetStorage::removeAndCallbackPendingDownloads(const LLUUID& file_id, LL { add(sFailedDownloadCount, 1); } - tmp->mDownCallback(gAssetStorage->mVFS, callback_id, callback_type, tmp->mUserData, result_code, ext_status); + tmp->mDownCallback(callback_id, callback_type, tmp->mUserData, result_code, ext_status); } delete tmp; } @@ -670,7 +664,7 @@ void LLAssetStorage::downloadCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLVFile vfile(gAssetStorage->mVFS, callback_id, callback_type); + LLFileSystem vfile(callback_id, callback_type); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset " << callback_id << LL_ENDL; @@ -719,19 +713,19 @@ void LLAssetStorage::getEstateAsset( if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LLExtStat::NULL_UUID); + callback(asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LLExtStat::NULL_UUID); } return; } - // Try static VFS first. - if (findInStaticVFSAndInvokeCallback(asset_id,atype,callback,user_data)) + // Try static first. + if (findInCacheAndInvokeCallback(asset_id,atype,callback,user_data)) { return; } - BOOL exists = mVFS->getExists(asset_id, atype); - LLVFile file(mVFS, asset_id, atype); + BOOL exists = LLFileSystem::getExists(asset_id, atype); + LLFileSystem file(asset_id, atype); U32 size = exists ? file.getSize() : 0; if (size > 0) @@ -741,7 +735,7 @@ void LLAssetStorage::getEstateAsset( // unless there's a weird error if (callback) { - callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); + callback(asset_id, atype, user_data, LL_ERR_NOERR, LLExtStat::CACHE_CACHED); } } else @@ -792,7 +786,7 @@ void LLAssetStorage::getEstateAsset( if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LLExtStat::NO_UPSTREAM); + callback(asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LLExtStat::NO_UPSTREAM); } } } @@ -824,7 +818,7 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getAType()); + LLFileSystem vfile(req->getUUID(), req->getAType()); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; @@ -838,7 +832,7 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback( { add(sFailedDownloadCount, 1); } - req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getAType(), req->mUserData, result, ext_status); + req->mDownCallback(req->getUUID(), req->getAType(), req->mUserData, result, ext_status); } void LLAssetStorage::getInvItemAsset( @@ -861,14 +855,13 @@ void LLAssetStorage::getInvItemAsset( if(asset_id.notNull()) { - // Try static VFS first. - if (findInStaticVFSAndInvokeCallback( asset_id, atype, callback, user_data)) + if (findInCacheAndInvokeCallback( asset_id, atype, callback, user_data)) { return; } - exists = mVFS->getExists(asset_id, atype); - LLVFile file(mVFS, asset_id, atype); + exists = LLFileSystem::getExists(asset_id, atype); + LLFileSystem file(asset_id, atype); size = exists ? file.getSize() : 0; if(exists && size < 1) { @@ -885,7 +878,7 @@ void LLAssetStorage::getInvItemAsset( // unless there's a weird error if (callback) { - callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); + callback(asset_id, atype, user_data, LL_ERR_NOERR, LLExtStat::CACHE_CACHED); } } else @@ -936,7 +929,7 @@ void LLAssetStorage::getInvItemAsset( if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LLExtStat::NO_UPSTREAM); + callback(asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LLExtStat::NO_UPSTREAM); } } } @@ -968,7 +961,7 @@ void LLAssetStorage::downloadInvItemCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType()); + LLFileSystem vfile(req->getUUID(), req->getType()); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; @@ -982,7 +975,7 @@ void LLAssetStorage::downloadInvItemCompleteCallback( { add(sFailedDownloadCount, 1); } - req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), req->mUserData, result, ext_status); + req->mDownCallback(req->getUUID(), req->getType(), req->mUserData, result, ext_status); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1293,7 +1286,7 @@ bool LLAssetStorage::deletePendingRequestImpl(LLAssetStorage::request_list_t* re if (req->mDownCallback) { add(sFailedDownloadCount, 1); - req->mDownCallback(mVFS, req->getUUID(), req->getType(), req->mUserData, error, LLExtStat::REQUEST_DROPPED); + req->mDownCallback(req->getUUID(), req->getType(), req->mUserData, error, LLExtStat::REQUEST_DROPPED); } if (req->mInfoCallback) { @@ -1363,8 +1356,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, { LLAssetRequest* tmp = *iter++; - //void(*const* cbptr)(LLVFS *, const LLUUID &, LLAssetType::EType, void *, S32, LLExtStat) - auto cbptr = tmp->mDownCallback.target(); + auto cbptr = tmp->mDownCallback.target(); if (type == tmp->getType() && uuid == tmp->getUUID() && @@ -1389,8 +1381,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, } // static -void LLAssetStorage::legacyGetDataCallback(LLVFS *vfs, - const LLUUID &uuid, +void LLAssetStorage::legacyGetDataCallback(const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 status, @@ -1405,7 +1396,7 @@ void LLAssetStorage::legacyGetDataCallback(LLVFS *vfs, if ( !status && !toxic ) { - LLVFile file(vfs, uuid, type); + LLFileSystem file(uuid, type); std::string uuid_str; diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index c799d8eefc..e0f22f1160 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -44,7 +44,6 @@ class LLMessageSystem; class LLXferManager; class LLAssetStorage; -class LLVFS; class LLSD; // anything that takes longer than this to download will abort. @@ -60,11 +59,11 @@ const int LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4; const int LL_ERR_INSUFFICIENT_PERMISSIONS = -5; const int LL_ERR_PRICE_MISMATCH = -23018; -// *TODO: these typedefs are passed into the VFS via a legacy C function pointer +// *TODO: these typedefs are passed into the cache via a legacy C function pointer // future project would be to convert these to C++ callables (std::function<>) so that // we can use bind and remove the userData parameter. // -typedef std::function LLGetAssetCallback; +typedef std::function LLGetAssetCallback; typedef std::function LLStoreAssetCallback; @@ -120,7 +119,6 @@ protected: public: LLGetAssetCallback mDownCallback; -// void(*mDownCallback)(LLVFS*, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat); void *mUserData; LLHost mHost; @@ -128,7 +126,7 @@ public: F64Seconds mTime; // Message system time BOOL mIsPriority; BOOL mDataSentInFirstPacket; - BOOL mDataIsInVFS; + BOOL mDataIsInCache; }; class LLAssetRequest : public LLBaseDownloadRequest @@ -198,9 +196,6 @@ typedef std::map toxic_asset_map_t; class LLAssetStorage { public: - // VFS member is public because static child methods need it :( - LLVFS *mVFS; - LLVFS *mStaticVFS; typedef ::LLStoreAssetCallback LLStoreAssetCallback; typedef ::LLGetAssetCallback LLGetAssetCallback; @@ -230,11 +225,9 @@ protected: toxic_asset_map_t mToxicAssetMap; // Objects in this list are known to cause problems and are not loaded public: - LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, const LLHost &upstream_host); + LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host); - LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs); + LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer); virtual ~LLAssetStorage(); void setUpstream(const LLHost &upstream_host); @@ -284,7 +277,7 @@ public: void markAssetToxic( const LLUUID& uuid ); protected: - bool findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, + bool findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback, void *user_data); LLSD getPendingDetailsImpl(const request_list_t* requests, @@ -375,7 +368,7 @@ public: bool user_waiting = false, F64Seconds timeout = LL_ASSET_STORAGE_TIMEOUT) = 0; - static void legacyGetDataCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType, void *user_data, S32 status, LLExtStat ext_status); + static void legacyGetDataCallback(const LLUUID &uuid, LLAssetType::EType, void *user_data, S32 status, LLExtStat ext_status); static void legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status, LLExtStat ext_status); // add extra methods to handle metadata @@ -385,15 +378,12 @@ protected: void _callUploadCallbacks(const LLUUID &uuid, const LLAssetType::EType asset_type, BOOL success, LLExtStat ext_status); virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback, -// void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), void *user_data, BOOL duplicate, BOOL is_priority) = 0; private: void _init(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, - LLVFS *static_vfs, const LLHost &upstream_host); protected: @@ -408,7 +398,7 @@ protected: MR_FILE_NONEXIST = 3, // Old format store call - source file does not exist MR_NO_FILENAME = 4, // Old format store call - source filename is NULL/0-length MR_NO_UPSTREAM = 5, // Upstream provider is missing - MR_VFS_CORRUPTION = 6 // VFS is corrupt - too-large or mismatched stated/returned sizes + MR_CACHE_CORRUPTION = 6 // cache is corrupt - too-large or mismatched stated/returned sizes }; static class LLMetrics *metric_recipient; diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index 0eae6d9826..7031f1aa8c 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -37,7 +37,7 @@ #include "llsdserialize.h" #include "reader.h" // JSON #include "writer.h" // JSON -#include "llvfile.h" +#include "llfilesystem.h" #include "message.h" // for getting the port @@ -784,7 +784,7 @@ LLSD HttpCoroutineAdapter::postFileAndSuspend(LLCore::HttpRequest::ptr_t request // scoping for our streams so that they go away when we no longer need them. { LLCore::BufferArrayStream outs(fileData.get()); - LLVFile vfile(gVFS, assetId, assetType, LLVFile::READ); + LLFileSystem vfile(assetId, assetType, LLFileSystem::READ); S32 fileSize = vfile.getSize(); U8* fileBuffer; diff --git a/indra/llmessage/llextendedstatus.h b/indra/llmessage/llextendedstatus.h index 9923d73c1a..2a53dced80 100644 --- a/indra/llmessage/llextendedstatus.h +++ b/indra/llmessage/llextendedstatus.h @@ -1,7 +1,7 @@ /** * @file llextendedstatus.h * @date August 2007 - * @brief extended status codes for curl/vfs/resident asset storage and delivery + * @brief extended status codes for curl/resident asset storage and delivery * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code @@ -32,9 +32,9 @@ enum class LLExtStat: uint32_t { // Status provider groups - Top bits indicate which status type it is // Zero is common status code (next section) - CURL_RESULT = 1UL<<30, // serviced by curl - use 1L if we really implement the below - RES_RESULT = 2UL<<30, // serviced by resident copy - VFS_RESULT = 3UL<<30, // serviced by vfs + CURL_RESULT = 1UL<<30, // serviced by curl - use 1L if we really implement the below + RES_RESULT = 2UL<<30, // serviced by resident copy + CACHE_RESULT = 3UL<<30, // serviced by cache // Common Status Codes @@ -54,9 +54,9 @@ enum class LLExtStat: uint32_t // Memory-Resident status codes: // None at present - // VFS status codes: - VFS_CACHED = VFS_RESULT | 0x0001, - VFS_CORRUPT = VFS_RESULT | 0x0002, + // CACHE status codes: + CACHE_CACHED = CACHE_RESULT | 0x0001, + CACHE_CORRUPT = CACHE_RESULT | 0x0002, }; diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp index 80ed3340c6..027283232d 100644 --- a/indra/llmessage/lltransfersourceasset.cpp +++ b/indra/llmessage/lltransfersourceasset.cpp @@ -32,7 +32,7 @@ #include "message.h" #include "lldatapacker.h" #include "lldir.h" -#include "llvfile.h" +#include "llfilesystem.h" LLTransferSourceAsset::LLTransferSourceAsset(const LLUUID &request_id, const F32 priority) : LLTransferSource(LLTST_ASSET, request_id, priority), @@ -99,7 +99,7 @@ LLTSCode LLTransferSourceAsset::dataCallback(const S32 packet_id, return LLTS_SKIP; } - LLVFile vf(gAssetStorage->mVFS, mParams.getAssetID(), mParams.getAssetType(), LLVFile::READ); + LLFileSystem vf(mParams.getAssetID(), mParams.getAssetType(), LLFileSystem::READ); if (!vf.getSize()) { @@ -171,7 +171,7 @@ BOOL LLTransferSourceAsset::unpackParams(LLDataPacker &dp) } -void LLTransferSourceAsset::responderCallback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, +void LLTransferSourceAsset::responderCallback(const LLUUID& uuid, LLAssetType::EType type, void *user_data, S32 result, LLExtStat ext_status ) { LLUUID *tidp = ((LLUUID*) user_data); @@ -198,7 +198,7 @@ void LLTransferSourceAsset::responderCallback(LLVFS *vfs, const LLUUID& uuid, LL if (LL_ERR_NOERR == result) { // Everything's OK. - LLVFile vf(gAssetStorage->mVFS, uuid, type, LLVFile::READ); + LLFileSystem vf(uuid, type, LLFileSystem::READ); tsap->mSize = vf.getSize(); status = LLTS_OK; } diff --git a/indra/llmessage/lltransfersourceasset.h b/indra/llmessage/lltransfersourceasset.h index 3abda83cf8..585e683cb3 100644 --- a/indra/llmessage/lltransfersourceasset.h +++ b/indra/llmessage/lltransfersourceasset.h @@ -30,7 +30,7 @@ #include "lltransfermanager.h" #include "llassetstorage.h" -class LLVFile; +class LLFileSystem; class LLTransferSourceParamsAsset : public LLTransferSourceParams { @@ -56,7 +56,7 @@ public: LLTransferSourceAsset(const LLUUID &request_id, const F32 priority); virtual ~LLTransferSourceAsset(); - static void responderCallback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, + static void responderCallback(const LLUUID& uuid, LLAssetType::EType type, void *user_data, S32 result, LLExtStat ext_status ); protected: /*virtual*/ void initTransfer(); diff --git a/indra/llmessage/lltransfertargetvfile.cpp b/indra/llmessage/lltransfertargetvfile.cpp index b27f0881e0..f6faadf87f 100644 --- a/indra/llmessage/lltransfertargetvfile.cpp +++ b/indra/llmessage/lltransfertargetvfile.cpp @@ -30,7 +30,7 @@ #include "lldatapacker.h" #include "llerror.h" -#include "llvfile.h" +#include "llfilesystem.h" //static void LLTransferTargetVFile::updateQueue(bool shutdown) @@ -138,10 +138,9 @@ LLTSCode LLTransferTargetVFile::dataCallback(const S32 packet_id, U8 *in_datap, //LL_INFOS() << "LLTransferTargetFile::dataCallback" << LL_ENDL; //LL_INFOS() << "Packet: " << packet_id << LL_ENDL; - LLVFile vf(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::APPEND); + LLFileSystem vf(mTempID, mParams.getAssetType(), LLFileSystem::APPEND); if (mNeedsCreate) { - vf.setMaxSize(mSize); mNeedsCreate = FALSE; } @@ -176,7 +175,7 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status) case LLTS_DONE: if (!mNeedsCreate) { - LLVFile file(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::WRITE); + LLFileSystem file(mTempID, mParams.getAssetType(), LLFileSystem::WRITE); if (!file.rename(mParams.getAssetID(), mParams.getAssetType())) { LL_ERRS() << "LLTransferTargetVFile: rename failed" << LL_ENDL; @@ -195,7 +194,7 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status) { // We're aborting this transfer, we don't want to keep this file. LL_WARNS() << "Aborting vfile transfer for " << mParams.getAssetID() << LL_ENDL; - LLVFile vf(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::APPEND); + LLFileSystem vf(mTempID, mParams.getAssetType(), LLFileSystem::APPEND); vf.remove(); } break; diff --git a/indra/llmessage/lltransfertargetvfile.h b/indra/llmessage/lltransfertargetvfile.h index c819c1e2f2..39a9125f1b 100644 --- a/indra/llmessage/lltransfertargetvfile.h +++ b/indra/llmessage/lltransfertargetvfile.h @@ -29,9 +29,9 @@ #include "lltransfermanager.h" #include "llassetstorage.h" -#include "llvfile.h" +#include "llfilesystem.h" -class LLVFile; +class LLFileSystem; // Lame, an S32 for now until I figure out the deal with how we want to do // error codes. diff --git a/indra/llmessage/llxfer_vfile.cpp b/indra/llmessage/llxfer_vfile.cpp index ddc24342f6..12419b342d 100644 --- a/indra/llmessage/llxfer_vfile.cpp +++ b/indra/llmessage/llxfer_vfile.cpp @@ -30,8 +30,7 @@ #include "lluuid.h" #include "llerror.h" #include "llmath.h" -#include "llvfile.h" -#include "llvfs.h" +#include "llfilesystem.h" #include "lldir.h" // size of chunks read from/written to disk @@ -42,13 +41,13 @@ const U32 LL_MAX_XFER_FILE_BUFFER = 65536; LLXfer_VFile::LLXfer_VFile () : LLXfer(-1) { - init(NULL, LLUUID::null, LLAssetType::AT_NONE); + init(LLUUID::null, LLAssetType::AT_NONE); } -LLXfer_VFile::LLXfer_VFile (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type) +LLXfer_VFile::LLXfer_VFile (const LLUUID &local_id, LLAssetType::EType type) : LLXfer(-1) { - init(vfs, local_id, type); + init(local_id, type); } /////////////////////////////////////////////////////////// @@ -60,10 +59,8 @@ LLXfer_VFile::~LLXfer_VFile () /////////////////////////////////////////////////////////// -void LLXfer_VFile::init (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type) +void LLXfer_VFile::init (const LLUUID &local_id, LLAssetType::EType type) { - - mVFS = vfs; mLocalID = local_id; mType = type; @@ -82,14 +79,14 @@ void LLXfer_VFile::cleanup () if (mTempID.notNull() && mDeleteTempFile) { - if (mVFS->getExists(mTempID, mType)) + if (LLFileSystem::getExists(mTempID, mType)) { - LLVFile file(mVFS, mTempID, mType, LLVFile::WRITE); + LLFileSystem file(mTempID, mType, LLFileSystem::WRITE); file.remove(); } else { - LL_WARNS("Xfer") << "LLXfer_VFile::cleanup() can't open to delete VFS file " << mTempID << "." << LLAssetType::lookup(mType) + LL_WARNS("Xfer") << "LLXfer_VFile::cleanup() can't open to delete cache file " << mTempID << "." << LLAssetType::lookup(mType) << ", mRemoteID is " << mRemoteID << LL_ENDL; } } @@ -103,7 +100,6 @@ void LLXfer_VFile::cleanup () /////////////////////////////////////////////////////////// S32 LLXfer_VFile::initializeRequest(U64 xfer_id, - LLVFS* vfs, const LLUUID& local_id, const LLUUID& remote_id, LLAssetType::EType type, @@ -115,7 +111,6 @@ S32 LLXfer_VFile::initializeRequest(U64 xfer_id, mRemoteHost = remote_host; - mVFS = vfs; mLocalID = local_id; mRemoteID = remote_id; mType = type; @@ -192,13 +187,13 @@ S32 LLXfer_VFile::startSend (U64 xfer_id, const LLHost &remote_host) delete mVFile; mVFile = NULL; - if(mVFS->getExists(mLocalID, mType)) + if(LLFileSystem::getExists(mLocalID, mType)) { - mVFile = new LLVFile(mVFS, mLocalID, mType, LLVFile::READ); + mVFile = new LLFileSystem(mLocalID, mType, LLFileSystem::READ); if (mVFile->getSize() <= 0) { - LL_WARNS("Xfer") << "LLXfer_VFile::startSend() VFS file " << mLocalID << "." << LLAssetType::lookup(mType) + LL_WARNS("Xfer") << "LLXfer_VFile::startSend() cache file " << mLocalID << "." << LLAssetType::lookup(mType) << " has unexpected file size of " << mVFile->getSize() << LL_ENDL; delete mVFile; mVFile = NULL; @@ -214,7 +209,7 @@ S32 LLXfer_VFile::startSend (U64 xfer_id, const LLHost &remote_host) } else { - LL_WARNS("Xfer") << "LLXfer_VFile::startSend() can't read VFS file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; + LL_WARNS("Xfer") << "LLXfer_VFile::startSend() can't read cache file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; retval = LL_ERR_FILE_NOT_FOUND; } @@ -240,13 +235,13 @@ S32 LLXfer_VFile::reopenFileHandle() if (mVFile == NULL) { - if (mVFS->getExists(mLocalID, mType)) + if (LLFileSystem::getExists(mLocalID, mType)) { - mVFile = new LLVFile(mVFS, mLocalID, mType, LLVFile::READ); + mVFile = new LLFileSystem(mLocalID, mType, LLFileSystem::READ); } else { - LL_WARNS("Xfer") << "LLXfer_VFile::reopenFileHandle() can't read VFS file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; + LL_WARNS("Xfer") << "LLXfer_VFile::reopenFileHandle() can't read cache file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; retval = LL_ERR_FILE_NOT_FOUND; } } @@ -265,8 +260,7 @@ void LLXfer_VFile::setXferSize (S32 xfer_size) // It would be nice if LLXFers could tell which end of the pipe they were if (! mVFile) { - LLVFile file(mVFS, mTempID, mType, LLVFile::APPEND); - file.setMaxSize(xfer_size); + LLFileSystem file(mTempID, mType, LLFileSystem::APPEND); } } @@ -320,7 +314,7 @@ S32 LLXfer_VFile::flush() S32 retval = 0; if (mBufferLength) { - LLVFile file(mVFS, mTempID, mType, LLVFile::APPEND); + LLFileSystem file(mTempID, mType, LLFileSystem::APPEND); file.write((U8*)mBuffer, mBufferLength); @@ -340,22 +334,15 @@ S32 LLXfer_VFile::processEOF() if (!mCallbackResult) { - if (mVFS->getExists(mTempID, mType)) + if (LLFileSystem::getExists(mTempID, mType)) { - LLVFile file(mVFS, mTempID, mType, LLVFile::WRITE); + LLFileSystem file(mTempID, mType, LLFileSystem::WRITE); if (!file.rename(mLocalID, mType)) { - LL_WARNS("Xfer") << "VFS rename of temp file failed: unable to rename " << mTempID << " to " << mLocalID << LL_ENDL; + LL_WARNS("Xfer") << "Cache rename of temp file failed: unable to rename " << mTempID << " to " << mLocalID << LL_ENDL; } else - { - #ifdef VFS_SPAM - // Debugging spam - LL_INFOS("Xfer") << "VFS rename of temp file done: renamed " << mTempID << " to " << mLocalID - << " LLVFile size is " << file.getSize() - << LL_ENDL; - #endif - + { // Rename worked: the original file is gone. Clear mDeleteTempFile // so we don't attempt to delete the file in cleanup() mDeleteTempFile = FALSE; @@ -363,7 +350,7 @@ S32 LLXfer_VFile::processEOF() } else { - LL_WARNS("Xfer") << "LLXfer_VFile::processEOF() can't open for renaming VFS file " << mTempID << "." << LLAssetType::lookup(mType) << LL_ENDL; + LL_WARNS("Xfer") << "LLXfer_VFile::processEOF() can't open for renaming cache file " << mTempID << "." << LLAssetType::lookup(mType) << LL_ENDL; } } diff --git a/indra/llmessage/llxfer_vfile.h b/indra/llmessage/llxfer_vfile.h index 5bf9a5cfba..d82bab5f6c 100644 --- a/indra/llmessage/llxfer_vfile.h +++ b/indra/llmessage/llxfer_vfile.h @@ -30,8 +30,7 @@ #include "llxfer.h" #include "llassetstorage.h" -class LLVFS; -class LLVFile; +class LLFileSystem; class LLXfer_VFile : public LLXfer { @@ -41,9 +40,7 @@ class LLXfer_VFile : public LLXfer LLUUID mTempID; LLAssetType::EType mType; - LLVFile *mVFile; - - LLVFS *mVFS; + LLFileSystem *mVFile; std::string mName; @@ -51,14 +48,13 @@ class LLXfer_VFile : public LLXfer public: LLXfer_VFile (); - LLXfer_VFile (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type); + LLXfer_VFile (const LLUUID &local_id, LLAssetType::EType type); virtual ~LLXfer_VFile(); - virtual void init(LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type); + virtual void init(const LLUUID &local_id, LLAssetType::EType type); virtual void cleanup(); virtual S32 initializeRequest(U64 xfer_id, - LLVFS *vfs, const LLUUID &local_id, const LLUUID &remote_id, const LLAssetType::EType type, diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp index 4cea886c8a..f9b59d7e42 100644 --- a/indra/llmessage/llxfermanager.cpp +++ b/indra/llmessage/llxfermanager.cpp @@ -56,9 +56,9 @@ const S32 LL_DEFAULT_MAX_HARD_LIMIT_SIMULTANEOUS_XFERS = 500; /////////////////////////////////////////////////////////// -LLXferManager::LLXferManager (LLVFS *vfs) +LLXferManager::LLXferManager () { - init(vfs); + init(); } /////////////////////////////////////////////////////////// @@ -70,7 +70,7 @@ LLXferManager::~LLXferManager () /////////////////////////////////////////////////////////// -void LLXferManager::init (LLVFS *vfs) +void LLXferManager::init() { cleanup(); @@ -78,8 +78,6 @@ void LLXferManager::init (LLVFS *vfs) setHardLimitOutgoingXfersPerCircuit(LL_DEFAULT_MAX_HARD_LIMIT_SIMULTANEOUS_XFERS); setMaxIncomingXfers(LL_DEFAULT_MAX_REQUEST_FIFO_XFERS); - mVFS = vfs; - // Turn on or off ack throttling mUseAckThrottling = FALSE; setAckThrottleBPS(100000); @@ -462,7 +460,7 @@ U64 LLXferManager::requestFile(const std::string& local_filename, void LLXferManager::requestVFile(const LLUUID& local_id, const LLUUID& remote_id, - LLAssetType::EType type, LLVFS* vfs, + LLAssetType::EType type, const LLHost& remote_host, void (*callback)(void**,S32,LLExtStat), void** user_data, @@ -508,7 +506,6 @@ void LLXferManager::requestVFile(const LLUUID& local_id, addToList(xfer_p, mReceiveList, is_priority); ((LLXfer_VFile *)xfer_p)->initializeRequest(getNextID(), - vfs, local_id, remote_id, type, @@ -784,33 +781,17 @@ void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user LLXfer *xferp; if (uuid != LLUUID::null) - { // Request for an asset - use a VFS file + { // Request for an asset - use a cache file if(NULL == LLAssetType::lookup(type)) { LL_WARNS("Xfer") << "Invalid type for xfer request: " << uuid << ":" << type_s16 << " to " << mesgsys->getSender() << LL_ENDL; return; } - - if (! mVFS) - { - LL_WARNS("Xfer") << "Attempt to send VFile w/o available VFS" << LL_ENDL; - return; - } - - /* Present in fireengine, not used by viewer - if (!validateVFileForTransfer(uuid.asString())) - { - // it is up to the app sending the file to mark it for expected - // transfer before the request arrives or it will be dropped - LL_WARNS("Xfer") << "SECURITY: Unapproved VFile '" << uuid << "'" << LL_ENDL; - return; - } - */ LL_INFOS("Xfer") << "starting vfile transfer: " << uuid << "," << LLAssetType::lookup(type) << " to " << mesgsys->getSender() << LL_ENDL; - xferp = (LLXfer *)new LLXfer_VFile(mVFS, uuid, type); + xferp = (LLXfer *)new LLXfer_VFile(uuid, type); if (xferp) { mSendList.push_front(xferp); @@ -1273,9 +1254,9 @@ void LLXferManager::addToList(LLXfer* xferp, xfer_list_t & xfer_list, BOOL is_pr LLXferManager *gXferManager = NULL; -void start_xfer_manager(LLVFS *vfs) +void start_xfer_manager() { - gXferManager = new LLXferManager(vfs); + gXferManager = new LLXferManager(); } void cleanup_xfer_manager() diff --git a/indra/llmessage/llxfermanager.h b/indra/llmessage/llxfermanager.h index 45ae2ffdd3..f49209bed0 100644 --- a/indra/llmessage/llxfermanager.h +++ b/indra/llmessage/llxfermanager.h @@ -35,7 +35,6 @@ //Forward declaration to avoid circular dependencies class LLXfer; -class LLVFS; #include "llxfer.h" #include "message.h" @@ -72,9 +71,6 @@ public: class LLXferManager { - private: - LLVFS *mVFS; - protected: S32 mMaxOutgoingXfersPerCircuit; S32 mHardLimitOutgoingXfersPerCircuit; // At this limit, kill off the connection @@ -111,10 +107,10 @@ class LLXferManager std::multiset mExpectedVFileRequests; // files that are authorized to be downloaded on top of public: - LLXferManager(LLVFS *vfs); + LLXferManager(); virtual ~LLXferManager(); - virtual void init(LLVFS *vfs); + virtual void init(); virtual void cleanup(); void setUseAckThrottling(const BOOL use); @@ -166,7 +162,7 @@ class LLXferManager // vfile requesting // .. to vfile virtual void requestVFile(const LLUUID &local_id, const LLUUID& remote_id, - LLAssetType::EType type, LLVFS* vfs, + LLAssetType::EType type, const LLHost& remote_host, void (*callback)(void**,S32,LLExtStat), void** user_data, BOOL is_priority = FALSE); @@ -213,7 +209,7 @@ class LLXferManager extern LLXferManager* gXferManager; // initialization and garbage collection -void start_xfer_manager(LLVFS *vfs); +void start_xfer_manager(); void cleanup_xfer_manager(); // message system callbacks diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index 47e7ad915b..baab09a104 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -9,10 +9,9 @@ include(LLCommon) include(LLImage) include(LLMath) include(LLRender) -include(LLVFS) include(LLWindow) include(LLXML) -include(LLVFS) +include(LLFileSystem) include_directories( ${FREETYPE_INCLUDE_DIRS} @@ -20,10 +19,9 @@ include_directories( ${LLIMAGE_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} ) include_directories(SYSTEM ${LLCOMMON_SYSTEM_INCLUDE_DIRS} @@ -104,9 +102,8 @@ if (BUILD_HEADLESS) ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLRENDER_HEADLESS_LIBRARIES} - ${LLVFS_LIBRARIES} ${LLXML_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLWINDOW_HEADLESS_LIBRARIES} ${OPENGL_HEADLESS_LIBRARIES}) @@ -126,9 +123,8 @@ target_link_libraries(llrender ${LLCOMMON_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLXML_LIBRARIES} - ${LLVFS_LIBRARIES} ${LLWINDOW_LIBRARIES} ${FREETYPE_LIBRARIES} ${OPENGL_LIBRARIES}) diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index cce618487b..7401e6130a 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -13,7 +13,7 @@ include(LLCoreHttp) include(LLRender) include(LLWindow) include(LLCoreHttp) -include(LLVFS) +include(LLFileSystem) include(LLXML) include_directories( @@ -25,7 +25,7 @@ include_directories( ${LLMESSAGE_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LIBS_PREBUILD_DIR}/include/hunspell ) @@ -283,7 +283,7 @@ target_link_libraries(llui ${LLINVENTORY_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} - ${LLVFS_LIBRARIES} # ugh, just for LLDir + ${LLFILESYSTEM_LIBRARIES} ${LLXUIXML_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} diff --git a/indra/llui/llviewereventrecorder.h b/indra/llui/llviewereventrecorder.h index d1059d55de..94e66f5dc4 100644 --- a/indra/llui/llviewereventrecorder.h +++ b/indra/llui/llviewereventrecorder.h @@ -32,7 +32,6 @@ #include "lldir.h" #include "llsd.h" #include "llfile.h" -#include "llvfile.h" #include "lldate.h" #include "llsdserialize.h" #include "llkeyboard.h" diff --git a/indra/llvfs/llpidlock.cpp b/indra/llvfs/llpidlock.cpp deleted file mode 100644 index f770e93d45..0000000000 --- a/indra/llvfs/llpidlock.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/** - * @file llformat.cpp - * @date January 2007 - * @brief string formatting utility - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llapr.h" // thread-related functions -#include "llpidlock.h" -#include "lldir.h" -#include "llsd.h" -#include "llsdserialize.h" -#include "llnametable.h" -#include "llframetimer.h" - -#if LL_WINDOWS //For windows platform. - -#include - -bool isProcessAlive(U32 pid) -{ - return (bool) GetProcessVersion((DWORD)pid); -} - -#else //Everyone Else -bool isProcessAlive(U32 pid) -{ - return (bool) kill( (pid_t)pid, 0); -} -#endif //Everyone else. - - - -class LLPidLockFile -{ - public: - LLPidLockFile( ) : - mAutosave(false), - mSaving(false), - mWaiting(false), - mPID(getpid()), - mNameTable(NULL), - mClean(true) - { - mLockName = gDirUtilp->getTempDir() + gDirUtilp->getDirDelimiter() + "savelock"; - } - bool requestLock(LLNameTable *name_table, bool autosave, - bool force_immediate=FALSE, F32 timeout=300.0); - bool checkLock(); - void releaseLock(); - - private: - void writeLockFile(LLSD pids); - public: - static LLPidLockFile& instance(); // return the singleton black list file - - bool mAutosave; - bool mSaving; - bool mWaiting; - LLFrameTimer mTimer; - U32 mPID; - std::string mLockName; - std::string mSaveName; - LLSD mPIDS_sd; - LLNameTable *mNameTable; - bool mClean; -}; - -LLPidLockFile& LLPidLockFile::instance() -{ - static LLPidLockFile the_file; - return the_file; -} - -void LLPidLockFile::writeLockFile(LLSD pids) -{ - llofstream ofile(mLockName.c_str()); - - if (!LLSDSerialize::toXML(pids,ofile)) - { - LL_WARNS() << "Unable to write concurrent save lock file." << LL_ENDL; - } - ofile.close(); -} - -bool LLPidLockFile::requestLock(LLNameTable *name_table, bool autosave, - bool force_immediate, F32 timeout) -{ - bool readyToSave = FALSE; - - if (mSaving) return FALSE; //Bail out if we're currently saving. Will not queue another save. - - if (!mWaiting){ - mNameTable=name_table; - mAutosave = autosave; - } - - LLSD out_pids; - out_pids.append( (LLSD::Integer)mPID ); - - llifstream ifile(mLockName.c_str()); - - if (ifile.is_open()) - { //If file exists, we need to decide whether or not to continue. - if ( force_immediate - || mTimer.hasExpired() ) //Only deserialize if we REALLY need to. - { - - LLSD in_pids; - - LLSDSerialize::fromXML(in_pids, ifile); - - //Clean up any dead PIDS that might be in there. - for (LLSD::array_iterator i=in_pids.beginArray(); - i !=in_pids.endArray(); - ++i) - { - U32 stored_pid=(*i).asInteger(); - - if (isProcessAlive(stored_pid)) - { - out_pids.append( (*i) ); - } - } - - readyToSave=TRUE; - } - ifile.close(); - } - else - { - readyToSave=TRUE; - } - - if (!mWaiting) //Not presently waiting to save. Queue up. - { - mTimer.resetWithExpiry(timeout); - mWaiting=TRUE; - } - - if (readyToSave) - { //Potential race condition won't kill us. Ignore it. - writeLockFile(out_pids); - mSaving=TRUE; - } - - return readyToSave; -} - -bool LLPidLockFile::checkLock() -{ - return mWaiting; -} - -void LLPidLockFile::releaseLock() -{ - llifstream ifile(mLockName.c_str()); - LLSD in_pids; - LLSD out_pids; - bool write_file=FALSE; - - LLSDSerialize::fromXML(in_pids, ifile); - - //Clean up this PID and any dead ones. - for (LLSD::array_iterator i=in_pids.beginArray(); - i !=in_pids.endArray(); - ++i) - { - U32 stored_pid=(*i).asInteger(); - - if (stored_pid != mPID && isProcessAlive(stored_pid)) - { - out_pids.append( (*i) ); - write_file=TRUE; - } - } - ifile.close(); - - if (write_file) - { - writeLockFile(out_pids); - } - else - { - unlink(mLockName.c_str()); - } - - mSaving=FALSE; - mWaiting=FALSE; -} - -//LLPidLock - -void LLPidLock::initClass() { - (void) LLPidLockFile::instance(); -} - -bool LLPidLock::checkLock() -{ - return LLPidLockFile::instance().checkLock(); -} - -bool LLPidLock::requestLock(LLNameTable *name_table, bool autosave, - bool force_immediate, F32 timeout) -{ - return LLPidLockFile::instance().requestLock(name_table,autosave,force_immediate,timeout); -} - -void LLPidLock::releaseLock() -{ - return LLPidLockFile::instance().releaseLock(); -} - -bool LLPidLock::isClean() -{ - return LLPidLockFile::instance().mClean; -} - -//getters -LLNameTable * LLPidLock::getNameTable() -{ - return LLPidLockFile::instance().mNameTable; -} - -bool LLPidLock::getAutosave() -{ - return LLPidLockFile::instance().mAutosave; -} - -bool LLPidLock::getClean() -{ - return LLPidLockFile::instance().mClean; -} - -std::string LLPidLock::getSaveName() -{ - return LLPidLockFile::instance().mSaveName; -} - -//setters -void LLPidLock::setClean(bool clean) -{ - LLPidLockFile::instance().mClean=clean; -} - -void LLPidLock::setSaveName(std::string savename) -{ - LLPidLockFile::instance().mSaveName=savename; -} - -S32 LLPidLock::getPID() -{ - return (S32)getpid(); -} diff --git a/indra/llvfs/llpidlock.h b/indra/llvfs/llpidlock.h deleted file mode 100644 index 334f26bb29..0000000000 --- a/indra/llvfs/llpidlock.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file llpidlock.h - * @brief System information debugging classes. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_PIDLOCK_H -#define LL_PIDLOCK_H -#include "llnametable.h" - -class LLSD; -class LLFrameTimer; - -#if !LL_WINDOWS //For non-windows platforms. -#include -#endif - -namespace LLPidLock -{ - void initClass(); // { (void) LLPidLockFile::instance(); } - - bool requestLock( LLNameTable *name_table=NULL, bool autosave=TRUE, - bool force_immediate=FALSE, F32 timeout=300.0); - bool checkLock(); - void releaseLock(); - bool isClean(); - - //getters - LLNameTable * getNameTable(); - bool getAutosave(); - bool getClean(); - std::string getSaveName(); - S32 getPID(); - - //setters - void setClean(bool clean); - void setSaveName(std::string savename); -}; - -#endif // LL_PIDLOCK_H diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp deleted file mode 100644 index b8588e99f4..0000000000 --- a/indra/llvfs/llvfile.cpp +++ /dev/null @@ -1,437 +0,0 @@ -/** - * @file llvfile.cpp - * @brief Implementation of virtual file - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llvfile.h" - -#include "llerror.h" -#include "llthread.h" -#include "lltimer.h" -#include "llfasttimer.h" -#include "llmemory.h" -#include "llvfs.h" - -const S32 LLVFile::READ = 0x00000001; -const S32 LLVFile::WRITE = 0x00000002; -const S32 LLVFile::READ_WRITE = 0x00000003; // LLVFile::READ & LLVFile::WRITE -const S32 LLVFile::APPEND = 0x00000006; // 0x00000004 & LLVFile::WRITE - -static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait"); - -//---------------------------------------------------------------------------- -LLVFSThread* LLVFile::sVFSThread = NULL; -BOOL LLVFile::sAllocdVFSThread = FALSE; -//---------------------------------------------------------------------------- - -//============================================================================ - -LLVFile::LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode) -{ - mFileType = file_type; - - mFileID = file_id; - mPosition = 0; - mMode = mode; - mVFS = vfs; - - mBytesRead = 0; - mHandle = LLVFSThread::nullHandle(); - mPriority = 128.f; - - mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN); -} - -LLVFile::~LLVFile() -{ - if (!isReadComplete()) - { - if (mHandle != LLVFSThread::nullHandle()) - { - if (!(mMode & LLVFile::WRITE)) - { - //LL_WARNS() << "Destroying LLVFile with pending async read/write, aborting..." << LL_ENDL; - sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT); - } - else // WRITE - { - sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE); - } - } - } - mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN); -} - -BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) -{ - if (! (mMode & READ)) - { - LL_WARNS() << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - return FALSE; - } - - if (mHandle != LLVFSThread::nullHandle()) - { - LL_WARNS() << "Attempt to read from vfile object " << mFileID << " with pending async operation" << LL_ENDL; - return FALSE; - } - mPriority = priority; - - BOOL success = TRUE; - - // We can't do a read while there are pending async writes - waitForLock(VFSLOCK_APPEND); - - // *FIX: (?) - if (async) - { - mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri()); - } - else - { - // We can't do a read while there are pending async writes on this file - mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes); - mPosition += mBytesRead; - if (! mBytesRead) - { - success = FALSE; - } - } - - return success; -} - -//static -U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read) -{ - U8 *data; - LLVFile file(vfs, uuid, type, LLVFile::READ); - S32 file_size = file.getSize(); - if (file_size == 0) - { - // File is empty. - data = NULL; - } - else - { - data = (U8*) ll_aligned_malloc<16>(file_size); - file.read(data, file_size); /* Flawfinder: ignore */ - - if (file.getLastBytesRead() != (S32)file_size) - { - ll_aligned_free<16>(data); - data = NULL; - file_size = 0; - } - } - if (bytes_read) - { - *bytes_read = file_size; - } - return data; -} - -void LLVFile::setReadPriority(const F32 priority) -{ - mPriority = priority; - if (mHandle != LLVFSThread::nullHandle()) - { - sVFSThread->setPriority(mHandle, threadPri()); - } -} - -BOOL LLVFile::isReadComplete() -{ - BOOL res = TRUE; - if (mHandle != LLVFSThread::nullHandle()) - { - LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle); - LLVFSThread::status_t status = req->getStatus(); - if (status == LLVFSThread::STATUS_COMPLETE) - { - mBytesRead = req->getBytesRead(); - mPosition += mBytesRead; - sVFSThread->completeRequest(mHandle); - mHandle = LLVFSThread::nullHandle(); - } - else - { - res = FALSE; - } - } - return res; -} - -S32 LLVFile::getLastBytesRead() -{ - return mBytesRead; -} - -BOOL LLVFile::eof() -{ - return mPosition >= getSize(); -} - -BOOL LLVFile::write(const U8 *buffer, S32 bytes) -{ - if (! (mMode & WRITE)) - { - LL_WARNS() << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - } - if (mHandle != LLVFSThread::nullHandle()) - { - LL_ERRS() << "Attempt to write to vfile object " << mFileID << " with pending async operation" << LL_ENDL; - return FALSE; - } - BOOL success = TRUE; - - // *FIX: allow async writes? potential problem wit mPosition... - if (mMode == APPEND) // all appends are async (but WRITEs are not) - { - U8* writebuf = new U8[bytes]; - memcpy(writebuf, buffer, bytes); - S32 offset = -1; - mHandle = sVFSThread->write(mVFS, mFileID, mFileType, - writebuf, offset, bytes, - LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_AUTO_DELETE); - mHandle = LLVFSThread::nullHandle(); // FLAG_AUTO_COMPLETE means we don't track this - } - else - { - // We can't do a write while there are pending reads or writes on this file - waitForLock(VFSLOCK_READ); - waitForLock(VFSLOCK_APPEND); - - S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition; - - S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes); - - mPosition += wrote; - - if (wrote < bytes) - { - LL_WARNS() << "Tried to write " << bytes << " bytes, actually wrote " << wrote << LL_ENDL; - - success = FALSE; - } - } - return success; -} - -//static -BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type) -{ - LLVFile file(vfs, uuid, type, LLVFile::WRITE); - file.setMaxSize(bytes); - return file.write(buffer, bytes); -} - -BOOL LLVFile::seek(S32 offset, S32 origin) -{ - if (mMode == APPEND) - { - LL_WARNS() << "Attempt to seek on append-only file" << LL_ENDL; - return FALSE; - } - - if (-1 == origin) - { - origin = mPosition; - } - - S32 new_pos = origin + offset; - - S32 size = getSize(); // Calls waitForLock(VFSLOCK_APPEND) - - if (new_pos > size) - { - LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL; - - mPosition = size; - return FALSE; - } - else if (new_pos < 0) - { - LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL; - - mPosition = 0; - return FALSE; - } - - mPosition = new_pos; - return TRUE; -} - -S32 LLVFile::tell() const -{ - return mPosition; -} - -S32 LLVFile::getSize() -{ - waitForLock(VFSLOCK_APPEND); - S32 size = mVFS->getSize(mFileID, mFileType); - - return size; -} - -S32 LLVFile::getMaxSize() -{ - S32 size = mVFS->getMaxSize(mFileID, mFileType); - - return size; -} - -BOOL LLVFile::setMaxSize(S32 size) -{ - if (! (mMode & WRITE)) - { - LL_WARNS() << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - - return FALSE; - } - - if (!mVFS->checkAvailable(size)) - { - //LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT); - S32 count = 0; - while (sVFSThread->getPending() > 1000) - { - if (count % 100 == 0) - { - LL_INFOS() << "VFS catching up... Pending: " << sVFSThread->getPending() << LL_ENDL; - } - if (sVFSThread->isPaused()) - { - sVFSThread->update(0); - } - ms_sleep(10); - } - } - return mVFS->setMaxSize(mFileID, mFileType, size); -} - -BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type) -{ - if (! (mMode & WRITE)) - { - LL_WARNS() << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - - return FALSE; - } - - if (mHandle != LLVFSThread::nullHandle()) - { - LL_WARNS() << "Renaming file with pending async read" << LL_ENDL; - } - - waitForLock(VFSLOCK_READ); - waitForLock(VFSLOCK_APPEND); - - // we need to release / replace our own lock - // since the renamed file will inherit locks from the new name - mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN); - mVFS->renameFile(mFileID, mFileType, new_id, new_type); - mVFS->incLock(new_id, new_type, VFSLOCK_OPEN); - - mFileID = new_id; - mFileType = new_type; - - return TRUE; -} - -BOOL LLVFile::remove() -{ -// LL_INFOS() << "Removing file " << mFileID << LL_ENDL; - - if (! (mMode & WRITE)) - { - // Leaving paranoia warning just because this should be a very infrequent - // operation. - LL_WARNS() << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - } - - if (mHandle != LLVFSThread::nullHandle()) - { - LL_WARNS() << "Removing file with pending async read" << LL_ENDL; - } - - // why not seek back to the beginning of the file too? - mPosition = 0; - - waitForLock(VFSLOCK_READ); - waitForLock(VFSLOCK_APPEND); - mVFS->removeFile(mFileID, mFileType); - - return TRUE; -} - -// static -void LLVFile::initClass(LLVFSThread* vfsthread) -{ - if (!vfsthread) - { - if (LLVFSThread::sLocal != NULL) - { - vfsthread = LLVFSThread::sLocal; - } - else - { - vfsthread = new LLVFSThread(); - sAllocdVFSThread = TRUE; - } - } - sVFSThread = vfsthread; -} - -// static -void LLVFile::cleanupClass() -{ - if (sAllocdVFSThread) - { - delete sVFSThread; - } - sVFSThread = NULL; -} - -bool LLVFile::isLocked(EVFSLock lock) -{ - return mVFS->isLocked(mFileID, mFileType, lock) ? true : false; -} - -void LLVFile::waitForLock(EVFSLock lock) -{ - //LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT); - // spin until the lock clears - while (isLocked(lock)) - { - if (sVFSThread->isPaused()) - { - sVFSThread->update(0); - } - ms_sleep(1); - } -} diff --git a/indra/llvfs/llvfile.h b/indra/llvfs/llvfile.h deleted file mode 100644 index 7e9d9f73e5..0000000000 --- a/indra/llvfs/llvfile.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @file llvfile.h - * @brief Definition of virtual file - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLVFILE_H -#define LL_LLVFILE_H - -#include "lluuid.h" -#include "llassettype.h" -#include "llvfs.h" -#include "llvfsthread.h" - -class LLVFile -{ -public: - LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLVFile::READ); - ~LLVFile(); - - BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f); /* Flawfinder: ignore */ - static U8* readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read = 0); - void setReadPriority(const F32 priority); - BOOL isReadComplete(); - S32 getLastBytesRead(); - BOOL eof(); - - BOOL write(const U8 *buffer, S32 bytes); - static BOOL writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type); - BOOL seek(S32 offset, S32 origin = -1); - S32 tell() const; - - S32 getSize(); - S32 getMaxSize(); - BOOL setMaxSize(S32 size); - BOOL rename(const LLUUID &new_id, const LLAssetType::EType new_type); - BOOL remove(); - - bool isLocked(EVFSLock lock); - void waitForLock(EVFSLock lock); - - static void initClass(LLVFSThread* vfsthread = NULL); - static void cleanupClass(); - static LLVFSThread* getVFSThread() { return sVFSThread; } - -protected: - static LLVFSThread* sVFSThread; - static BOOL sAllocdVFSThread; - U32 threadPri() { return LLVFSThread::PRIORITY_NORMAL + llmin((U32)mPriority,(U32)0xfff); } - -public: - static const S32 READ; - static const S32 WRITE; - static const S32 READ_WRITE; - static const S32 APPEND; - -protected: - LLAssetType::EType mFileType; - - LLUUID mFileID; - S32 mPosition; - S32 mMode; - LLVFS *mVFS; - F32 mPriority; - - S32 mBytesRead; - LLVFSThread::handle_t mHandle; -}; - -#endif diff --git a/indra/llvfs/llvfs.cpp b/indra/llvfs/llvfs.cpp deleted file mode 100644 index 617056d94d..0000000000 --- a/indra/llvfs/llvfs.cpp +++ /dev/null @@ -1,2220 +0,0 @@ -/** - * @file llvfs.cpp - * @brief Implementation of virtual file system - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llvfs.h" - -#include -#include -#include -#if LL_WINDOWS -#include -#elif LL_SOLARIS -#include -#include -#include -#else -#include -#endif - -#include "llstl.h" -#include "lltimer.h" - -const S32 FILE_BLOCK_MASK = 0x000003FF; // 1024-byte blocks -const S32 VFS_CLEANUP_SIZE = 5242880; // how much space we free up in a single stroke -const S32 BLOCK_LENGTH_INVALID = -1; // mLength for invalid LLVFSFileBlocks - -LLVFS *gVFS = NULL; - -// internal class definitions -class LLVFSBlock -{ -public: - LLVFSBlock() - { - mLocation = 0; - mLength = 0; - } - - LLVFSBlock(U32 loc, S32 size) - { - mLocation = loc; - mLength = size; - } - - static bool locationSortPredicate( - const LLVFSBlock* lhs, - const LLVFSBlock* rhs) - { - return lhs->mLocation < rhs->mLocation; - } - -public: - U32 mLocation; - S32 mLength; // allocated block size -}; - -LLVFSFileSpecifier::LLVFSFileSpecifier() -: mFileID(), - mFileType( LLAssetType::AT_NONE ) -{ -} - -LLVFSFileSpecifier::LLVFSFileSpecifier(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - mFileID = file_id; - mFileType = file_type; -} - -bool LLVFSFileSpecifier::operator<(const LLVFSFileSpecifier &rhs) const -{ - return (mFileID == rhs.mFileID) - ? mFileType < rhs.mFileType - : mFileID < rhs.mFileID; -} - -bool LLVFSFileSpecifier::operator==(const LLVFSFileSpecifier &rhs) const -{ - return (mFileID == rhs.mFileID && - mFileType == rhs.mFileType); -} - - -class LLVFSFileBlock : public LLVFSBlock, public LLVFSFileSpecifier -{ -public: - LLVFSFileBlock() : LLVFSBlock(), LLVFSFileSpecifier() - { - init(); - } - - LLVFSFileBlock(const LLUUID &file_id, LLAssetType::EType file_type, U32 loc = 0, S32 size = 0) - : LLVFSBlock(loc, size), LLVFSFileSpecifier( file_id, file_type ) - { - init(); - } - - void init() - { - mSize = 0; - mIndexLocation = -1; - mAccessTime = (U32)time(NULL); - - for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++) - { - mLocks[(EVFSLock)i] = 0; - } - } - - #ifdef LL_LITTLE_ENDIAN - inline void swizzleCopy(void *dst, void *src, int size) { memcpy(dst, src, size); /* Flawfinder: ignore */} - - #else - - inline U32 swizzle32(U32 x) - { - return(((x >> 24) & 0x000000FF) | ((x >> 8) & 0x0000FF00) | ((x << 8) & 0x00FF0000) |((x << 24) & 0xFF000000)); - } - - inline U16 swizzle16(U16 x) - { - return( ((x >> 8) & 0x000000FF) | ((x << 8) & 0x0000FF00) ); - } - - inline void swizzleCopy(void *dst, void *src, int size) - { - if(size == 4) - { - ((U32*)dst)[0] = swizzle32(((U32*)src)[0]); - } - else if(size == 2) - { - ((U16*)dst)[0] = swizzle16(((U16*)src)[0]); - } - else - { - // Perhaps this should assert... - memcpy(dst, src, size); /* Flawfinder: ignore */ - } - } - - #endif - - void serialize(U8 *buffer) - { - swizzleCopy(buffer, &mLocation, 4); - buffer += 4; - swizzleCopy(buffer, &mLength, 4); - buffer +=4; - swizzleCopy(buffer, &mAccessTime, 4); - buffer +=4; - memcpy(buffer, &mFileID.mData, 16); /* Flawfinder: ignore */ - buffer += 16; - S16 temp_type = mFileType; - swizzleCopy(buffer, &temp_type, 2); - buffer += 2; - swizzleCopy(buffer, &mSize, 4); - } - - void deserialize(U8 *buffer, const S32 index_loc) - { - mIndexLocation = index_loc; - - swizzleCopy(&mLocation, buffer, 4); - buffer += 4; - swizzleCopy(&mLength, buffer, 4); - buffer += 4; - swizzleCopy(&mAccessTime, buffer, 4); - buffer += 4; - memcpy(&mFileID.mData, buffer, 16); - buffer += 16; - S16 temp_type; - swizzleCopy(&temp_type, buffer, 2); - mFileType = (LLAssetType::EType)temp_type; - buffer += 2; - swizzleCopy(&mSize, buffer, 4); - } - - static BOOL insertLRU(LLVFSFileBlock* const& first, - LLVFSFileBlock* const& second) - { - return (first->mAccessTime == second->mAccessTime) - ? *first < *second - : first->mAccessTime < second->mAccessTime; - } - -public: - S32 mSize; - S32 mIndexLocation; // location of index entry - U32 mAccessTime; - BOOL mLocks[VFSLOCK_COUNT]; // number of outstanding locks of each type - - static const S32 SERIAL_SIZE; -}; - -// Helper structure for doing lru w/ stl... is there a simpler way? -struct LLVFSFileBlock_less -{ - bool operator()(LLVFSFileBlock* const& lhs, LLVFSFileBlock* const& rhs) const - { - return (LLVFSFileBlock::insertLRU(lhs, rhs)) ? true : false; - } -}; - - -const S32 LLVFSFileBlock::SERIAL_SIZE = 34; - - -LLVFS::LLVFS(const std::string& index_filename, const std::string& data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash) -: mRemoveAfterCrash(remove_after_crash), - mDataFP(NULL), - mIndexFP(NULL) -{ - mDataMutex = new LLMutex(); - - S32 i; - for (i = 0; i < VFSLOCK_COUNT; i++) - { - mLockCounts[i] = 0; - } - mValid = VFSVALID_OK; - mReadOnly = read_only; - mIndexFilename = index_filename; - mDataFilename = data_filename; - - const char *file_mode = mReadOnly ? "rb" : "r+b"; - - LL_INFOS("VFS") << "Attempting to open VFS index file " << mIndexFilename << LL_ENDL; - LL_INFOS("VFS") << "Attempting to open VFS data file " << mDataFilename << LL_ENDL; - - mDataFP = openAndLock(mDataFilename, file_mode, mReadOnly); - if (!mDataFP) - { - if (mReadOnly) - { - LL_WARNS("VFS") << "Can't find " << mDataFilename << " to open read-only VFS" << LL_ENDL; - mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY; - return; - } - - mDataFP = openAndLock(mDataFilename, "w+b", FALSE); - if (mDataFP) - { - // Since we're creating this data file, assume any index file is bogus - // remove the index, since this vfs is now blank - LLFile::remove(mIndexFilename); - } - else - { - LL_WARNS("VFS") << "Couldn't open vfs data file " - << mDataFilename << LL_ENDL; - mValid = VFSVALID_BAD_CANNOT_CREATE; - return; - } - - if (presize) - { - presizeDataFile(presize); - } - } - - // Did we leave this file open for writing last time? - // If so, close it and start over. - if (!mReadOnly && mRemoveAfterCrash) - { - llstat marker_info; - std::string marker = mDataFilename + ".open"; - if (!LLFile::stat(marker, &marker_info)) - { - // marker exists, kill the lock and the VFS files - unlockAndClose(mDataFP); - mDataFP = NULL; - - LL_WARNS("VFS") << "VFS: File left open on last run, removing old VFS file " << mDataFilename << LL_ENDL; - LLFile::remove(mIndexFilename); - LLFile::remove(mDataFilename); - LLFile::remove(marker); - - mDataFP = openAndLock(mDataFilename, "w+b", FALSE); - if (!mDataFP) - { - LL_WARNS("VFS") << "Can't open VFS data file in crash recovery" << LL_ENDL; - mValid = VFSVALID_BAD_CANNOT_CREATE; - return; - } - - if (presize) - { - presizeDataFile(presize); - } - } - } - - // determine the real file size - fseek(mDataFP, 0, SEEK_END); - U32 data_size = ftell(mDataFP); - - // read the index file - // make sure there's at least one file in it too - // if not, we'll treat this as a new vfs - llstat fbuf; - if (! LLFile::stat(mIndexFilename, &fbuf) && - fbuf.st_size >= LLVFSFileBlock::SERIAL_SIZE && - (mIndexFP = openAndLock(mIndexFilename, file_mode, mReadOnly)) // Yes, this is an assignment and not '==' - ) - { - std::vector buffer(fbuf.st_size); - size_t buf_offset = 0; - size_t nread = fread(&buffer[0], 1, fbuf.st_size, mIndexFP); - - std::vector files_by_loc; - - while (buf_offset < nread) - { - LLVFSFileBlock *block = new LLVFSFileBlock(); - - block->deserialize(&buffer[buf_offset], (S32)buf_offset); - - // Do sanity check on this block. - // Note that this skips zero size blocks, which helps VFS - // to heal after some errors. JC - if (block->mLength > 0 && - (U32)block->mLength <= data_size && - block->mLocation < data_size && - block->mSize > 0 && - block->mSize <= block->mLength && - block->mFileType >= LLAssetType::AT_NONE && - block->mFileType < LLAssetType::AT_COUNT) - { - mFileBlocks.insert(fileblock_map::value_type(*block, block)); - files_by_loc.push_back(block); - } - else - if (block->mLength && block->mSize > 0) - { - // this is corrupt, not empty - LL_WARNS("VFS") << "VFS corruption: " << block->mFileID << " (" << block->mFileType << ") at index " << block->mIndexLocation << " DS: " << data_size << LL_ENDL; - LL_WARNS("VFS") << "Length: " << block->mLength << "\tLocation: " << block->mLocation << "\tSize: " << block->mSize << LL_ENDL; - LL_WARNS("VFS") << "File has bad data - VFS removed" << LL_ENDL; - - delete block; - - unlockAndClose( mIndexFP ); - mIndexFP = NULL; - LLFile::remove( mIndexFilename ); - - unlockAndClose( mDataFP ); - mDataFP = NULL; - LLFile::remove( mDataFilename ); - - LL_WARNS("VFS") << "Deleted corrupt VFS files " - << mDataFilename - << " and " - << mIndexFilename - << LL_ENDL; - - mValid = VFSVALID_BAD_CORRUPT; - return; - } - else - { - // this is a null or bad entry, skip it - mIndexHoles.push_back(buf_offset); - - delete block; - } - - buf_offset += LLVFSFileBlock::SERIAL_SIZE; - } - - std::sort( - files_by_loc.begin(), - files_by_loc.end(), - LLVFSFileBlock::locationSortPredicate); - - // There are 3 cases that have to be considered. - // 1. No blocks - // 2. One block. - // 3. Two or more blocks. - if (!files_by_loc.empty()) - { - // cur walks through the list. - std::vector::iterator cur = files_by_loc.begin(); - std::vector::iterator end = files_by_loc.end(); - LLVFSFileBlock* last_file_block = *cur; - - // Check to see if there is an empty space before the first file. - if (last_file_block->mLocation > 0) - { - // If so, create a free block. - addFreeBlock(new LLVFSBlock(0, last_file_block->mLocation)); - } - - // Walk through the 2nd+ block. If there is a free space - // between cur_file_block and last_file_block, add it to - // the free space collection. This block will not need to - // run in the case there is only one entry in the VFS. - ++cur; - while( cur != end ) - { - LLVFSFileBlock* cur_file_block = *cur; - - // Dupe check on the block - if (cur_file_block->mLocation == last_file_block->mLocation - && cur_file_block->mLength == last_file_block->mLength) - { - LL_WARNS("VFS") << "VFS: removing duplicate entry" - << " at " << cur_file_block->mLocation - << " length " << cur_file_block->mLength - << " size " << cur_file_block->mSize - << " ID " << cur_file_block->mFileID - << " type " << cur_file_block->mFileType - << LL_ENDL; - - // Duplicate entries. Nuke them both for safety. - mFileBlocks.erase(*cur_file_block); // remove ID/type entry - if (cur_file_block->mLength > 0) - { - // convert to hole - addFreeBlock( - new LLVFSBlock( - cur_file_block->mLocation, - cur_file_block->mLength)); - } - lockData(); // needed for sync() - sync(cur_file_block, TRUE); // remove first on disk - sync(last_file_block, TRUE); // remove last on disk - unlockData(); // needed for sync() - last_file_block = cur_file_block; - ++cur; - continue; - } - - // Figure out where the last block ended. - S32 loc = last_file_block->mLocation+last_file_block->mLength; - - // Figure out how much space there is between where - // the last block ended and this block begins. - S32 length = cur_file_block->mLocation - loc; - - // Check for more errors... Seeing if the current - // entry and the last entry make sense together. - if (length < 0 || loc < 0 || (U32)loc > data_size) - { - // Invalid VFS - unlockAndClose( mIndexFP ); - mIndexFP = NULL; - LLFile::remove( mIndexFilename ); - - unlockAndClose( mDataFP ); - mDataFP = NULL; - LLFile::remove( mDataFilename ); - - LL_WARNS("VFS") << "VFS: overlapping entries" - << " at " << cur_file_block->mLocation - << " length " << cur_file_block->mLength - << " ID " << cur_file_block->mFileID - << " type " << cur_file_block->mFileType - << LL_ENDL; - - LL_WARNS("VFS") << "Deleted corrupt VFS files " - << mDataFilename - << " and " - << mIndexFilename - << LL_ENDL; - - mValid = VFSVALID_BAD_CORRUPT; - return; - } - - // we don't want to add empty blocks to the list... - if (length > 0) - { - addFreeBlock(new LLVFSBlock(loc, length)); - } - last_file_block = cur_file_block; - ++cur; - } - - // also note any empty space at the end - U32 loc = last_file_block->mLocation + last_file_block->mLength; - if (loc < data_size) - { - addFreeBlock(new LLVFSBlock(loc, data_size - loc)); - } - } - else // There where no blocks in the file. - { - addFreeBlock(new LLVFSBlock(0, data_size)); - } - } - else // Pre-existing index file wasn't opened - { - if (mReadOnly) - { - LL_WARNS("VFS") << "Can't find " << mIndexFilename << " to open read-only VFS" << LL_ENDL; - mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY; - return; - } - - - mIndexFP = openAndLock(mIndexFilename, "w+b", FALSE); - if (!mIndexFP) - { - LL_WARNS("VFS") << "Couldn't open an index file for the VFS, probably a sharing violation!" << LL_ENDL; - - unlockAndClose( mDataFP ); - mDataFP = NULL; - LLFile::remove( mDataFilename ); - - mValid = VFSVALID_BAD_CANNOT_CREATE; - return; - } - - // no index file, start from scratch w/ 1GB allocation - LLVFSBlock *first_block = new LLVFSBlock(0, data_size ? data_size : 0x40000000); - addFreeBlock(first_block); - } - - // Open marker file to look for bad shutdowns - if (!mReadOnly && mRemoveAfterCrash) - { - std::string marker = mDataFilename + ".open"; - LLFILE* marker_fp = LLFile::fopen(marker, "w"); /* Flawfinder: ignore */ - if (marker_fp) - { - fclose(marker_fp); - marker_fp = NULL; - } - } - - LL_INFOS("VFS") << "Using VFS index file " << mIndexFilename << LL_ENDL; - LL_INFOS("VFS") << "Using VFS data file " << mDataFilename << LL_ENDL; - - mValid = VFSVALID_OK; -} - -LLVFS::~LLVFS() -{ - if (mDataMutex->isLocked()) - { - LL_ERRS("VFS") << "LLVFS destroyed with mutex locked" << LL_ENDL; - } - - unlockAndClose(mIndexFP); - mIndexFP = NULL; - - fileblock_map::const_iterator it; - for (it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - delete (*it).second; - } - mFileBlocks.clear(); - - mFreeBlocksByLength.clear(); - - for_each(mFreeBlocksByLocation.begin(), mFreeBlocksByLocation.end(), DeletePairedPointer()); - mFreeBlocksByLocation.clear(); - - unlockAndClose(mDataFP); - mDataFP = NULL; - - // Remove marker file - if (!mReadOnly && mRemoveAfterCrash) - { - std::string marker = mDataFilename + ".open"; - LLFile::remove(marker); - } - - delete mDataMutex; -} - - -// Use this function normally to create LLVFS files. -// Will append digits to the end of the filename with multiple re-trys -// static -LLVFS * LLVFS::createLLVFS(const std::string& index_filename, - const std::string& data_filename, - const BOOL read_only, - const U32 presize, - const BOOL remove_after_crash) -{ - LLVFS * new_vfs = new LLVFS(index_filename, data_filename, read_only, presize, remove_after_crash); - - if( !new_vfs->isValid() ) - { // First name failed, retry with new names - std::string retry_vfs_index_name; - std::string retry_vfs_data_name; - S32 count = 0; - while (!new_vfs->isValid() && - count < 256) - { // Append '.' to end of filenames - retry_vfs_index_name = index_filename + llformat(".%u",count); - retry_vfs_data_name = data_filename + llformat(".%u", count); - - delete new_vfs; // Delete bad VFS and try again - new_vfs = new LLVFS(retry_vfs_index_name, retry_vfs_data_name, read_only, presize, remove_after_crash); - - count++; - } - } - - if( !new_vfs->isValid() ) - { - delete new_vfs; // Delete bad VFS - new_vfs = NULL; // Total failure - } - - return new_vfs; -} - - - -void LLVFS::presizeDataFile(const U32 size) -{ - if (!mDataFP) - { - LL_ERRS() << "LLVFS::presizeDataFile() with no data file open" << LL_ENDL; - return; - } - - // we're creating this file for the first time, size it - fseek(mDataFP, size-1, SEEK_SET); - S32 tmp = 0; - tmp = (S32)fwrite(&tmp, 1, 1, mDataFP); - // fflush(mDataFP); - - // also remove any index, since this vfs is now blank - LLFile::remove(mIndexFilename); - - if (tmp) - { - LL_INFOS() << "Pre-sized VFS data file to " << ftell(mDataFP) << " bytes" << LL_ENDL; - } - else - { - LL_WARNS() << "Failed to pre-size VFS data file" << LL_ENDL; - } -} - -BOOL LLVFS::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - LLVFSFileBlock *block = NULL; - - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - block = (*it).second; - block->mAccessTime = (U32)time(NULL); - } - - BOOL res = (block && block->mLength > 0) ? TRUE : FALSE; - - unlockData(); - - return res; -} - -S32 LLVFS::getSize(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - S32 size = 0; - - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - - } - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - - block->mAccessTime = (U32)time(NULL); - size = block->mSize; - } - - unlockData(); - - return size; -} - -S32 LLVFS::getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - S32 size = 0; - - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - - block->mAccessTime = (U32)time(NULL); - size = block->mLength; - } - - unlockData(); - - return size; -} - -BOOL LLVFS::checkAvailable(S32 max_size) -{ - lockData(); - - blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(max_size); // first entry >= size - const BOOL res(iter == mFreeBlocksByLength.end() ? FALSE : TRUE); - - unlockData(); - - return res; -} - -BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type, S32 max_size) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - if (mReadOnly) - { - LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; - } - if (max_size <= 0) - { - LL_WARNS() << "VFS: Attempt to assign size " << max_size << " to vfile " << file_id << LL_ENDL; - return FALSE; - } - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - LLVFSFileBlock *block = NULL; - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - block = (*it).second; - } - - // round all sizes upward to KB increments - // SJB: Need to not round for the new texture-pipeline code so we know the correct - // max file size. Need to investigate the potential problems with this... - if (file_type != LLAssetType::AT_TEXTURE) - { - if (max_size & FILE_BLOCK_MASK) - { - max_size += FILE_BLOCK_MASK; - max_size &= ~FILE_BLOCK_MASK; - } - } - - if (block && block->mLength > 0) - { - block->mAccessTime = (U32)time(NULL); - - if (max_size == block->mLength) - { - unlockData(); - return TRUE; - } - else if (max_size < block->mLength) - { - // this file is shrinking - LLVFSBlock *free_block = new LLVFSBlock(block->mLocation + max_size, block->mLength - max_size); - - addFreeBlock(free_block); - - block->mLength = max_size; - - if (block->mLength < block->mSize) - { - // JC: Was a warning, but Ian says it's bad. - LL_ERRS() << "Truncating virtual file " << file_id << " to " << block->mLength << " bytes" << LL_ENDL; - block->mSize = block->mLength; - } - - sync(block); - //mergeFreeBlocks(); - - unlockData(); - return TRUE; - } - else if (max_size > block->mLength) - { - // this file is growing - // first check for an adjacent free block to grow into - S32 size_increase = max_size - block->mLength; - - // Find the first free block with and addres > block->mLocation - LLVFSBlock *free_block; - blocks_location_map_t::iterator iter = mFreeBlocksByLocation.upper_bound(block->mLocation); - if (iter != mFreeBlocksByLocation.end()) - { - free_block = iter->second; - - if (free_block->mLocation == block->mLocation + block->mLength && - free_block->mLength >= size_increase) - { - // this free block is at the end of the file and is large enough - - // Must call useFreeSpace before sync(), as sync() - // unlocks data structures. - useFreeSpace(free_block, size_increase); - block->mLength += size_increase; - sync(block); - - unlockData(); - return TRUE; - } - } - - // no adjecent free block, find one in the list - free_block = findFreeBlock(max_size, block); - - if (free_block) - { - // Save location where data is going, useFreeSpace will move free_block->mLocation; - U32 new_data_location = free_block->mLocation; - - //mark the free block as used so it does not - //interfere with other operations such as addFreeBlock - useFreeSpace(free_block, max_size); // useFreeSpace takes ownership (and may delete) free_block - - if (block->mLength > 0) - { - // create a new free block where this file used to be - LLVFSBlock *new_free_block = new LLVFSBlock(block->mLocation, block->mLength); - - addFreeBlock(new_free_block); - - if (block->mSize > 0) - { - // move the file into the new block - std::vector buffer(block->mSize); - fseek(mDataFP, block->mLocation, SEEK_SET); - if (fread(&buffer[0], block->mSize, 1, mDataFP) == 1) - { - fseek(mDataFP, new_data_location, SEEK_SET); - if (fwrite(&buffer[0], block->mSize, 1, mDataFP) != 1) - { - LL_WARNS() << "Short write" << LL_ENDL; - } - } else { - LL_WARNS() << "Short read" << LL_ENDL; - } - } - } - - block->mLocation = new_data_location; - - block->mLength = max_size; - - - sync(block); - - unlockData(); - return TRUE; - } - else - { - LL_WARNS() << "VFS: No space (" << max_size << ") to resize existing vfile " << file_id << LL_ENDL; - //dumpMap(); - unlockData(); - dumpStatistics(); - return FALSE; - } - } - } - else - { - // find a free block in the list - LLVFSBlock *free_block = findFreeBlock(max_size); - - if (free_block) - { - if (block) - { - block->mLocation = free_block->mLocation; - block->mLength = max_size; - } - else - { - // this file doesn't exist, create it - block = new LLVFSFileBlock(file_id, file_type, free_block->mLocation, max_size); - mFileBlocks.insert(fileblock_map::value_type(spec, block)); - } - - // Must call useFreeSpace before sync(), as sync() - // unlocks data structures. - useFreeSpace(free_block, max_size); - block->mAccessTime = (U32)time(NULL); - - sync(block); - } - else - { - LL_WARNS() << "VFS: No space (" << max_size << ") for new virtual file " << file_id << LL_ENDL; - //dumpMap(); - unlockData(); - dumpStatistics(); - return FALSE; - } - } - unlockData(); - return TRUE; -} - - -// WARNING: HERE BE DRAGONS! -// rename is the weirdest VFS op, because the file moves but the locks don't! -void LLVFS::renameFile(const LLUUID &file_id, const LLAssetType::EType file_type, - const LLUUID &new_id, const LLAssetType::EType &new_type) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - if (mReadOnly) - { - LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; - } - - lockData(); - - LLVFSFileSpecifier new_spec(new_id, new_type); - LLVFSFileSpecifier old_spec(file_id, file_type); - - fileblock_map::iterator it = mFileBlocks.find(old_spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *src_block = (*it).second; - - // this will purge the data but leave the file block in place, w/ locks, if any - // WAS: removeFile(new_id, new_type); NOW uses removeFileBlock() to avoid mutex lock recursion - fileblock_map::iterator new_it = mFileBlocks.find(new_spec); - if (new_it != mFileBlocks.end()) - { - LLVFSFileBlock *new_block = (*new_it).second; - removeFileBlock(new_block); - } - - // if there's something in the target location, remove it but inherit its locks - it = mFileBlocks.find(new_spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *dest_block = (*it).second; - - for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++) - { - if(dest_block->mLocks[i]) - { - LL_ERRS() << "Renaming VFS block to a locked file." << LL_ENDL; - } - dest_block->mLocks[i] = src_block->mLocks[i]; - } - - mFileBlocks.erase(new_spec); - delete dest_block; - } - - src_block->mFileID = new_id; - src_block->mFileType = new_type; - src_block->mAccessTime = (U32)time(NULL); - - mFileBlocks.erase(old_spec); - mFileBlocks.insert(fileblock_map::value_type(new_spec, src_block)); - - sync(src_block); - } - else - { - LL_WARNS() << "VFS: Attempt to rename nonexistent vfile " << file_id << ":" << file_type << LL_ENDL; - } - unlockData(); -} - -// mDataMutex must be LOCKED before calling this -void LLVFS::removeFileBlock(LLVFSFileBlock *fileblock) -{ - // convert this into an unsaved, dummy fileblock to preserve locks - // a more rubust solution would store the locks in a seperate data structure - sync(fileblock, TRUE); - - if (fileblock->mLength > 0) - { - // turn this file into an empty block - LLVFSBlock *free_block = new LLVFSBlock(fileblock->mLocation, fileblock->mLength); - - addFreeBlock(free_block); - } - - fileblock->mLocation = 0; - fileblock->mSize = 0; - fileblock->mLength = BLOCK_LENGTH_INVALID; - fileblock->mIndexLocation = -1; - - //mergeFreeBlocks(); -} - -void LLVFS::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - if (mReadOnly) - { - LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; - } - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - removeFileBlock(block); - } - else - { - LL_WARNS() << "VFS: attempting to remove nonexistent file " << file_id << " type " << file_type << LL_ENDL; - } - - unlockData(); -} - - -S32 LLVFS::getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 *buffer, S32 location, S32 length) -{ - S32 bytesread = 0; - - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - llassert(location >= 0); - llassert(length >= 0); - - BOOL do_read = FALSE; - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - - block->mAccessTime = (U32)time(NULL); - - if (location > block->mSize) - { - LL_WARNS() << "VFS: Attempt to read location " << location << " in file " << file_id << " of length " << block->mSize << LL_ENDL; - } - else - { - if (length > block->mSize - location) - { - length = block->mSize - location; - } - location += block->mLocation; - do_read = TRUE; - } - } - - if (do_read) - { - fseek(mDataFP, location, SEEK_SET); - bytesread = (S32)fread(buffer, 1, length, mDataFP); - } - - unlockData(); - - return bytesread; -} - -S32 LLVFS::storeData(const LLUUID &file_id, const LLAssetType::EType file_type, const U8 *buffer, S32 location, S32 length) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - if (mReadOnly) - { - LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; - } - - llassert(length > 0); - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - - S32 in_loc = location; - if (location == -1) - { - location = block->mSize; - } - llassert(location >= 0); - - block->mAccessTime = (U32)time(NULL); - - if (block->mLength == BLOCK_LENGTH_INVALID) - { - // Block was removed, ignore write - LL_WARNS() << "VFS: Attempt to write to invalid block" - << " in file " << file_id - << " location: " << in_loc - << " bytes: " << length - << LL_ENDL; - unlockData(); - return length; - } - else if (location > block->mLength) - { - LL_WARNS() << "VFS: Attempt to write to location " << location - << " in file " << file_id - << " type " << S32(file_type) - << " of size " << block->mSize - << " block length " << block->mLength - << LL_ENDL; - unlockData(); - return length; - } - else - { - if (length > block->mLength - location ) - { - LL_WARNS() << "VFS: Truncating write to virtual file " << file_id << " type " << S32(file_type) << LL_ENDL; - length = block->mLength - location; - } - U32 file_location = location + block->mLocation; - - fseek(mDataFP, file_location, SEEK_SET); - S32 write_len = (S32)fwrite(buffer, 1, length, mDataFP); - if (write_len != length) - { - LL_WARNS() << llformat("VFS Write Error: %d != %d",write_len,length) << LL_ENDL; - } - // fflush(mDataFP); - - if (location + length > block->mSize) - { - block->mSize = location + write_len; - sync(block); - } - unlockData(); - - return write_len; - } - } - else - { - unlockData(); - return 0; - } -} - -void LLVFS::incLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock) -{ - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - LLVFSFileBlock *block; - - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - block = (*it).second; - } - else - { - // Create a dummy block which isn't saved - block = new LLVFSFileBlock(file_id, file_type, 0, BLOCK_LENGTH_INVALID); - block->mAccessTime = (U32)time(NULL); - mFileBlocks.insert(fileblock_map::value_type(spec, block)); - } - - block->mLocks[lock]++; - mLockCounts[lock]++; - - unlockData(); -} - -void LLVFS::decLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock) -{ - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - - if (block->mLocks[lock] > 0) - { - block->mLocks[lock]--; - } - else - { - LL_WARNS() << "VFS: Decrementing zero-value lock " << lock << LL_ENDL; - } - mLockCounts[lock]--; - } - - unlockData(); -} - -BOOL LLVFS::isLocked(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock) -{ - lockData(); - - BOOL res = FALSE; - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - res = (block->mLocks[lock] > 0); - } - - unlockData(); - - return res; -} - -//============================================================================ -// protected -//============================================================================ - -void LLVFS::eraseBlockLength(LLVFSBlock *block) -{ - // find the corresponding map entry in the length map and erase it - S32 length = block->mLength; - blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(length); - blocks_length_map_t::iterator end = mFreeBlocksByLength.end(); - bool found_block = false; - while(iter != end) - { - LLVFSBlock *tblock = iter->second; - llassert(tblock->mLength == length); // there had -better- be an entry with our length! - if (tblock == block) - { - mFreeBlocksByLength.erase(iter); - found_block = true; - break; - } - ++iter; - } - if(!found_block) - { - LL_ERRS() << "eraseBlock could not find block" << LL_ENDL; - } -} - - -// Remove block from both free lists (by location and by length). -void LLVFS::eraseBlock(LLVFSBlock *block) -{ - eraseBlockLength(block); - // find the corresponding map entry in the location map and erase it - U32 location = block->mLocation; - llverify(mFreeBlocksByLocation.erase(location) == 1); // we should only have one entry per location. -} - - -// Add the region specified by block location and length to the free lists. -// Also incrementally defragment by merging with previous and next free blocks. -void LLVFS::addFreeBlock(LLVFSBlock *block) -{ -#if LL_DEBUG - size_t dbgcount = mFreeBlocksByLocation.count(block->mLocation); - if(dbgcount > 0) - { - LL_ERRS() << "addFreeBlock called with block already in list" << LL_ENDL; - } -#endif - - // Get a pointer to the next free block (by location). - blocks_location_map_t::iterator next_free_it = mFreeBlocksByLocation.lower_bound(block->mLocation); - - // We can merge with previous if it ends at our requested location. - LLVFSBlock* prev_block = NULL; - bool merge_prev = false; - if (next_free_it != mFreeBlocksByLocation.begin()) - { - blocks_location_map_t::iterator prev_free_it = next_free_it; - --prev_free_it; - prev_block = prev_free_it->second; - merge_prev = (prev_block->mLocation + prev_block->mLength == block->mLocation); - } - - // We can merge with next if our block ends at the next block's location. - LLVFSBlock* next_block = NULL; - bool merge_next = false; - if (next_free_it != mFreeBlocksByLocation.end()) - { - next_block = next_free_it->second; - merge_next = (block->mLocation + block->mLength == next_block->mLocation); - } - - if (merge_prev && merge_next) - { - // LL_INFOS() << "VFS merge BOTH" << LL_ENDL; - // Previous block is changing length (a lot), so only need to update length map. - // Next block is going away completely. JC - eraseBlockLength(prev_block); - eraseBlock(next_block); - prev_block->mLength += block->mLength + next_block->mLength; - mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block)); - delete block; - block = NULL; - delete next_block; - next_block = NULL; - } - else if (merge_prev) - { - // LL_INFOS() << "VFS merge previous" << LL_ENDL; - // Previous block is maintaining location, only changing length, - // therefore only need to update the length map. JC - eraseBlockLength(prev_block); - prev_block->mLength += block->mLength; - mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block)); // multimap insert - delete block; - block = NULL; - } - else if (merge_next) - { - // LL_INFOS() << "VFS merge next" << LL_ENDL; - // Next block is changing both location and length, - // so both free lists must update. JC - eraseBlock(next_block); - next_block->mLocation = block->mLocation; - next_block->mLength += block->mLength; - // Don't hint here, next_free_it iterator may be invalid. - mFreeBlocksByLocation.insert(blocks_location_map_t::value_type(next_block->mLocation, next_block)); // multimap insert - mFreeBlocksByLength.insert(blocks_length_map_t::value_type(next_block->mLength, next_block)); // multimap insert - delete block; - block = NULL; - } - else - { - // Can't merge with other free blocks. - // Hint that insert should go near next_free_it. - mFreeBlocksByLocation.insert(next_free_it, blocks_location_map_t::value_type(block->mLocation, block)); // multimap insert - mFreeBlocksByLength.insert(blocks_length_map_t::value_type(block->mLength, block)); // multimap insert - } -} - -// Superceeded by new addFreeBlock which does incremental free space merging. -// Incremental is faster overall. -//void LLVFS::mergeFreeBlocks() -//{ -// if (!isValid()) -// { -// LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; -// } -// // TODO: could we optimize this with hints from the calling code? -// blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(); -// blocks_location_map_t::iterator end = mFreeBlocksByLocation.end(); -// LLVFSBlock *first_block = iter->second; -// while(iter != end) -// { -// blocks_location_map_t::iterator first_iter = iter; // save for if we do a merge -// if (++iter == end) -// break; -// LLVFSBlock *second_block = iter->second; -// if (first_block->mLocation + first_block->mLength == second_block->mLocation) -// { -// // remove the first block from the length map -// eraseBlockLength(first_block); -// // merge first_block with second_block, since they're adjacent -// first_block->mLength += second_block->mLength; -// // add the first block to the length map (with the new size) -// mFreeBlocksByLength.insert(blocks_length_map_t::value_type(first_block->mLength, first_block)); // multimap insert -// -// // erase and delete the second block -// eraseBlock(second_block); -// delete second_block; -// -// // reset iterator -// iter = first_iter; // haven't changed first_block, so corresponding iterator is still valid -// end = mFreeBlocksByLocation.end(); -// } -// first_block = second_block; -// } -//} - -// length bytes from free_block are going to be used (so they are no longer free) -void LLVFS::useFreeSpace(LLVFSBlock *free_block, S32 length) -{ - if (free_block->mLength == length) - { - eraseBlock(free_block); - delete free_block; - } - else - { - eraseBlock(free_block); - - free_block->mLocation += length; - free_block->mLength -= length; - - addFreeBlock(free_block); - } -} - -// NOTE! mDataMutex must be LOCKED before calling this -// sync this index entry out to the index file -// we need to do this constantly to avoid corruption on viewer crash -void LLVFS::sync(LLVFSFileBlock *block, BOOL remove) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - if (mReadOnly) - { - LL_WARNS() << "Attempt to sync read-only VFS" << LL_ENDL; - return; - } - if (block->mLength == BLOCK_LENGTH_INVALID) - { - // This is a dummy file, don't save - return; - } - if (block->mLength == 0) - { - LL_ERRS() << "VFS syncing zero-length block" << LL_ENDL; - } - - BOOL set_index_to_end = FALSE; - long seek_pos = block->mIndexLocation; - - if (-1 == seek_pos) - { - if (!mIndexHoles.empty()) - { - seek_pos = mIndexHoles.front(); - mIndexHoles.pop_front(); - } - else - { - set_index_to_end = TRUE; - } - } - - if (set_index_to_end) - { - // Need fseek/ftell to update the seek_pos and hence data - // structures, so can't unlockData() before this. - fseek(mIndexFP, 0, SEEK_END); - seek_pos = ftell(mIndexFP); - } - - block->mIndexLocation = seek_pos; - if (remove) - { - mIndexHoles.push_back(seek_pos); - } - - U8 buffer[LLVFSFileBlock::SERIAL_SIZE]; - if (remove) - { - memset(buffer, 0, LLVFSFileBlock::SERIAL_SIZE); - } - else - { - block->serialize(buffer); - } - - // If set_index_to_end, file pointer is already at seek_pos - // and we don't need to do anything. Only seek if not at end. - if (!set_index_to_end) - { - fseek(mIndexFP, seek_pos, SEEK_SET); - } - - if (fwrite(buffer, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP) != 1) - { - LL_WARNS() << "Short write" << LL_ENDL; - } - - // *NOTE: Why was this commented out? - // fflush(mIndexFP); - - return; -} - -// mDataMutex must be LOCKED before calling this -// Can initiate LRU-based file removal to make space. -// The immune file block will not be removed. -LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - - LLVFSBlock *block = NULL; - BOOL have_lru_list = FALSE; - - typedef std::set lru_set; - lru_set lru_list; - - LLTimer timer; - - while (! block) - { - // look for a suitable free block - blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(size); // first entry >= size - if (iter != mFreeBlocksByLength.end()) - block = iter->second; - - // no large enough free blocks, time to clean out some junk - if (! block) - { - // create a list of files sorted by usage time - // this is far faster than sorting a linked list - if (! have_lru_list) - { - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileBlock *tmp = (*it).second; - - if (tmp != immune && - tmp->mLength > 0 && - ! tmp->mLocks[VFSLOCK_READ] && - ! tmp->mLocks[VFSLOCK_APPEND] && - ! tmp->mLocks[VFSLOCK_OPEN]) - { - lru_list.insert(tmp); - } - } - - have_lru_list = TRUE; - } - - if (lru_list.size() == 0) - { - // No more files to delete, and still not enough room! - LL_WARNS() << "VFS: Can't make " << size << " bytes of free space in VFS, giving up" << LL_ENDL; - break; - } - - // is the oldest file big enough? (Should be about half the time) - lru_set::iterator it = lru_list.begin(); - LLVFSFileBlock *file_block = *it; - if (file_block->mLength >= size && file_block != immune) - { - // ditch this file and look again for a free block - should find it - // TODO: it'll be faster just to assign the free block and break - LL_INFOS() << "LRU: Removing " << file_block->mFileID << ":" << file_block->mFileType << LL_ENDL; - lru_list.erase(it); - removeFileBlock(file_block); - file_block = NULL; - continue; - } - - - LL_INFOS() << "VFS: LRU: Aggressive: " << (S32)lru_list.size() << " files remain" << LL_ENDL; - dumpLockCounts(); - - // Now it's time to aggressively make more space - // Delete the oldest 5MB of the vfs or enough to hold the file, which ever is larger - // This may yield too much free space, but we'll use it up soon enough - U32 cleanup_target = (size > VFS_CLEANUP_SIZE) ? size : VFS_CLEANUP_SIZE; - U32 cleaned_up = 0; - for (it = lru_list.begin(); - it != lru_list.end() && cleaned_up < cleanup_target; - ) - { - file_block = *it; - - // TODO: it would be great to be able to batch all these sync() calls - // LL_INFOS() << "LRU2: Removing " << file_block->mFileID << ":" << file_block->mFileType << " last accessed" << file_block->mAccessTime << LL_ENDL; - - cleaned_up += file_block->mLength; - lru_list.erase(it++); - removeFileBlock(file_block); - file_block = NULL; - } - //mergeFreeBlocks(); - } - } - - F32 time = timer.getElapsedTimeF32(); - if (time > 0.5f) - { - LL_WARNS() << "VFS: Spent " << time << " seconds in findFreeBlock!" << LL_ENDL; - } - - return block; -} - -//============================================================================ -// public -//============================================================================ - -void LLVFS::pokeFiles() -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - U32 word; - - // only write data if we actually read 4 bytes - // otherwise we're writing garbage and screwing up the file - fseek(mDataFP, 0, SEEK_SET); - if (fread(&word, sizeof(word), 1, mDataFP) == 1) - { - fseek(mDataFP, 0, SEEK_SET); - if (fwrite(&word, sizeof(word), 1, mDataFP) != 1) - { - LL_WARNS() << "Could not write to data file" << LL_ENDL; - } - fflush(mDataFP); - } - - fseek(mIndexFP, 0, SEEK_SET); - if (fread(&word, sizeof(word), 1, mIndexFP) == 1) - { - fseek(mIndexFP, 0, SEEK_SET); - if (fwrite(&word, sizeof(word), 1, mIndexFP) != 1) - { - LL_WARNS() << "Could not write to index file" << LL_ENDL; - } - fflush(mIndexFP); - } -} - - -void LLVFS::dumpMap() -{ - LL_INFOS() << "Files:" << LL_ENDL; - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileBlock *file_block = (*it).second; - LL_INFOS() << "Location: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << LL_ENDL; - } - - LL_INFOS() << "Free Blocks:" << LL_ENDL; - for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(), - end = mFreeBlocksByLocation.end(); - iter != end; iter++) - { - LLVFSBlock *free_block = iter->second; - LL_INFOS() << "Location: " << free_block->mLocation << "\tLength: " << free_block->mLength << LL_ENDL; - } -} - -// verify that the index file contents match the in-memory file structure -// Very slow, do not call routinely. JC -void LLVFS::audit() -{ - // Lock the mutex through this whole function. - LLMutexLock lock_data(mDataMutex); - - fflush(mIndexFP); - - fseek(mIndexFP, 0, SEEK_END); - size_t index_size = ftell(mIndexFP); - fseek(mIndexFP, 0, SEEK_SET); - - BOOL vfs_corrupt = FALSE; - - // since we take the address of element 0, we need to have at least one element. - std::vector buffer(llmax(index_size,1U)); - - if (fread(&buffer[0], 1, index_size, mIndexFP) != index_size) - { - LL_WARNS() << "Index truncated" << LL_ENDL; - vfs_corrupt = TRUE; - } - - size_t buf_offset = 0; - - std::map found_files; - U32 cur_time = (U32)time(NULL); - - std::vector audit_blocks; - while (!vfs_corrupt && buf_offset < index_size) - { - LLVFSFileBlock *block = new LLVFSFileBlock(); - audit_blocks.push_back(block); - - block->deserialize(&buffer[buf_offset], (S32)buf_offset); - buf_offset += block->SERIAL_SIZE; - - // do sanity check on this block - if (block->mLength >= 0 && - block->mSize >= 0 && - block->mSize <= block->mLength && - block->mFileType >= LLAssetType::AT_NONE && - block->mFileType < LLAssetType::AT_COUNT && - block->mAccessTime <= cur_time && - block->mFileID != LLUUID::null) - { - if (mFileBlocks.find(*block) == mFileBlocks.end()) - { - LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " on disk, not in memory, loc " << block->mIndexLocation << LL_ENDL; - } - else if (found_files.find(*block) != found_files.end()) - { - std::map::iterator it; - it = found_files.find(*block); - LLVFSFileBlock* dupe = it->second; - // try to keep data from being lost - unlockAndClose(mIndexFP); - mIndexFP = NULL; - unlockAndClose(mDataFP); - mDataFP = NULL; - LL_WARNS() << "VFS: Original block index " << block->mIndexLocation - << " location " << block->mLocation - << " length " << block->mLength - << " size " << block->mSize - << " id " << block->mFileID - << " type " << block->mFileType - << LL_ENDL; - LL_WARNS() << "VFS: Duplicate block index " << dupe->mIndexLocation - << " location " << dupe->mLocation - << " length " << dupe->mLength - << " size " << dupe->mSize - << " id " << dupe->mFileID - << " type " << dupe->mFileType - << LL_ENDL; - LL_WARNS() << "VFS: Index size " << index_size << LL_ENDL; - LL_WARNS() << "VFS: INDEX CORRUPT" << LL_ENDL; - vfs_corrupt = TRUE; - break; - } - else - { - found_files[*block] = block; - } - } - else - { - if (block->mLength) - { - LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " corrupt on disk" << LL_ENDL; - } - // else this is just a hole - } - } - - if (!vfs_corrupt) - { - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileBlock* block = (*it).second; - - if (block->mSize > 0) - { - if (! found_files.count(*block)) - { - LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " in memory, not on disk, loc " << block->mIndexLocation<< LL_ENDL; - fseek(mIndexFP, block->mIndexLocation, SEEK_SET); - U8 buf[LLVFSFileBlock::SERIAL_SIZE]; - if (fread(buf, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP) != 1) - { - LL_WARNS() << "VFile " << block->mFileID - << " gave short read" << LL_ENDL; - } - - LLVFSFileBlock disk_block; - disk_block.deserialize(buf, block->mIndexLocation); - - LL_WARNS() << "Instead found " << disk_block.mFileID << ":" << block->mFileType << LL_ENDL; - } - else - { - block = found_files.find(*block)->second; - found_files.erase(*block); - } - } - } - - for (std::map::iterator iter = found_files.begin(); - iter != found_files.end(); iter++) - { - LLVFSFileBlock* block = iter->second; - LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " szie:" << block->mSize << " leftover" << LL_ENDL; - } - - LL_INFOS() << "VFS: audit OK" << LL_ENDL; - // mutex released by LLMutexLock() destructor. - } - - for_each(audit_blocks.begin(), audit_blocks.end(), DeletePointer()); - audit_blocks.clear(); -} - - -// quick check for uninitialized blocks -// Slow, do not call in release. -void LLVFS::checkMem() -{ - lockData(); - - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileBlock *block = (*it).second; - llassert(block->mFileType >= LLAssetType::AT_NONE && - block->mFileType < LLAssetType::AT_COUNT && - block->mFileID != LLUUID::null); - - for (std::deque::iterator iter = mIndexHoles.begin(); - iter != mIndexHoles.end(); ++iter) - { - S32 index_loc = *iter; - if (index_loc == block->mIndexLocation) - { - LL_WARNS() << "VFile block " << block->mFileID << ":" << block->mFileType << " is marked as a hole" << LL_ENDL; - } - } - } - - LL_INFOS() << "VFS: mem check OK" << LL_ENDL; - - unlockData(); -} - -void LLVFS::dumpLockCounts() -{ - S32 i; - for (i = 0; i < VFSLOCK_COUNT; i++) - { - LL_INFOS() << "LockType: " << i << ": " << mLockCounts[i] << LL_ENDL; - } -} - -void LLVFS::dumpStatistics() -{ - lockData(); - - // Investigate file blocks. - std::map size_counts; - std::map location_counts; - std::map > filetype_counts; - - S32 max_file_size = 0; - S32 total_file_size = 0; - S32 invalid_file_count = 0; - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileBlock *file_block = (*it).second; - if (file_block->mLength == BLOCK_LENGTH_INVALID) - { - invalid_file_count++; - } - else if (file_block->mLength <= 0) - { - LL_INFOS() << "Bad file block at: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << LL_ENDL; - size_counts[file_block->mLength]++; - location_counts[file_block->mLocation]++; - } - else - { - total_file_size += file_block->mLength; - } - - if (file_block->mLength > max_file_size) - { - max_file_size = file_block->mLength; - } - - filetype_counts[file_block->mFileType].first++; - filetype_counts[file_block->mFileType].second += file_block->mLength; - } - - for (std::map::iterator it = size_counts.begin(); it != size_counts.end(); ++it) - { - S32 size = it->first; - S32 size_count = it->second; - LL_INFOS() << "Bad files size " << size << " count " << size_count << LL_ENDL; - } - for (std::map::iterator it = location_counts.begin(); it != location_counts.end(); ++it) - { - U32 location = it->first; - S32 location_count = it->second; - LL_INFOS() << "Bad files location " << location << " count " << location_count << LL_ENDL; - } - - // Investigate free list. - S32 max_free_size = 0; - S32 total_free_size = 0; - std::map free_length_counts; - for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(), - end = mFreeBlocksByLocation.end(); - iter != end; iter++) - { - LLVFSBlock *free_block = iter->second; - if (free_block->mLength <= 0) - { - LL_INFOS() << "Bad free block at: " << free_block->mLocation << "\tLength: " << free_block->mLength << LL_ENDL; - } - else - { - LL_INFOS() << "Block: " << free_block->mLocation - << "\tLength: " << free_block->mLength - << "\tEnd: " << free_block->mLocation + free_block->mLength - << LL_ENDL; - total_free_size += free_block->mLength; - } - - if (free_block->mLength > max_free_size) - { - max_free_size = free_block->mLength; - } - - free_length_counts[free_block->mLength]++; - } - - // Dump histogram of free block sizes - for (std::map::iterator it = free_length_counts.begin(); it != free_length_counts.end(); ++it) - { - LL_INFOS() << "Free length " << it->first << " count " << it->second << LL_ENDL; - } - - LL_INFOS() << "Invalid blocks: " << invalid_file_count << LL_ENDL; - LL_INFOS() << "File blocks: " << mFileBlocks.size() << LL_ENDL; - - S32 length_list_count = (S32)mFreeBlocksByLength.size(); - S32 location_list_count = (S32)mFreeBlocksByLocation.size(); - if (length_list_count == location_list_count) - { - LL_INFOS() << "Free list lengths match, free blocks: " << location_list_count << LL_ENDL; - } - else - { - LL_WARNS() << "Free list lengths do not match!" << LL_ENDL; - LL_WARNS() << "By length: " << length_list_count << LL_ENDL; - LL_WARNS() << "By location: " << location_list_count << LL_ENDL; - } - LL_INFOS() << "Max file: " << max_file_size/1024 << "K" << LL_ENDL; - LL_INFOS() << "Max free: " << max_free_size/1024 << "K" << LL_ENDL; - LL_INFOS() << "Total file size: " << total_file_size/1024 << "K" << LL_ENDL; - LL_INFOS() << "Total free size: " << total_free_size/1024 << "K" << LL_ENDL; - LL_INFOS() << "Sum: " << (total_file_size + total_free_size) << " bytes" << LL_ENDL; - LL_INFOS() << llformat("%.0f%% full",((F32)(total_file_size)/(F32)(total_file_size+total_free_size))*100.f) << LL_ENDL; - - LL_INFOS() << " " << LL_ENDL; - for (std::map >::iterator iter = filetype_counts.begin(); - iter != filetype_counts.end(); ++iter) - { - LL_INFOS() << "Type: " << LLAssetType::getDesc(iter->first) - << " Count: " << iter->second.first - << " Bytes: " << (iter->second.second>>20) << " MB" << LL_ENDL; - } - - // Look for potential merges - { - blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(); - blocks_location_map_t::iterator end = mFreeBlocksByLocation.end(); - LLVFSBlock *first_block = iter->second; - while(iter != end) - { - if (++iter == end) - break; - LLVFSBlock *second_block = iter->second; - if (first_block->mLocation + first_block->mLength == second_block->mLocation) - { - LL_INFOS() << "Potential merge at " << first_block->mLocation << LL_ENDL; - } - first_block = second_block; - } - } - unlockData(); -} - -// Debug Only! -std::string get_extension(LLAssetType::EType type) -{ - std::string extension; - switch(type) - { - case LLAssetType::AT_TEXTURE: - extension = ".jp2"; // formerly ".j2c" - break; - case LLAssetType::AT_SOUND: - extension = ".ogg"; - break; - case LLAssetType::AT_SOUND_WAV: - extension = ".wav"; - break; - case LLAssetType::AT_TEXTURE_TGA: - extension = ".tga"; - break; - case LLAssetType::AT_ANIMATION: - extension = ".lla"; - break; - case LLAssetType::AT_MESH: - extension = ".slm"; - break; - default: - // Just use the asset server filename extension in most cases - extension += "."; - extension += LLAssetType::lookup(type); - break; - } - return extension; -} - -void LLVFS::listFiles() -{ - lockData(); - - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileSpecifier file_spec = it->first; - LLVFSFileBlock *file_block = it->second; - S32 length = file_block->mLength; - S32 size = file_block->mSize; - if (length != BLOCK_LENGTH_INVALID && size > 0) - { - LLUUID id = file_spec.mFileID; - std::string extension = get_extension(file_spec.mFileType); - LL_INFOS() << " File: " << id - << " Type: " << LLAssetType::getDesc(file_spec.mFileType) - << " Size: " << size - << LL_ENDL; - } - } - - unlockData(); -} - -#include "llapr.h" -void LLVFS::dumpFiles() -{ - lockData(); - - S32 files_extracted = 0; - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileSpecifier file_spec = it->first; - LLVFSFileBlock *file_block = it->second; - S32 length = file_block->mLength; - S32 size = file_block->mSize; - if (length != BLOCK_LENGTH_INVALID && size > 0) - { - LLUUID id = file_spec.mFileID; - LLAssetType::EType type = file_spec.mFileType; - std::vector buffer(size); - - unlockData(); - getData(id, type, &buffer[0], 0, size); - lockData(); - - std::string extension = get_extension(type); - std::string filename = id.asString() + extension; - LL_INFOS() << " Writing " << filename << LL_ENDL; - - LLAPRFile outfile; - outfile.open(filename, LL_APR_WB); - outfile.write(&buffer[0], size); - outfile.close(); - - files_extracted++; - } - } - - unlockData(); - - LL_INFOS() << "Extracted " << files_extracted << " files out of " << mFileBlocks.size() << LL_ENDL; -} - -time_t LLVFS::creationTime() -{ - llstat data_file_stat; - int errors = LLFile::stat(mDataFilename, &data_file_stat); - if (0 == errors) - { - time_t creation_time = data_file_stat.st_ctime; -#if LL_DARWIN - creation_time = data_file_stat.st_birthtime; -#endif - return creation_time; - } - return 0; -} - -//============================================================================ -// protected -//============================================================================ - -// static -LLFILE *LLVFS::openAndLock(const std::string& filename, const char* mode, BOOL read_lock) -{ -#if LL_WINDOWS - - return LLFile::_fsopen(filename, mode, (read_lock ? _SH_DENYWR : _SH_DENYRW)); - -#else - - LLFILE *fp; - int fd; - - // first test the lock in a non-destructive way -#if LL_SOLARIS - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 1; -#else // !LL_SOLARIS - if (strchr(mode, 'w') != NULL) - { - fp = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */ - if (fp) - { - fd = fileno(fp); - if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1) - { - fclose(fp); - return NULL; - } - - fclose(fp); - } - } -#endif // !LL_SOLARIS - - // now actually open the file for use - fp = LLFile::fopen(filename, mode); /* Flawfinder: ignore */ - if (fp) - { - fd = fileno(fp); -#if LL_SOLARIS - fl.l_type = read_lock ? F_RDLCK : F_WRLCK; - if (fcntl(fd, F_SETLK, &fl) == -1) -#else - if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1) -#endif - { - fclose(fp); - fp = NULL; - } - } - - return fp; - -#endif -} - -// static -void LLVFS::unlockAndClose(LLFILE *fp) -{ - if (fp) - { - // IW: we don't actually want to unlock on linux - // this is because a forked process can kill the parent's lock - // with an explicit unlock - // however, fclose() will implicitly remove the lock - // but only once both parent and child have closed the file - /* - #if !LL_WINDOWS - int fd = fileno(fp); - flock(fd, LOCK_UN); - #endif - */ -#if LL_SOLARIS - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 1; - fl.l_type = F_UNLCK; - fcntl(fileno(fp), F_SETLK, &fl); -#endif - fclose(fp); - } -} diff --git a/indra/llvfs/llvfs.h b/indra/llvfs/llvfs.h deleted file mode 100644 index 42feafe20b..0000000000 --- a/indra/llvfs/llvfs.h +++ /dev/null @@ -1,183 +0,0 @@ -/** - * @file llvfs.h - * @brief Definition of virtual file system - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLVFS_H -#define LL_LLVFS_H - -#include -#include "lluuid.h" -#include "llassettype.h" -#include "llthread.h" -#include "llmutex.h" - -enum EVFSValid -{ - VFSVALID_UNKNOWN = 0, - VFSVALID_OK = 1, - VFSVALID_BAD_CORRUPT = 2, - VFSVALID_BAD_CANNOT_OPEN_READONLY = 3, - VFSVALID_BAD_CANNOT_CREATE = 4 -}; - -// Lock types for open vfiles, pending async reads, and pending async appends -// (There are no async normal writes, currently) -enum EVFSLock -{ - VFSLOCK_OPEN = 0, - VFSLOCK_READ = 1, - VFSLOCK_APPEND = 2, - - VFSLOCK_COUNT = 3 -}; - -// internal classes -class LLVFSBlock; -class LLVFSFileBlock; -class LLVFSFileSpecifier -{ -public: - LLVFSFileSpecifier(); - LLVFSFileSpecifier(const LLUUID &file_id, const LLAssetType::EType file_type); - bool operator<(const LLVFSFileSpecifier &rhs) const; - bool operator==(const LLVFSFileSpecifier &rhs) const; - -public: - LLUUID mFileID; - LLAssetType::EType mFileType; -}; - -class LLVFS -{ -private: - // Use createLLVFS() to open a VFS file - // Pass 0 to not presize - LLVFS(const std::string& index_filename, - const std::string& data_filename, - const BOOL read_only, - const U32 presize, - const BOOL remove_after_crash); -public: - ~LLVFS(); - - // Use this function normally to create LLVFS files - // Pass 0 to not presize - static LLVFS * createLLVFS(const std::string& index_filename, - const std::string& data_filename, - const BOOL read_only, - const U32 presize, - const BOOL remove_after_crash); - - BOOL isValid() const { return (VFSVALID_OK == mValid); } - EVFSValid getValidState() const { return mValid; } - - // ---------- The following fucntions lock/unlock mDataMutex ---------- - BOOL getExists(const LLUUID &file_id, const LLAssetType::EType file_type); - S32 getSize(const LLUUID &file_id, const LLAssetType::EType file_type); - - BOOL checkAvailable(S32 max_size); - - S32 getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type); - BOOL setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type, S32 max_size); - - void renameFile(const LLUUID &file_id, const LLAssetType::EType file_type, - const LLUUID &new_id, const LLAssetType::EType &new_type); - void removeFile(const LLUUID &file_id, const LLAssetType::EType file_type); - - S32 getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 *buffer, S32 location, S32 length); - S32 storeData(const LLUUID &file_id, const LLAssetType::EType file_type, const U8 *buffer, S32 location, S32 length); - - void incLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock); - void decLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock); - BOOL isLocked(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock); - // ---------------------------------------------------------------- - - // Used to trigger evil WinXP behavior of "preloading" entire file into memory. - void pokeFiles(); - - // Verify that the index file contents match the in-memory file structure - // Very slow, do not call routinely. JC - void audit(); - // Check for uninitialized blocks. Slow, do not call in release. JC - void checkMem(); - // for debugging, prints a map of the vfs - void dumpMap(); - void dumpLockCounts(); - void dumpStatistics(); - void listFiles(); - void dumpFiles(); - time_t creationTime(); - -protected: - void removeFileBlock(LLVFSFileBlock *fileblock); - - void eraseBlockLength(LLVFSBlock *block); - void eraseBlock(LLVFSBlock *block); - void addFreeBlock(LLVFSBlock *block); - //void mergeFreeBlocks(); - void useFreeSpace(LLVFSBlock *free_block, S32 length); - void sync(LLVFSFileBlock *block, BOOL remove = FALSE); - void presizeDataFile(const U32 size); - - static LLFILE *openAndLock(const std::string& filename, const char* mode, BOOL read_lock); - static void unlockAndClose(FILE *fp); - - // Can initiate LRU-based file removal to make space. - // The immune file block will not be removed. - LLVFSBlock *findFreeBlock(S32 size, LLVFSFileBlock *immune = NULL); - - // lock/unlock data mutex (mDataMutex) - void lockData() { mDataMutex->lock(); } - void unlockData() { mDataMutex->unlock(); } - -protected: - LLMutex* mDataMutex; - - typedef std::map fileblock_map; - fileblock_map mFileBlocks; - - typedef std::multimap blocks_length_map_t; - blocks_length_map_t mFreeBlocksByLength; - typedef std::multimap blocks_location_map_t; - blocks_location_map_t mFreeBlocksByLocation; - - LLFILE *mDataFP; - LLFILE *mIndexFP; - - std::deque mIndexHoles; - - std::string mIndexFilename; - std::string mDataFilename; - BOOL mReadOnly; - - EVFSValid mValid; - - S32 mLockCounts[VFSLOCK_COUNT]; - BOOL mRemoveAfterCrash; -}; - -extern LLVFS *gVFS; - -#endif diff --git a/indra/llvfs/llvfsthread.cpp b/indra/llvfs/llvfsthread.cpp deleted file mode 100644 index 8cd85929e2..0000000000 --- a/indra/llvfs/llvfsthread.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/** - * @file llvfsthread.cpp - * @brief LLVFSThread implementation - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llvfsthread.h" -#include "llstl.h" - -//============================================================================ - -/*static*/ std::string LLVFSThread::sDataPath = ""; - -/*static*/ LLVFSThread* LLVFSThread::sLocal = NULL; - -//============================================================================ -// Run on MAIN thread -//static -void LLVFSThread::initClass(bool local_is_threaded) -{ - llassert(sLocal == NULL); - sLocal = new LLVFSThread(local_is_threaded); -} - -//static -S32 LLVFSThread::updateClass(U32 ms_elapsed) -{ - sLocal->update((F32)ms_elapsed); - return sLocal->getPending(); -} - -//static -void LLVFSThread::cleanupClass() -{ - sLocal->setQuitting(); - while (sLocal->getPending()) - { - sLocal->update(0); - } - delete sLocal; - sLocal = 0; -} - -//---------------------------------------------------------------------------- - -LLVFSThread::LLVFSThread(bool threaded) : - LLQueuedThread("VFS", threaded) -{ -} - -LLVFSThread::~LLVFSThread() -{ - // ~LLQueuedThread() will be called here -} - -//---------------------------------------------------------------------------- - -LLVFSThread::handle_t LLVFSThread::read(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes, U32 priority, U32 flags) -{ - handle_t handle = generateHandle(); - - priority = llmax(priority, (U32)PRIORITY_LOW); // All reads are at least PRIORITY_LOW - Request* req = new Request(handle, priority, flags, FILE_READ, vfs, file_id, file_type, - buffer, offset, numbytes); - - bool res = addRequest(req); - if (!res) - { - LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; - req->deleteRequest(); - handle = nullHandle(); - } - - return handle; -} - -S32 LLVFSThread::readImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes) -{ - handle_t handle = generateHandle(); - - Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0, FILE_READ, vfs, file_id, file_type, - buffer, offset, numbytes); - - S32 res = addRequest(req) ? 1 : 0; - if (res == 0) - { - LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; - req->deleteRequest(); - } - else - { - llverify(waitForResult(handle, false) == true); - res = req->getBytesRead(); - completeRequest(handle); - } - return res; -} - -LLVFSThread::handle_t LLVFSThread::write(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes, U32 flags) -{ - handle_t handle = generateHandle(); - - Request* req = new Request(handle, 0, flags, FILE_WRITE, vfs, file_id, file_type, - buffer, offset, numbytes); - - bool res = addRequest(req); - if (!res) - { - LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; - req->deleteRequest(); - handle = nullHandle(); - } - - return handle; -} - -S32 LLVFSThread::writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes) -{ - handle_t handle = generateHandle(); - - Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0, FILE_WRITE, vfs, file_id, file_type, - buffer, offset, numbytes); - - S32 res = addRequest(req) ? 1 : 0; - if (res == 0) - { - LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; - req->deleteRequest(); - } - else - { - llverify(waitForResult(handle, false) == true); - res = req->getBytesRead(); - completeRequest(handle); - } - return res; -} - - -// LLVFSThread::handle_t LLVFSThread::rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, -// const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags) -// { -// handle_t handle = generateHandle(); - -// LLUUID* new_idp = new LLUUID(new_id); // deleted with Request -// // new_type is passed as "numbytes" -// Request* req = new Request(handle, 0, flags, FILE_RENAME, vfs, file_id, file_type, -// (U8*)new_idp, 0, (S32)new_type); - -// bool res = addRequest(req); -// if (!res) -// { -// LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; -// req->deleteRequest(); -// handle = nullHandle(); -// } - -// return handle; -// } - -//============================================================================ - -LLVFSThread::Request::Request(handle_t handle, U32 priority, U32 flags, - operation_t op, LLVFS* vfs, - const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes) : - QueuedRequest(handle, priority, flags), - mOperation(op), - mVFS(vfs), - mFileID(file_id), - mFileType(file_type), - mBuffer(buffer), - mOffset(offset), - mBytes(numbytes), - mBytesRead(0) -{ - llassert(mBuffer); - - if (numbytes <= 0 && mOperation != FILE_RENAME) - { - LL_WARNS() << "LLVFSThread: Request with numbytes = " << numbytes - << " operation = " << op - << " offset " << offset - << " file_type " << file_type << LL_ENDL; - } - if (mOperation == FILE_WRITE) - { - S32 blocksize = mVFS->getMaxSize(mFileID, mFileType); - if (blocksize < 0) - { - LL_WARNS() << "VFS write to temporary block (shouldn't happen)" << LL_ENDL; - } - mVFS->incLock(mFileID, mFileType, VFSLOCK_APPEND); - } - else if (mOperation == FILE_RENAME) - { - mVFS->incLock(mFileID, mFileType, VFSLOCK_APPEND); - } - else // if (mOperation == FILE_READ) - { - mVFS->incLock(mFileID, mFileType, VFSLOCK_READ); - } -} - -// dec locks as soon as a request finishes -void LLVFSThread::Request::finishRequest(bool completed) -{ - if (mOperation == FILE_WRITE) - { - mVFS->decLock(mFileID, mFileType, VFSLOCK_APPEND); - } - else if (mOperation == FILE_RENAME) - { - mVFS->decLock(mFileID, mFileType, VFSLOCK_APPEND); - } - else // if (mOperation == FILE_READ) - { - mVFS->decLock(mFileID, mFileType, VFSLOCK_READ); - } -} - -void LLVFSThread::Request::deleteRequest() -{ - if (getStatus() == STATUS_QUEUED) - { - LL_ERRS() << "Attempt to delete a queued LLVFSThread::Request!" << LL_ENDL; - } - if (mOperation == FILE_WRITE) - { - if (mFlags & FLAG_AUTO_DELETE) - { - delete [] mBuffer; - } - } - else if (mOperation == FILE_RENAME) - { - LLUUID* new_idp = (LLUUID*)mBuffer; - delete new_idp; - } - LLQueuedThread::QueuedRequest::deleteRequest(); -} - -bool LLVFSThread::Request::processRequest() -{ - bool complete = false; - if (mOperation == FILE_READ) - { - llassert(mOffset >= 0); - mBytesRead = mVFS->getData(mFileID, mFileType, mBuffer, mOffset, mBytes); - complete = true; - //LL_INFOS() << llformat("LLVFSThread::READ '%s': %d bytes arg:%d",getFilename(),mBytesRead) << LL_ENDL; - } - else if (mOperation == FILE_WRITE) - { - mBytesRead = mVFS->storeData(mFileID, mFileType, mBuffer, mOffset, mBytes); - complete = true; - //LL_INFOS() << llformat("LLVFSThread::WRITE '%s': %d bytes arg:%d",getFilename(),mBytesRead) << LL_ENDL; - } - else if (mOperation == FILE_RENAME) - { - LLUUID* new_idp = (LLUUID*)mBuffer; - LLAssetType::EType new_type = (LLAssetType::EType)mBytes; - mVFS->renameFile(mFileID, mFileType, *new_idp, new_type); - mFileID = *new_idp; - complete = true; - //LL_INFOS() << llformat("LLVFSThread::RENAME '%s': %d bytes arg:%d",getFilename(),mBytesRead) << LL_ENDL; - } - else - { - LL_ERRS() << llformat("LLVFSThread::unknown operation: %d", mOperation) << LL_ENDL; - } - return complete; -} - -//============================================================================ diff --git a/indra/llvfs/llvfsthread.h b/indra/llvfs/llvfsthread.h deleted file mode 100644 index 7814de4a2d..0000000000 --- a/indra/llvfs/llvfsthread.h +++ /dev/null @@ -1,140 +0,0 @@ -/** - * @file llvfsthread.h - * @brief LLVFSThread definition - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLVFSTHREAD_H -#define LL_LLVFSTHREAD_H - -#include -#include -#include -#include - -#include "llqueuedthread.h" - -#include "llvfs.h" - -//============================================================================ - -class LLVFSThread : public LLQueuedThread -{ - //------------------------------------------------------------------------ -public: - enum operation_t { - FILE_READ, - FILE_WRITE, - FILE_RENAME - }; - - //------------------------------------------------------------------------ -public: - - class Request : public QueuedRequest - { - protected: - ~Request() {}; // use deleteRequest() - - public: - Request(handle_t handle, U32 priority, U32 flags, - operation_t op, LLVFS* vfs, - const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes); - - S32 getBytesRead() - { - return mBytesRead; - } - S32 getOperation() - { - return mOperation; - } - U8* getBuffer() - { - return mBuffer; - } - LLVFS* getVFS() - { - return mVFS; - } - std::string getFilename() - { - std::string tstring; - mFileID.toString(tstring); - return tstring; - } - - /*virtual*/ bool processRequest(); - /*virtual*/ void finishRequest(bool completed); - /*virtual*/ void deleteRequest(); - - private: - operation_t mOperation; - - LLVFS* mVFS; - LLUUID mFileID; - LLAssetType::EType mFileType; - - U8* mBuffer; // dest for reads, source for writes, new UUID for rename - S32 mOffset; // offset into file, -1 = append (WRITE only) - S32 mBytes; // bytes to read from file, -1 = all (new mFileType for rename) - S32 mBytesRead; // bytes read from file - }; - - //------------------------------------------------------------------------ -public: - static std::string sDataPath; - static LLVFSThread* sLocal; // Default worker thread - -public: - LLVFSThread(bool threaded = TRUE); - ~LLVFSThread(); - - // Return a Request handle - handle_t read(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, /* Flawfinder: ignore */ - U8* buffer, S32 offset, S32 numbytes, U32 pri=PRIORITY_NORMAL, U32 flags = 0); - handle_t write(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes, U32 flags); - // SJB: rename seems to have issues, especially when threaded -// handle_t rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, -// const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags); - // Return number of bytes read - S32 readImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes); - S32 writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes); - - /*virtual*/ bool processRequest(QueuedRequest* req); - -public: - static void initClass(bool local_is_threaded = TRUE); // Setup sLocal - static S32 updateClass(U32 ms_elapsed); - static void cleanupClass(); // Delete sLocal - static void setDataPath(const std::string& path) { sDataPath = path; } -}; - -//============================================================================ - - -#endif // LL_LLVFSTHREAD_H diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 8bfb23ed64..70eb99c86c 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -16,7 +16,7 @@ include(LLCommon) include(LLImage) include(LLMath) include(LLRender) -include(LLVFS) +include(LLFileSystem) include(LLWindow) include(LLXML) include(UI) @@ -26,7 +26,7 @@ include_directories( ${LLIMAGE_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) @@ -72,7 +72,7 @@ if (LINUX) ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLRENDER_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLWINDOW_LIBRARIES} ${LLXML_LIBRARIES} ${UI_LIBRARIES} # for GTK @@ -95,7 +95,7 @@ if (LINUX) ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLRENDER_HEADLESS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLWINDOW_HEADLESS_LIBRARIES} ${LLXML_LIBRARIES} fontconfig # For FCInit and other FC* functions. diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt index 013a422d35..3a7a54e51d 100644 --- a/indra/llxml/CMakeLists.txt +++ b/indra/llxml/CMakeLists.txt @@ -5,13 +5,13 @@ project(llxml) include(00-Common) include(LLCommon) include(LLMath) -include(LLVFS) +include(LLFileSystem) include(LLXML) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ) include_directories( ${LLCOMMON_SYSTEM_INCLUDE_DIRS} @@ -42,7 +42,7 @@ add_library (llxml ${llxml_SOURCE_FILES}) # Libraries on which this library depends, needed for Linux builds # Sort by high-level to low-level target_link_libraries( llxml - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${EXPAT_LIBRARIES} diff --git a/indra/mac_crash_logger/CMakeLists.txt b/indra/mac_crash_logger/CMakeLists.txt index 95637c9a28..75621d7b75 100644 --- a/indra/mac_crash_logger/CMakeLists.txt +++ b/indra/mac_crash_logger/CMakeLists.txt @@ -8,7 +8,7 @@ include(LLCoreHttp) include(LLCrashLogger) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLFilesystem) include(LLXML) include(Linking) include(LLSharedLibs) @@ -19,7 +19,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCRASHLOGGER_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) include_directories(SYSTEM @@ -68,11 +68,10 @@ find_library(COCOA_LIBRARY Cocoa) target_link_libraries(mac-crash-logger ${LLCRASHLOGGER_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${COCOA_LIBRARIES} ${LLXML_LIBRARIES} ${LLMESSAGE_LIBRARIES} - ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp index 54e41a1954..66d8cfa590 100644 --- a/indra/mac_crash_logger/mac_crash_logger.cpp +++ b/indra/mac_crash_logger/mac_crash_logger.cpp @@ -27,7 +27,6 @@ #include "linden_common.h" #include "llcrashloggermac.h" #include "indra_constants.h" -#include "llpidlock.h" #include diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 3439951e80..82e6cda193 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -39,7 +39,7 @@ include(LLPlugin) include(LLPrimitive) include(LLRender) include(LLUI) -include(LLVFS) +include(LLFileSystem) include(LLWindow) include(LLXML) include(NDOF) @@ -84,7 +84,7 @@ include_directories( ${LLPRIMITIVE_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLUI_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LLLOGIN_INCLUDE_DIRS} @@ -2016,7 +2016,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLRENDER_LIBRARIES} ${FREETYPE_LIBRARIES} ${LLUI_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLWINDOW_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} @@ -2492,7 +2492,7 @@ if (LL_TESTS) set(test_libs ${LLMESSAGE_LIBRARIES} ${WINDOWS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} @@ -2507,7 +2507,7 @@ if (LL_TESTS) set(test_libs ${WINDOWS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${LLMESSAGE_LIBRARIES} diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c0166f158e..393a38fb9c 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1362,6 +1362,39 @@ Value 23 + EnableCacheDebugInfo + + Comment + When set, display additional cache debugging information + Persist + 1 + Type + Boolean + Value + 0 + + DiskCachePercentOfTotal + + Comment + The percent of total cache size (defined by CacheSize) to use for the disk cache + Persist + 1 + Type + F32 + Value + 20.0 + + DiskCacheDirName + + Comment + The name of the disk cache (within the standard Viewer disk cache directory) + Persist + 1 + Type + String + Value + cache + CacheLocation Comment @@ -2835,17 +2868,6 @@ Value -1 - DebugStatModeVFSPendingOps - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - DebugStatModeTimeDialation Comment @@ -3638,17 +3660,6 @@ Value 4 - DumpVFSCaches - - Comment - Dump VFS caches on startup. - Persist - 1 - Type - Boolean - Value - 0 - DynamicCameraStrength Comment @@ -11580,7 +11591,7 @@ Boolean Value 0 - + NearbyListShowMap Comment @@ -14258,28 +14269,6 @@ Value - VFSOldSize - - Comment - [DO NOT MODIFY] Controls resizing of local file cache - Persist - 1 - Type - U32 - Value - 0 - - VFSSalt - - Comment - [DO NOT MODIFY] Controls local file caching behavior - Persist - 1 - Type - U32 - Value - 1 - VelocityInterpolate Comment @@ -16621,7 +16610,7 @@ Type Boolean Value - 1 + 1 diff --git a/indra/newview/app_settings/static_data.db2 b/indra/newview/app_settings/static_data.db2 deleted file mode 100644 index f85aa81601787e0643162d85f1086f548577809d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 576578 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�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

iUO{A+_>y^r$H<>b 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�sEslb`{_o+p3MRGmyKk7StqW&>O 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 diff --git a/indra/newview/app_settings/static_index.db2 b/indra/newview/app_settings/static_index.db2 deleted file mode 100644 index a5440f96f2b16bd93bb9eeffe561ea1db8b93c25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9894 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

KmtC<(>d4qd0zj` zNBA}wKNj4ZH;-@6%SnwtK3avL$9m(m%?PchW{s<8AOuQ)3X=FMu|7gCWpaB{xKn&Y zgN8f)-NikUnAaM*SEqewW0L7zN#bo~1J3nYNWcgAvB2o9eCFlUmg6mEdVFTz#|dyB zMXZ_B_~(4*J;l1^-{~W{7IWNZ9q?|el_VBd5Fd1%)$0f3Ja~V$eTH>?X;!w=5u$=( Tu1Tg_{HGecd?G%d`H%P?%$X-n diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f518704e06..6ede9ec0e7 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -98,6 +98,7 @@ #include "lllogininstance.h" #include "llprogressview.h" #include "llvocache.h" +#include "lldiskcache.h" #include "llvopartgroup.h" #include "llweb.h" #include "llfloatertexturefetchdebugger.h" @@ -115,8 +116,6 @@ #include "llprimitive.h" #include "llurlaction.h" #include "llurlentry.h" -#include "llvfile.h" -#include "llvfsthread.h" #include "llvolumemgr.h" #include "llxfermanager.h" #include "llphysicsextensions.h" @@ -340,9 +339,6 @@ bool gUseWireframe = FALSE; //use for remember deferred mode in wireframe switch bool gInitialDeferredModeForWireframe = FALSE; -// VFS globals - see llappviewer.h -LLVFS* gStaticVFS = NULL; - LLMemoryInfo gSysMemory; U64Bytes gMemoryAllocated(0); // updated in display_stats() in llviewerdisplay.cpp @@ -431,12 +427,6 @@ void init_default_trans_args() default_trans_args.insert("create_account_url"); } -//---------------------------------------------------------------------------- -// File scope definitons -const char *VFS_DATA_FILE_BASE = "data.db2.x."; -const char *VFS_INDEX_FILE_BASE = "index.db2.x."; - - struct SettingsFile : public LLInitParam::Block { Mandatory name; @@ -971,10 +961,6 @@ bool LLAppViewer::init() // *Note: this is where gViewerStats used to be created. - // - // Initialize the VFS, and gracefully handle initialization errors - // - if (!initCache()) { LL_WARNS("InitInfo") << "Failed to init cache" << LL_ENDL; @@ -1369,7 +1355,6 @@ static LLTrace::BlockTimerStatHandle FTM_TEXTURE_CACHE("Texture Cache"); static LLTrace::BlockTimerStatHandle FTM_DECODE("Image Decode"); static LLTrace::BlockTimerStatHandle FTM_FETCH("Image Fetch"); -static LLTrace::BlockTimerStatHandle FTM_VFS("VFS Thread"); static LLTrace::BlockTimerStatHandle FTM_LFS("LFS Thread"); static LLTrace::BlockTimerStatHandle FTM_PAUSE_THREADS("Pause Threads"); static LLTrace::BlockTimerStatHandle FTM_IDLE("Idle"); @@ -1603,10 +1588,6 @@ bool LLAppViewer::doFrame() work_pending += updateTextureThreads(max_time); - { - LL_RECORD_BLOCK_TIME(FTM_VFS); - io_pending += LLVFSThread::updateClass(1); - } { LL_RECORD_BLOCK_TIME(FTM_LFS); io_pending += LLLFSThread::updateClass(1); @@ -1614,7 +1595,7 @@ bool LLAppViewer::doFrame() if (io_pending > 1000) { - ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up + ms_sleep(llmin(io_pending/100,100)); // give the lfs some time to catch up } total_work_pending += work_pending ; @@ -1631,7 +1612,6 @@ bool LLAppViewer::doFrame() } if(!total_io_pending) //pause file threads if nothing to process. { - LLVFSThread::sLocal->pause(); LLLFSThread::sLocal->pause(); } @@ -1693,12 +1673,11 @@ S32 LLAppViewer::updateTextureThreads(F32 max_time) return work_pending; } -void LLAppViewer::flushVFSIO() +void LLAppViewer::flushLFSIO() { while (1) { - S32 pending = LLVFSThread::updateClass(0); - pending += LLLFSThread::updateClass(0); + S32 pending = LLLFSThread::updateClass(0); if (!pending) { break; @@ -1786,7 +1765,7 @@ bool LLAppViewer::cleanup() LLKeyframeDataCache::clear(); - // End TransferManager before deleting systems it depends on (Audio, VFS, AssetStorage) + // End TransferManager before deleting systems it depends on (Audio, AssetStorage) #if 0 // this seems to get us stuck in an infinite loop... gTransferManager.cleanup(); #endif @@ -1853,8 +1832,8 @@ bool LLAppViewer::cleanup() LL_INFOS() << "Cache files removed" << LL_ENDL; - // Wait for any pending VFS IO - flushVFSIO(); + // Wait for any pending LFS IO + flushLFSIO(); LL_INFOS() << "Shutting down Views" << LL_ENDL; // Destroy the UI @@ -1938,15 +1917,6 @@ bool LLAppViewer::cleanup() SUBSYSTEM_CLEANUP(LLWorldMapView); SUBSYSTEM_CLEANUP(LLFolderViewItem); - // - // Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles). - // Also after viewerwindow is deleted, since it may have image pointers (which have vfiles) - // Also after shutting down the messaging system since it has VFS dependencies - - // - LL_INFOS() << "Cleaning up VFS" << LL_ENDL; - SUBSYSTEM_CLEANUP(LLVFile); - LL_INFOS() << "Saving Data" << LL_ENDL; // Store the time of our current logoff @@ -2033,7 +2003,6 @@ bool LLAppViewer::cleanup() pending += LLAppViewer::getTextureCache()->update(1); // unpauses the worker thread pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread - pending += LLVFSThread::updateClass(0); pending += LLLFSThread::updateClass(0); F64 idle_time = idleTimer.getElapsedTimeF64(); if(!pending) @@ -2108,28 +2077,11 @@ bool LLAppViewer::cleanup() gTextureList.shutdown(); // shutdown again in case a callback added something LLUIImageList::getInstance()->cleanUp(); - // This should eventually be done in LLAppViewer SUBSYSTEM_CLEANUP(LLImage); - SUBSYSTEM_CLEANUP(LLVFSThread); SUBSYSTEM_CLEANUP(LLLFSThread); -#ifndef LL_RELEASE_FOR_DOWNLOAD - LL_INFOS() << "Auditing VFS" << LL_ENDL; - if(gVFS) - { - gVFS->audit(); - } -#endif - LL_INFOS() << "Misc Cleanup" << LL_ENDL; - // For safety, the LLVFS has to be deleted *after* LLVFSThread. This should be cleaned up. - // (LLVFS doesn't know about LLVFSThread so can't kill pending requests) -Steve - delete gStaticVFS; - gStaticVFS = NULL; - delete gVFS; - gVFS = NULL; - gSavedSettings.cleanup(); LLUIColorTable::instance().clear(); @@ -2211,7 +2163,6 @@ bool LLAppViewer::initThreads() LLImage::initClass(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange")); - LLVFSThread::initClass(enable_threads && false); LLLFSThread::initClass(enable_threads && false); // Image decoding @@ -3213,10 +3164,6 @@ LLSD LLAppViewer::getViewerInfo() const info["GPU_SHADERS"] = gSavedSettings.getBOOL("RenderDeferred") ? "Enabled" : "Disabled"; info["TEXTURE_MEMORY"] = gSavedSettings.getS32("TextureMemory"); - LLSD substitution; - substitution["datetime"] = (S32)(gVFS ? gVFS->creationTime() : 0); - info["VFS_TIME"] = LLTrans::getString("AboutTime", substitution); - #if LL_DARWIN info["HIDPI"] = gHiDPISupport; #endif @@ -3308,6 +3255,9 @@ LLSD LLAppViewer::getViewerInfo() const info["SERVER_RELEASE_NOTES_URL"] = mServerReleaseNotesURL; } + // populate field for new local disk cache with some details + info["DISK_CACHE_INFO"] = LLDiskCache::getInstance()->getCacheInfo(); + return info; } @@ -3957,7 +3907,7 @@ void LLAppViewer::forceQuit() void LLAppViewer::fastQuit(S32 error_code) { // finish pending transfers - flushVFSIO(); + flushLFSIO(); // let sim know we're logging out sendLogoutRequest(); // flush network buffers by shutting down messaging system @@ -4146,39 +4096,6 @@ void LLAppViewer::migrateCacheDirectory() #endif // LL_WINDOWS || LL_DARWIN } -void dumpVFSCaches() -{ - LL_INFOS() << "======= Static VFS ========" << LL_ENDL; - gStaticVFS->listFiles(); -#if LL_WINDOWS - LL_INFOS() << "======= Dumping static VFS to StaticVFSDump ========" << LL_ENDL; - WCHAR w_str[MAX_PATH]; - GetCurrentDirectory(MAX_PATH, w_str); - S32 res = LLFile::mkdir("StaticVFSDump"); - if (res == -1) - { - LL_WARNS() << "Couldn't create dir StaticVFSDump" << LL_ENDL; - } - SetCurrentDirectory(utf8str_to_utf16str("StaticVFSDump").c_str()); - gStaticVFS->dumpFiles(); - SetCurrentDirectory(w_str); -#endif - - LL_INFOS() << "========= Dynamic VFS ====" << LL_ENDL; - gVFS->listFiles(); -#if LL_WINDOWS - LL_INFOS() << "========= Dumping dynamic VFS to VFSDump ====" << LL_ENDL; - res = LLFile::mkdir("VFSDump"); - if (res == -1) - { - LL_WARNS() << "Couldn't create dir VFSDump" << LL_ENDL; - } - SetCurrentDirectory(utf8str_to_utf16str("VFSDump").c_str()); - gVFS->dumpFiles(); - SetCurrentDirectory(w_str); -#endif -} - //static U32 LLAppViewer::getTextureCacheVersion() { @@ -4205,6 +4122,17 @@ bool LLAppViewer::initCache() LLAppViewer::getTextureCache()->setReadOnly(read_only) ; LLVOCache::initParamSingleton(read_only); + // initialize the new disk cache using saved settings + const std::string cache_dir_name = gSavedSettings.getString("DiskCacheDirName"); + + // note that the maximum size of this cache is defined as a percentage of the + // total cache size - the 'CacheSize' pref - for all caches. + const unsigned int cache_total_size_mb = gSavedSettings.getU32("CacheSize"); + const double disk_cache_percent = gSavedSettings.getF32("DiskCachePercentOfTotal"); + const unsigned int disk_cache_mb = cache_total_size_mb * disk_cache_percent / 100; + const unsigned int disk_cache_bytes = disk_cache_mb * 1024 * 1024; + const bool enable_cache_debug_info = gSavedSettings.getBOOL("EnableDiskCacheDebugInfo"); + bool texture_cache_mismatch = false; if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getTextureCacheVersion()) { @@ -4251,10 +4179,24 @@ bool LLAppViewer::initCache() gSavedSettings.setString("CacheLocationTopFolder", ""); } - if (mPurgeCache && !read_only) + const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); + LLDiskCache::initParamSingleton(cache_dir, disk_cache_bytes, enable_cache_debug_info); + + if (!read_only) { - LLSplashScreen::update(LLTrans::getString("StartupClearingCache")); - purgeCache(); + if (mPurgeCache) + { + LLSplashScreen::update(LLTrans::getString("StartupClearingCache")); + purgeCache(); + + // clear the new C++ file system based cache + LLDiskCache::getInstance()->clearCache(); + } + else + { + // purge excessive files from the new file system based cache + LLDiskCache::getInstance()->purge(); + } } LLSplashScreen::update(LLTrans::getString("StartupInitializingTextureCache")); @@ -4264,172 +4206,18 @@ bool LLAppViewer::initCache() const S32 MB = 1024 * 1024; const S64 MIN_CACHE_SIZE = 256 * MB; const S64 MAX_CACHE_SIZE = 9984ll * MB; - const S64 MAX_VFS_SIZE = 1024 * MB; // 1 GB S64 cache_size = (S64)(gSavedSettings.getU32("CacheSize")) * MB; cache_size = llclamp(cache_size, MIN_CACHE_SIZE, MAX_CACHE_SIZE); - S64 vfs_size = llmin((S64)((cache_size * 2) / 10), MAX_VFS_SIZE); - S64 texture_cache_size = cache_size - vfs_size; + S64 texture_cache_size = cache_size; S64 extra = LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, texture_cache_mismatch); texture_cache_size -= extra; - LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()); - LLSplashScreen::update(LLTrans::getString("StartupInitializingVFS")); - - // Init the VFS - vfs_size = llmin(vfs_size + extra, MAX_VFS_SIZE); - vfs_size = (vfs_size / MB) * MB; // make sure it is MB aligned - U32 vfs_size_u32 = (U32)vfs_size; - U32 old_vfs_size = gSavedSettings.getU32("VFSOldSize") * MB; - bool resize_vfs = (vfs_size_u32 != old_vfs_size); - if (resize_vfs) - { - gSavedSettings.setU32("VFSOldSize", vfs_size_u32 / MB); - } - LL_INFOS("AppCache") << "VFS CACHE SIZE: " << vfs_size / (1024*1024) << " MB" << LL_ENDL; - - // This has to happen BEFORE starting the vfs - // time_t ltime; - srand(time(NULL)); // Flawfinder: ignore - U32 old_salt = gSavedSettings.getU32("VFSSalt"); - U32 new_salt; - std::string old_vfs_data_file; - std::string old_vfs_index_file; - std::string new_vfs_data_file; - std::string new_vfs_index_file; - std::string static_vfs_index_file; - std::string static_vfs_data_file; - - if (gSavedSettings.getBOOL("AllowMultipleViewers")) - { - // don't mess with renaming the VFS in this case - new_salt = old_salt; - } - else - { - do - { - new_salt = rand(); - } while(new_salt == old_salt); - } - - old_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_DATA_FILE_BASE) + llformat("%u", old_salt); - - // make sure this file exists - llstat s; - S32 stat_result = LLFile::stat(old_vfs_data_file, &s); - if (stat_result) - { - // doesn't exist, look for a data file - std::string mask; - mask = VFS_DATA_FILE_BASE; - mask += "*"; - - std::string dir; - dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""); - - std::string found_file; - LLDirIterator iter(dir, mask); - if (iter.next(found_file)) - { - old_vfs_data_file = gDirUtilp->add(dir, found_file); - - S32 start_pos = found_file.find_last_of('.'); - if (start_pos > 0) - { - sscanf(found_file.substr(start_pos+1).c_str(), "%d", &old_salt); - } - LL_DEBUGS("AppCache") << "Default vfs data file not present, found: " << old_vfs_data_file << " Old salt: " << old_salt << LL_ENDL; - } - } - - old_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_INDEX_FILE_BASE) + llformat("%u", old_salt); - - stat_result = LLFile::stat(old_vfs_index_file, &s); - if (stat_result) - { - // We've got a bad/missing index file, nukem! - LL_WARNS("AppCache") << "Bad or missing vfx index file " << old_vfs_index_file << LL_ENDL; - LL_WARNS("AppCache") << "Removing old vfs data file " << old_vfs_data_file << LL_ENDL; - LLFile::remove(old_vfs_data_file); - LLFile::remove(old_vfs_index_file); - - // Just in case, nuke any other old cache files in the directory. - std::string dir; - dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""); - - std::string mask; - mask = VFS_DATA_FILE_BASE; - mask += "*"; - - gDirUtilp->deleteFilesInDir(dir, mask); - - mask = VFS_INDEX_FILE_BASE; - mask += "*"; - - gDirUtilp->deleteFilesInDir(dir, mask); - } - - new_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_DATA_FILE_BASE) + llformat("%u", new_salt); - new_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_INDEX_FILE_BASE) + llformat("%u", new_salt); - - static_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "static_data.db2"); - static_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "static_index.db2"); - - if (resize_vfs) - { - LL_DEBUGS("AppCache") << "Removing old vfs and re-sizing" << LL_ENDL; - - LLFile::remove(old_vfs_data_file); - LLFile::remove(old_vfs_index_file); - } - else if (old_salt != new_salt) - { - // move the vfs files to a new name before opening - LL_DEBUGS("AppCache") << "Renaming " << old_vfs_data_file << " to " << new_vfs_data_file << LL_ENDL; - LL_DEBUGS("AppCache") << "Renaming " << old_vfs_index_file << " to " << new_vfs_index_file << LL_ENDL; - LLFile::rename(old_vfs_data_file, new_vfs_data_file); - LLFile::rename(old_vfs_index_file, new_vfs_index_file); - } - - // Startup the VFS... - gSavedSettings.setU32("VFSSalt", new_salt); - - // Don't remove VFS after viewer crashes. If user has corrupt data, they can reinstall. JC - gVFS = LLVFS::createLLVFS(new_vfs_index_file, new_vfs_data_file, false, vfs_size_u32, false); - if (!gVFS) - { - return false; - } - - gStaticVFS = LLVFS::createLLVFS(static_vfs_index_file, static_vfs_data_file, true, 0, false); - if (!gStaticVFS) - { - return false; - } - - BOOL success = gVFS->isValid() && gStaticVFS->isValid(); - if (!success) - { - return false; - } - else - { - LLVFile::initClass(); - -#ifndef LL_RELEASE_FOR_DOWNLOAD - if (gSavedSettings.getBOOL("DumpVFSCaches")) - { - dumpVFSCaches(); - } -#endif - - return true; - } + return true; } void LLAppViewer::addOnIdleCallback(const boost::function& cb) diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 5332fe2deb..902b94d495 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -1,4 +1,5 @@ /** + * @mainpage * @mainpage * * This is the sources for the Second Life Viewer; @@ -82,7 +83,7 @@ public: virtual bool frame(); // Override for application body logic // Application control - void flushVFSIO(); // waits for vfs transfers to complete + void flushLFSIO(); // waits for lfs transfers to complete void forceQuit(); // Puts the viewer into 'shutting down without error' mode. void fastQuit(S32 error_code = 0); // Shuts down the viewer immediately after sending a logout message void requestQuit(); // Request a quit. A kinder, gentler quit. @@ -381,12 +382,6 @@ extern BOOL gRestoreGL; extern bool gUseWireframe; extern bool gInitialDeferredModeForWireframe; -// VFS globals - gVFS is for general use -// gStaticVFS is read-only and is shipped w/ the viewer -// it has pre-cache data like the UI .TGAs -class LLVFS; -extern LLVFS *gStaticVFS; - extern LLMemoryInfo gSysMemory; extern U64Bytes gMemoryAllocated; diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 9b1c0d1f8b..84ffb3551d 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -502,7 +502,7 @@ void LLAppViewerWin32::disableWinErrorReporting() } } -const S32 MAX_CONSOLE_LINES = 500; +const S32 MAX_CONSOLE_LINES = 7500; // Only defined in newer SDKs than we currently use #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 4 diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index 3aaaaf52f5..5d010a6f1e 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -52,7 +52,7 @@ #include "lldir.h" #include "llnotificationsutil.h" #include "llviewerstats.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "lluictrlfactory.h" #include "lltrans.h" @@ -116,7 +116,7 @@ namespace } // *NOTE$: A minor specialization of LLScriptAssetUpload, it does not require a buffer -// (and does not save a buffer to the vFS) and it finds the compile queue window and +// (and does not save a buffer to the cache) and it finds the compile queue window and // displays a compiling message. class LLQueuedScriptAssetUpload : public LLScriptAssetUpload { @@ -134,8 +134,8 @@ public: virtual LLSD prepareUpload() { /* *NOTE$: The parent class (LLScriptAssetUpload will attempt to save - * the script buffer into to the VFS. Since the resource is already in - * the VFS we don't want to do that. Just put a compiling message in + * the script buffer into to the cache. Since the resource is already in + * the cache we don't want to do that. Just put a compiling message in * the window and move on */ LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance("compile_queue", LLSD(mQueueId)); @@ -283,11 +283,11 @@ void LLFloaterCompileQueue::handleHTTPResponse(std::string pumpName, const LLSD LLEventPumps::instance().post(pumpName, expresult); } -// *TODO: handleSCriptRetrieval is passed into the VFS via a legacy C function pointer +// *TODO: handleSCriptRetrieval is passed into the cache via a legacy C function pointer // future project would be to convert these to C++ callables (std::function<>) so that // we can use bind and remove the userData parameter. // -void LLFloaterCompileQueue::handleScriptRetrieval(LLVFS *vfs, const LLUUID& assetId, +void LLFloaterCompileQueue::handleScriptRetrieval(const LLUUID& assetId, LLAssetType::EType type, void* userData, S32 status, LLExtStat extStatus) { LLSD result(LLSD::emptyMap()); diff --git a/indra/newview/llcompilequeue.h b/indra/newview/llcompilequeue.h index adb854875a..a9bac345b5 100644 --- a/indra/newview/llcompilequeue.h +++ b/indra/newview/llcompilequeue.h @@ -135,7 +135,7 @@ protected: //bool checkAssetId(const LLUUID &assetId); static void handleHTTPResponse(std::string pumpName, const LLSD &expresult); - static void handleScriptRetrieval(LLVFS *vfs, const LLUUID& assetId, LLAssetType::EType type, void* userData, S32 status, LLExtStat extStatus); + static void handleScriptRetrieval(const LLUUID& assetId, LLAssetType::EType type, void* userData, S32 status, LLExtStat extStatus); private: static void processExperienceIdResults(LLSD result, LLUUID parent); diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index 2fc496a144..04ba4416d7 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -136,7 +136,7 @@ public: S32 getFileCount() const { return (S32)mFiles.size(); } - // See llvfs/lldir.h : getBaseFileName and getDirName to extract base or directory names + // see lldir.h : getBaseFileName and getDirName to extract base or directory names // clear any lists of buffers or whatever, and make sure the file // picker isn't locked. diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index 957b2e1e8e..9813156bf2 100644 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -32,8 +32,7 @@ #include "llimagej2c.h" #include "llimagetga.h" #include "llparcel.h" -#include "llvfile.h" -#include "llvfs.h" +#include "llfilesystem.h" #include "llwindow.h" #include "message.h" @@ -202,7 +201,9 @@ void LLFloaterAuction::onClickSnapshot(void* data) LLPointer tga = new LLImageTGA; tga->encode(raw); - LLVFile::writeFile(tga->getData(), tga->getDataSize(), gVFS, self->mImageID, LLAssetType::AT_IMAGE_TGA); + + LLFileSystem tga_file(self->mImageID, LLAssetType::AT_IMAGE_TGA, LLFileSystem::WRITE); + tga_file.write(tga->getData(), tga->getDataSize()); raw->biasedScaleToPowerOfTwo(LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT); @@ -210,7 +211,9 @@ void LLFloaterAuction::onClickSnapshot(void* data) LLPointer j2c = new LLImageJ2C; j2c->encode(raw, 0.0f); - LLVFile::writeFile(j2c->getData(), j2c->getDataSize(), gVFS, self->mImageID, LLAssetType::AT_TEXTURE); + + LLFileSystem j2c_file(self->mImageID, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE); + j2c_file.write(j2c->getData(), j2c->getDataSize()); self->mImage = LLViewerTextureManager::getLocalTexture((LLImageRaw*)raw, FALSE); gGL.getTexUnit(0)->bind(self->mImage); diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index 131d9b077b..687d820a18 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -32,7 +32,7 @@ #include "lldatapacker.h" #include "lldir.h" #include "llnotificationsutil.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "llapr.h" #include "llstring.h" @@ -997,10 +997,9 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata) LLDataPackerBinaryBuffer dp(buffer, file_size); if (motionp->serialize(dp)) { - LLVFile file(gVFS, motionp->getID(), LLAssetType::AT_ANIMATION, LLVFile::APPEND); + LLFileSystem file(motionp->getID(), LLAssetType::AT_ANIMATION, LLFileSystem::APPEND); S32 size = dp.getCurrentSize(); - file.setMaxSize(size); if (file.write((U8*)buffer, size)) { std::string name = floaterp->getChild("name_form")->getValue().asString(); diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index b9c03f66a3..481a7dab66 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -59,6 +59,7 @@ #include "llspinctrl.h" #include "lltabcontainer.h" #include "lltrans.h" +#include "llfilesystem.h" #include "llcallbacklist.h" #include "llviewertexteditor.h" #include "llviewernetwork.h" diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 526214a617..f99d0e6150 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -107,7 +107,7 @@ protected: void onBtnOK(const LLSD& userdata); void onBtnCancel(const LLSD& userdata); - void onClickClearCache(); // Clear viewer texture cache, vfs, and VO cache on next startup + void onClickClearCache(); // Clear viewer texture cache, file cache on next startup void onClickBrowserClearCache(); // Clear web history and caches as well as viewer caches above void onLanguageChange(); void onNotificationsChange(const std::string& OptionName); diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index ec1909d02a..0375c15467 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -36,7 +36,7 @@ #include "llglheaders.h" #include "llregionflags.h" #include "llstl.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "llxfermanager.h" #include "indra_constants.h" #include "message.h" @@ -2229,10 +2229,9 @@ void LLPanelEstateCovenant::loadInvItem(LLInventoryItem *itemp) } // static -void LLPanelEstateCovenant::onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void LLPanelEstateCovenant::onLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LL_INFOS() << "LLPanelEstateCovenant::onLoadComplete()" << LL_ENDL; LLPanelEstateCovenant* panelp = (LLPanelEstateCovenant*)user_data; @@ -2240,7 +2239,7 @@ void LLPanelEstateCovenant::onLoadComplete(LLVFS *vfs, { if(0 == status) { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 file_length = file.getSize(); diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index 75d0c3ea5c..c34dbb62e8 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -55,7 +55,6 @@ class LLRadioGroup; class LLSliderCtrl; class LLSpinCtrl; class LLTextBox; -class LLVFS; class LLPanelRegionGeneralInfo; class LLPanelRegionDebugInfo; @@ -357,8 +356,7 @@ public: static bool confirmResetCovenantCallback(const LLSD& notification, const LLSD& response); void sendChangeCovenantID(const LLUUID &asset_id); void loadInvItem(LLInventoryItem *itemp); - static void onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, + static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 7bfba2a6d7..5d0e2bbc55 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -44,8 +44,7 @@ #include "llnotificationsutil.h" #include "llstring.h" #include "llsys.h" -#include "llvfile.h" -#include "llvfs.h" +#include "llfilesystem.h" #include "mean_collision_data.h" #include "message.h" #include "v3math.h" @@ -899,12 +898,9 @@ void LLFloaterReporter::takeScreenshot(bool use_prev_screenshot) mResourceDatap->mAssetInfo.setName("screenshot_name"); mResourceDatap->mAssetInfo.setDescription("screenshot_descr"); - // store in VFS - LLVFile::writeFile(upload_data->getData(), - upload_data->getDataSize(), - gVFS, - mResourceDatap->mAssetInfo.mUuid, - mResourceDatap->mAssetInfo.mType); + // store in cache + LLFileSystem j2c_file(mResourceDatap->mAssetInfo.mUuid, mResourceDatap->mAssetInfo.mType, LLFileSystem::WRITE); + j2c_file.write(upload_data->getData(), upload_data->getDataSize()); // store in the image list so it doesn't try to fetch from the server LLPointer image_in_list = diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp index bd403f68d7..1aeb727172 100644 --- a/indra/newview/llfloatertos.cpp +++ b/indra/newview/llfloatertos.cpp @@ -40,7 +40,7 @@ #include "lltextbox.h" #include "llui.h" #include "lluictrlfactory.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "message.h" #include "llstartup.h" // login_alert_done #include "llcorehttputil.h" diff --git a/indra/newview/llfloatertos.h b/indra/newview/llfloatertos.h index 85033acf4d..7c2f0705b7 100644 --- a/indra/newview/llfloatertos.h +++ b/indra/newview/llfloatertos.h @@ -36,7 +36,6 @@ class LLButton; class LLRadioGroup; -class LLVFS; class LLTextEditor; class LLUUID; diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 950a6cfaef..9f2119281d 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -42,7 +42,7 @@ #include "llnotificationsutil.h" #include "llstl.h" #include "llstring.h" // todo: remove -#include "llvfile.h" +#include "llfilesystem.h" #include "message.h" // newview @@ -548,7 +548,7 @@ void LLGestureMgr::playGesture(LLMultiGesture* gesture) LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; const LLUUID& anim_id = anim_step->mAnimAssetID; - // Don't request the animation if this step stops it or if it is already in Static VFS + // Don't request the animation if this step stops it or if it is already in the cache if (!(anim_id.isNull() || anim_step->mFlags & ANIM_FLAG_STOP || gAssetStorage->hasLocalAsset(anim_id, LLAssetType::AT_ANIMATION))) @@ -1038,10 +1038,9 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) // static -void LLGestureMgr::onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void LLGestureMgr::onLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LLLoadInfo* info = (LLLoadInfo*)user_data; @@ -1056,7 +1055,7 @@ void LLGestureMgr::onLoadComplete(LLVFS *vfs, if (0 == status) { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 size = file.getSize(); std::vector buffer(size+1); @@ -1159,8 +1158,7 @@ void LLGestureMgr::onLoadComplete(LLVFS *vfs, } // static -void LLGestureMgr::onAssetLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, +void LLGestureMgr::onAssetLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status) { @@ -1172,7 +1170,7 @@ void LLGestureMgr::onAssetLoadComplete(LLVFS *vfs, { case LLAssetType::AT_ANIMATION: { - LLKeyframeMotion::onLoadComplete(vfs, asset_uuid, type, user_data, status, ext_status); + LLKeyframeMotion::onLoadComplete(asset_uuid, type, user_data, status, ext_status); self.mLoadingAssets.erase(asset_uuid); @@ -1180,7 +1178,7 @@ void LLGestureMgr::onAssetLoadComplete(LLVFS *vfs, } case LLAssetType::AT_SOUND: { - LLAudioEngine::assetCallback(vfs, asset_uuid, type, user_data, status, ext_status); + LLAudioEngine::assetCallback(asset_uuid, type, user_data, status, ext_status); self.mLoadingAssets.erase(asset_uuid); diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h index 402bdf6039..91ab445273 100644 --- a/indra/newview/llgesturemgr.h +++ b/indra/newview/llgesturemgr.h @@ -40,7 +40,6 @@ class LLMultiGesture; class LLGestureListener; class LLGestureStep; class LLUUID; -class LLVFS; class LLGestureManagerObserver { @@ -154,15 +153,13 @@ protected: void done(); // Used by loadGesture - static void onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status); + static void onLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status); // Used by playGesture to load an asset file // required to play a gesture step - static void onAssetLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, + static void onAssetLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/lllandmarklist.cpp b/indra/newview/lllandmarklist.cpp index b4236c406b..2966ca1f10 100644 --- a/indra/newview/lllandmarklist.cpp +++ b/indra/newview/lllandmarklist.cpp @@ -33,7 +33,7 @@ #include "llappviewer.h" #include "llagent.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "llviewerstats.h" // Globals @@ -118,7 +118,6 @@ LLLandmark* LLLandmarkList::getAsset(const LLUUID& asset_uuid, loaded_callback_t // static void LLLandmarkList::processGetAssetReply( - LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, void* user_data, @@ -127,7 +126,7 @@ void LLLandmarkList::processGetAssetReply( { if( status == 0 ) { - LLVFile file(vfs, uuid, type); + LLFileSystem file(uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length + 1); diff --git a/indra/newview/lllandmarklist.h b/indra/newview/lllandmarklist.h index 2e7bd25610..0e4859dbc9 100644 --- a/indra/newview/lllandmarklist.h +++ b/indra/newview/lllandmarklist.h @@ -52,7 +52,6 @@ public: BOOL assetExists(const LLUUID& asset_uuid); LLLandmark* getAsset(const LLUUID& asset_uuid, loaded_callback_t cb = NULL); static void processGetAssetReply( - LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, void* user_data, diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 3e8731dfe6..8e5bdc0225 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -49,7 +49,7 @@ #include "llsdutil_math.h" #include "llsdserialize.h" #include "llthread.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewermenufile.h" @@ -294,8 +294,6 @@ // * Header parse failures come without much explanation. Elaborate. // * Work queue for uploads? Any need for this or is the current scheme good // enough? -// * Various temp buffers used in VFS I/O might be allocated once or even -// statically. Look for some wins here. // * Move data structures holding mesh data used by main thread into main- // thread-only access so that no locking is needed. May require duplication // of some data so that worker thread has a minimal data set to guide @@ -1336,8 +1334,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { - //check VFS for mesh skin info - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + //check cache for mesh skin info + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -1370,7 +1368,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) delete[] buffer; } - //reading from VFS failed for whatever reason, fetch from sim + //reading from cache failed for whatever reason, fetch from sim std::string http_url; constructUrl(mesh_id, &http_url); @@ -1432,8 +1430,8 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { - //check VFS for mesh skin info - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + //check cache for mesh skin info + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -1467,7 +1465,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) delete[] buffer; } - //reading from VFS failed for whatever reason, fetch from sim + //reading from cache failed for whatever reason, fetch from sim std::string http_url; constructUrl(mesh_id, &http_url); @@ -1529,8 +1527,8 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { - //check VFS for mesh physics shape info - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + //check cache for mesh physics shape info + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesRead += size; @@ -1563,7 +1561,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) delete[] buffer; } - //reading from VFS failed for whatever reason, fetch from sim + //reading from cache failed for whatever reason, fetch from sim std::string http_url; constructUrl(mesh_id, &http_url); @@ -1634,8 +1632,8 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c ++LLMeshRepository::sMeshRequestCount; { - //look for mesh in asset in vfs - LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH); + //look for mesh in asset in cache + LLFileSystem file(mesh_params.getSculptID(), LLAssetType::AT_MESH); S32 size = file.getSize(); @@ -1649,7 +1647,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c file.read(buffer, bytes); if (headerReceived(mesh_params, buffer, bytes) == MESH_OK) { - // Found mesh in VFS cache + // Found mesh in cache return true; } } @@ -1713,8 +1711,8 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { - //check VFS for mesh asset - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + //check cache for mesh asset + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -1749,7 +1747,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, delete[] buffer; } - //reading from VFS failed for whatever reason, fetch from sim + //reading from cache failed for whatever reason, fetch from sim std::string http_url; constructUrl(mesh_id, &http_url); @@ -3208,7 +3206,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b } else if (data && data_size > 0) { - // header was successfully retrieved from sim and parsed, cache in vfs + // header was successfully retrieved from sim and parsed and is in cache S32 header_bytes = 0; LLSD header; @@ -3247,31 +3245,16 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b // It's possible for the remote asset to have more data than is needed for the local cache - // only allocate as much space in the VFS as is needed for the local cache + // only allocate as much space in the cache as is needed for the local cache data_size = llmin(data_size, bytes); - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); - if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) + LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::WRITE); + if (file.getMaxSize() >= bytes) { LLMeshRepository::sCacheBytesWritten += data_size; ++LLMeshRepository::sCacheWrites; file.write(data, data_size); - - // zero out the rest of the file - U8 block[MESH_HEADER_SIZE]; - memset(block, 0, sizeof(block)); - - while (bytes-file.tell() > sizeof(block)) - { - file.write(block, sizeof(block)); - } - - S32 remaining = bytes-file.tell(); - if (remaining > 0) - { - file.write(block, remaining); - } } } else @@ -3323,8 +3306,8 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size); if (result == MESH_OK) { - // good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); + // good fetch from sim, write to cache + LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3387,8 +3370,8 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) { - // good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + // good fetch from sim, write to cache + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3435,8 +3418,8 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) { - // good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + // good fetch from sim, write to cache + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3482,8 +3465,8 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size) == MESH_OK) { - // good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + // good fetch from sim, write to cache for caching + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 81e49cb1d8..441264d42f 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -48,7 +48,6 @@ class LLVOVolume; class LLMutex; class LLCondition; -class LLVFS; class LLMeshRepository; typedef enum e_mesh_processing_result_enum diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index 272e7ae351..90f6d23a61 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -32,7 +32,7 @@ // llcommon #include "llcommonutils.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "llaccordionctrltab.h" #include "llappearancemgr.h" diff --git a/indra/newview/lloutfitgallery.h b/indra/newview/lloutfitgallery.h index 6dd8a6298f..ce5c090134 100644 --- a/indra/newview/lloutfitgallery.h +++ b/indra/newview/lloutfitgallery.h @@ -38,7 +38,6 @@ #include -class LLVFS; class LLOutfitGallery; class LLOutfitGalleryItem; class LLOutfitListGearMenuBase; diff --git a/indra/newview/llpostcard.cpp b/indra/newview/llpostcard.cpp index d5775042c1..071fc31d27 100644 --- a/indra/newview/llpostcard.cpp +++ b/indra/newview/llpostcard.cpp @@ -28,8 +28,7 @@ #include "llpostcard.h" -#include "llvfile.h" -#include "llvfs.h" +#include "llfilesystem.h" #include "llviewerregion.h" #include "message.h" diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 70ce275734..4318a55704 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -30,7 +30,7 @@ #include "llagent.h" #include "llanimstatelabels.h" #include "llanimationstates.h" -#include "llappviewer.h" // gVFS +#include "llappviewer.h" #include "llcheckboxctrl.h" #include "llcombobox.h" #include "lldatapacker.h" @@ -47,7 +47,7 @@ #include "llradiogroup.h" #include "llresmgr.h" #include "lltrans.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerstats.h" @@ -841,10 +841,9 @@ void LLPreviewGesture::loadAsset() // static -void LLPreviewGesture::onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void LLPreviewGesture::onLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LLUUID* item_idp = (LLUUID*)user_data; @@ -853,7 +852,7 @@ void LLPreviewGesture::onLoadComplete(LLVFS *vfs, { if (0 == status) { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 size = file.getSize(); std::vector buffer(size+1); @@ -1138,10 +1137,9 @@ void LLPreviewGesture::saveIfNeeded() tid.generate(); assetId = tid.makeAssetID(gAgent.getSecureSessionID()); - LLVFile file(gVFS, assetId, LLAssetType::AT_GESTURE, LLVFile::APPEND); + LLFileSystem file(assetId, LLAssetType::AT_GESTURE, LLFileSystem::APPEND); S32 size = dp.getCurrentSize(); - file.setMaxSize(size); file.write((U8*)buffer, size); LLLineEditor* descEditor = getChild("desc"); diff --git a/indra/newview/llpreviewgesture.h b/indra/newview/llpreviewgesture.h index 3ba4f56295..19bccf35bd 100644 --- a/indra/newview/llpreviewgesture.h +++ b/indra/newview/llpreviewgesture.h @@ -39,7 +39,6 @@ class LLScrollListCtrl; class LLScrollListItem; class LLButton; class LLRadioGroup; -class LLVFS; class LLPreviewGesture : public LLPreview { @@ -80,8 +79,7 @@ protected: void loadAsset(); - static void onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, + static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 1b60610668..a7bb5c8236 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -46,7 +46,7 @@ #include "llselectmgr.h" #include "lltrans.h" #include "llviewertexteditor.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "llviewerinventory.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -327,8 +327,7 @@ void LLPreviewNotecard::loadAsset() } // static -void LLPreviewNotecard::onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, +void LLPreviewNotecard::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status) { @@ -339,7 +338,7 @@ void LLPreviewNotecard::onLoadComplete(LLVFS *vfs, { if(0 == status) { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 file_length = file.getSize(); @@ -446,7 +445,7 @@ void LLPreviewNotecard::finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, LLPreviewNotecard* nc = LLFloaterReg::findTypedInstance("preview_notecard", LLSD(itemId)); if (nc) { - // *HACK: we have to delete the asset in the VFS so + // *HACK: we have to delete the asset in the cache so // that the viewer will redownload it. This is only // really necessary if the asset had to be modified by // the uploader, so this can be optimized away in some @@ -454,7 +453,7 @@ void LLPreviewNotecard::finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, // script actually changed the asset. if (nc->hasEmbeddedInventory()) { - gVFS->removeFile(newAssetId, LLAssetType::AT_NOTECARD); + LLFileSystem::removeFile(newAssetId, LLAssetType::AT_NOTECARD); } if (newItemId.isNull()) { @@ -479,7 +478,7 @@ void LLPreviewNotecard::finishTaskUpload(LLUUID itemId, LLUUID newAssetId, LLUUI { if (nc->hasEmbeddedInventory()) { - gVFS->removeFile(newAssetId, LLAssetType::AT_NOTECARD); + LLFileSystem::removeFile(newAssetId, LLAssetType::AT_NOTECARD); } nc->setAssetId(newAssetId); nc->refreshFromInventory(); @@ -558,14 +557,13 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem, bool sync) tid.generate(); asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - LLVFile file(gVFS, asset_id, LLAssetType::AT_NOTECARD, LLVFile::APPEND); + LLFileSystem file(asset_id, LLAssetType::AT_NOTECARD, LLFileSystem::APPEND); LLSaveNotecardInfo* info = new LLSaveNotecardInfo(this, mItemUUID, mObjectUUID, tid, copyitem); S32 size = buffer.length() + 1; - file.setMaxSize(size); file.write((U8*)buffer.c_str(), size); gAssetStorage->storeAssetData(tid, LLAssetType::AT_NOTECARD, diff --git a/indra/newview/llpreviewnotecard.h b/indra/newview/llpreviewnotecard.h index d9c14815c1..063a01ca47 100644 --- a/indra/newview/llpreviewnotecard.h +++ b/indra/newview/llpreviewnotecard.h @@ -83,8 +83,7 @@ protected: void deleteNotecard(); - static void onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, + static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 5e81fa6402..379bc9871c 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -51,7 +51,7 @@ #include "llsdserialize.h" #include "llslider.h" #include "lltooldraganddrop.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "llagent.h" #include "llmenugl.h" @@ -1685,8 +1685,11 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) { std::string buffer(mScriptEd->mEditor->getText()); + LLUUID old_asset_id = inv_item->getAssetUUID().isNull() ? mScriptEd->getAssetID() : inv_item->getAssetUUID(); + LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared(mItemUUID, buffer, - [](LLUUID itemId, LLUUID, LLUUID, LLSD response) { + [old_asset_id](LLUUID itemId, LLUUID, LLUUID, LLSD response) { + LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT); LLPreviewLSL::finishedLSLUpload(itemId, response); })); @@ -1696,8 +1699,8 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) } // static -void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void LLPreviewLSL::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LL_DEBUGS() << "LLPreviewLSL::onLoadComplete: got uuid " << asset_uuid << LL_ENDL; @@ -1707,7 +1710,7 @@ void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAsset { if(0 == status) { - LLVFile file(vfs, asset_uuid, type); + LLFileSystem file(asset_uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length+1); @@ -1734,6 +1737,7 @@ void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAsset } preview->mScriptEd->setScriptName(script_name); preview->mScriptEd->setEnableEditing(is_modifiable); + preview->mScriptEd->setAssetID(asset_uuid); preview->mAssetStatus = PREVIEW_ASSET_LOADED; } else @@ -1970,7 +1974,7 @@ void LLLiveLSLEditor::loadAsset() } // static -void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, +void LLLiveLSLEditor::onLoadComplete(const LLUUID& asset_id, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status) { @@ -1984,9 +1988,10 @@ void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, { if( LL_ERR_NOERR == status ) { - instance->loadScriptText(vfs, asset_id, type); + instance->loadScriptText(asset_id, type); instance->mScriptEd->setEnableEditing(TRUE); instance->mAssetStatus = PREVIEW_ASSET_LOADED; + instance->mScriptEd->setAssetID(asset_id); } else { @@ -2010,9 +2015,9 @@ void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, delete floater_key; } -void LLLiveLSLEditor::loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type) +void LLLiveLSLEditor::loadScriptText(const LLUUID &uuid, LLAssetType::EType type) { - LLVFile file(vfs, uuid, type); + LLFileSystem file(uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length + 1); file.read((U8*)&buffer[0], file_length); @@ -2166,6 +2171,7 @@ void LLLiveLSLEditor::finishLSLUpload(LLUUID itemId, LLUUID taskId, LLUUID newAs if (preview) { preview->mItem->setAssetUUID(newAssetId); + preview->mScriptEd->setAssetID(newAssetId); // Bytecode save completed if (response["compiled"]) @@ -2236,12 +2242,14 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) if (!url.empty()) { std::string buffer(mScriptEd->mEditor->getText()); + LLUUID old_asset_id = mScriptEd->getAssetID(); LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared(mObjectUUID, mItemUUID, monoChecked() ? LLScriptAssetUpload::MONO : LLScriptAssetUpload::LSL2, isRunning, mScriptEd->getAssociatedExperience(), buffer, - [isRunning](LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response) { - LLLiveLSLEditor::finishLSLUpload(itemId, taskId, newAssetId, response, isRunning); + [isRunning, old_asset_id](LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response) { + LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT); + LLLiveLSLEditor::finishLSLUpload(itemId, taskId, newAssetId, response, isRunning); })); LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 3cf22a0e6e..18c10ab365 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -48,7 +48,6 @@ struct LLEntryAndEdCore; class LLMenuBarGL; class LLFloaterScriptSearch; class LLKeywordToken; -class LLVFS; class LLViewerInventoryItem; class LLScriptEdContainer; class LLFloaterGotoLine; @@ -143,6 +142,9 @@ public: void setItemRemoved(bool script_removed){mScriptRemoved = script_removed;}; + void setAssetID( const LLUUID& asset_id){ mAssetID = asset_id; }; + LLUUID getAssetID() { return mAssetID; } + private: void onBtnDynamicHelp(); void onBtnUndoChanges(); @@ -188,6 +190,7 @@ private: LLUUID mAssociatedExperience; BOOL mScriptRemoved; BOOL mSaveDialogShown; + LLUUID mAssetID; LLScriptEdContainer* mContainer; // parent view @@ -234,7 +237,7 @@ protected: static void onLoad(void* userdata); static void onSave(void* userdata, BOOL close_after_save); - static void onLoadComplete(LLVFS *vfs, const LLUUID& uuid, + static void onLoadComplete(const LLUUID& uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); @@ -295,13 +298,13 @@ private: static void onLoad(void* userdata); static void onSave(void* userdata, BOOL close_after_save); - static void onLoadComplete(LLVFS *vfs, const LLUUID& asset_uuid, + static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); static void onRunningCheckboxClicked(LLUICtrl*, void* userdata); static void onReset(void* userdata); - void loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type); + void loadScriptText(const LLUUID &uuid, LLAssetType::EType type); static void onErrorList(LLUICtrl*, void* user_data); diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 1e5b893cbc..58cbe0e20a 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -57,7 +57,7 @@ #include "llinventorymodel.h" #include "llassetstorage.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "lldrawpoolwater.h" #include @@ -292,18 +292,18 @@ void LLSettingsVOBase::onTaskAssetUploadComplete(LLUUID itemId, LLUUID taskId, L void LLSettingsVOBase::getSettingsAsset(const LLUUID &assetId, LLSettingsVOBase::asset_download_fn callback) { gAssetStorage->getAssetData(assetId, LLAssetType::AT_SETTINGS, - [callback](LLVFS *vfs, const LLUUID &asset_id, LLAssetType::EType, void *, S32 status, LLExtStat ext_status) - { onAssetDownloadComplete(vfs, asset_id, status, ext_status, callback); }, + [callback](const LLUUID &asset_id, LLAssetType::EType, void *, S32 status, LLExtStat ext_status) + { onAssetDownloadComplete(asset_id, status, ext_status, callback); }, nullptr, true); } -void LLSettingsVOBase::onAssetDownloadComplete(LLVFS *vfs, const LLUUID &asset_id, S32 status, LLExtStat ext_status, LLSettingsVOBase::asset_download_fn callback) +void LLSettingsVOBase::onAssetDownloadComplete(const LLUUID &asset_id, S32 status, LLExtStat ext_status, LLSettingsVOBase::asset_download_fn callback) { LLSettingsBase::ptr_t settings; if (!status) { - LLVFile file(vfs, asset_id, LLAssetType::AT_SETTINGS, LLVFile::READ); + LLFileSystem file(asset_id, LLAssetType::AT_SETTINGS, LLFileSystem::READ); S32 size = file.getSize(); std::string buffer(size + 1, '\0'); diff --git a/indra/newview/llsettingsvo.h b/indra/newview/llsettingsvo.h index 65136ad2f5..a1baf02fe7 100644 --- a/indra/newview/llsettingsvo.h +++ b/indra/newview/llsettingsvo.h @@ -38,7 +38,6 @@ #include "llextendedstatus.h" #include -class LLVFS; class LLInventoryItem; class LLGLSLShader; @@ -81,7 +80,7 @@ private: static void onAgentAssetUploadComplete(LLUUID itemId, LLUUID newAssetId, LLUUID newItemId, LLSD response, LLSettingsBase::ptr_t psettings, inventory_result_fn callback); static void onTaskAssetUploadComplete(LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response, LLSettingsBase::ptr_t psettings, inventory_result_fn callback); - static void onAssetDownloadComplete(LLVFS *vfs, const LLUUID &asset_id, S32 status, LLExtStat ext_status, asset_download_fn callback); + static void onAssetDownloadComplete(const LLUUID &asset_id, S32 status, LLExtStat ext_status, asset_download_fn callback); }; //========================================================================= diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 8369def968..8134187c21 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -31,6 +31,7 @@ #include "llagentbenefits.h" #include "llagentcamera.h" #include "llagentui.h" +#include "llfilesystem.h" #include "llcombobox.h" #include "llfloaterperms.h" #include "llfloaterreg.h" @@ -50,8 +51,6 @@ #include "llviewercontrol.h" #include "llviewermenufile.h" // upload_new_resource() #include "llviewerstats.h" -#include "llvfile.h" -#include "llvfs.h" #include "llwindow.h" #include "llworld.h" #include @@ -1006,7 +1005,8 @@ void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) if (formatted->encode(scaled, 0.0f)) { - LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE); + LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE); + fmt_file.write(formatted->getData(), formatted->getDataSize()); std::string pos_string; LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); std::string who_took_it; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 17777c3ceb..f281b30a8e 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -82,7 +82,6 @@ #include "llversioninfo.h" #include "llviewercontrol.h" #include "llviewerhelp.h" -#include "llvfs.h" #include "llxorcipher.h" // saved password, MAC address #include "llwindow.h" #include "message.h" @@ -268,7 +267,7 @@ bool login_alert_status(const LLSD& notification, const LLSD& response); void login_packet_failed(void**, S32 result); void use_circuit_callback(void**, S32 result); void register_viewer_callbacks(LLMessageSystem* msg); -void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S32); +void asset_callback_nothing(const LLUUID&, LLAssetType::EType, void*, S32); bool callback_choose_gender(const LLSD& notification, const LLSD& response); void init_start_screen(S32 location_id); void release_start_screen(); @@ -580,7 +579,7 @@ bool idle_startup() // start the xfer system. by default, choke the downloads // a lot... const S32 VIEWER_MAX_XFER = 3; - start_xfer_manager(gVFS); + start_xfer_manager(); gXferManager->setMaxIncomingXfers(VIEWER_MAX_XFER); F32 xfer_throttle_bps = gSavedSettings.getF32("XferThrottle"); if (xfer_throttle_bps > 1.f) @@ -588,7 +587,7 @@ bool idle_startup() gXferManager->setUseAckThrottling(TRUE); gXferManager->setAckThrottleBPS(xfer_throttle_bps); } - gAssetStorage = new LLViewerAssetStorage(msg, gXferManager, gVFS, gStaticVFS); + gAssetStorage = new LLViewerAssetStorage(msg, gXferManager); F32 dropPercent = gSavedSettings.getF32("PacketDropPercentage"); @@ -1004,13 +1003,6 @@ bool idle_startup() gViewerWindow->revealIntroPanel(); - // Poke the VFS, which could potentially block for a while if - // Windows XP is acting up - set_startup_status(0.07f, LLTrans::getString("LoginVerifyingCache"), LLStringUtil::null); - display_startup(); - - gVFS->pokeFiles(); - LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); return FALSE; @@ -2589,7 +2581,7 @@ void register_viewer_callbacks(LLMessageSystem* msg) msg->setHandlerFuncFast(_PREHASH_FeatureDisabled, process_feature_disabled_message); } -void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S32) +void asset_callback_nothing(const LLUUID&, LLAssetType::EType, void*, S32) { // nothing } diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index a9f19dc32d..d4fc6f3de2 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -55,6 +55,7 @@ const S32 TEXTURE_FAST_CACHE_ENTRY_OVERHEAD = sizeof(S32) * 4; //w, h, c, level const S32 TEXTURE_FAST_CACHE_DATA_SIZE = 16 * 16 * 4; const S32 TEXTURE_FAST_CACHE_ENTRY_SIZE = TEXTURE_FAST_CACHE_DATA_SIZE + TEXTURE_FAST_CACHE_ENTRY_OVERHEAD; const F32 TEXTURE_LAZY_PURGE_TIME_LIMIT = .004f; // 4ms. Would be better to autoadjust, but there is a major cache rework in progress. +const F32 TEXTURE_PRUNING_MAX_TIME = 15.f; class LLTextureCacheWorker : public LLWorkerClass { @@ -1551,7 +1552,6 @@ void LLTextureCache::readHeaderCache() if (num_entries - empty_entries > sCacheMaxEntries) { // Special case: cache size was reduced, need to remove entries - // Note: After we prune entries, we will call this again and create the LRU U32 entries_to_purge = (num_entries - empty_entries) - sCacheMaxEntries; LL_INFOS() << "Texture Cache Entries: " << num_entries << " Max: " << sCacheMaxEntries << " Empty: " << empty_entries << " Purging: " << entries_to_purge << LL_ENDL; // We can exit the following loop with the given condition, since if we'd reach the end of the lru set we'd have: @@ -1564,7 +1564,7 @@ void LLTextureCache::readHeaderCache() ++iter; } } - else + { S32 lru_entries = (S32)((F32)sCacheMaxEntries * TEXTURE_CACHE_LRU_SIZE); for (std::set::iterator iter = lru.begin(); iter != lru.end(); ++iter) @@ -1578,30 +1578,19 @@ void LLTextureCache::readHeaderCache() if (purge_list.size() > 0) { + LLTimer timer; for (std::set::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter) { std::string tex_filename = getTextureFileName(entries[*iter].mID); removeEntry((S32)*iter, entries[*iter], tex_filename); - } - // If we removed any entries, we need to rebuild the entries list, - // write the header, and call this again - std::vector new_entries; - for (U32 i=0; i 0) + + //make sure that pruning entries doesn't take too much time + if (timer.getElapsedTimeF32() > TEXTURE_PRUNING_MAX_TIME) { - new_entries.push_back(entry); + break; } } - mFreeList.clear(); // recreating list, no longer valid. - llassert_always(new_entries.size() <= sCacheMaxEntries); - mHeaderEntriesInfo.mEntries = new_entries.size(); - writeEntriesHeader(); - writeEntriesAndClose(new_entries); - mHeaderMutex.unlock(); // unlock the mutex before calling again - readHeaderCache(); // repeat with new entries file - mHeaderMutex.lock(); + writeEntriesAndClose(entries); } else { @@ -1724,7 +1713,7 @@ void LLTextureCache::purgeTexturesLazy(F32 time_limit_sec) } S64 cache_size = mTexturesSizeTotal; - S64 purged_cache_size = (sCacheMaxTexturesSize * (S64)((1.f - TEXTURE_CACHE_PURGE_AMOUNT) * 100)) / 100; + S64 purged_cache_size = (llmax(cache_size, sCacheMaxTexturesSize) * (S64)((1.f - TEXTURE_CACHE_PURGE_AMOUNT) * 100)) / 100; for (time_idx_set_t::iterator iter = time_idx_set.begin(); iter != time_idx_set.end(); ++iter) { @@ -1820,21 +1809,26 @@ void LLTextureCache::purgeTextures(bool validate) } S64 cache_size = mTexturesSizeTotal; - S64 purged_cache_size = (sCacheMaxTexturesSize * (S64)((1.f-TEXTURE_CACHE_PURGE_AMOUNT)*100)) / 100; + S64 purged_cache_size = (llmax(cache_size, sCacheMaxTexturesSize) * (S64)((1.f - TEXTURE_CACHE_PURGE_AMOUNT) * 100)) / 100; S32 purge_count = 0; for (time_idx_set_t::iterator iter = time_idx_set.begin(); iter != time_idx_set.end(); ++iter) { S32 idx = iter->second; bool purge_entry = false; - if (validate) + + if (cache_size >= purged_cache_size) + { + purge_entry = true; + } + else if (validate) { // make sure file exists and is the correct size U32 uuididx = entries[idx].mID.mData[0]; if (uuididx == validate_idx) { - std::string filename = getTextureFileName(entries[idx].mID); - LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL; + std::string filename = getTextureFileName(entries[idx].mID); + LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL; // mHeaderAPRFilePoolp because this is under header mutex in main thread S32 bodysize = LLAPRFile::size(filename, mHeaderAPRFilePoolp); if (bodysize != entries[idx].mBodySize) @@ -1844,10 +1838,6 @@ void LLTextureCache::purgeTextures(bool validate) } } } - else if (cache_size >= purged_cache_size) - { - purge_entry = true; - } else { break; diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 54f80a2995..0f2901406a 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -28,8 +28,7 @@ #include "llviewerassetstorage.h" -#include "llvfile.h" -#include "llvfs.h" +#include "llfilesystem.h" #include "message.h" #include "llagent.h" @@ -104,10 +103,8 @@ public: ///---------------------------------------------------------------------------- // Unused? -LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, - const LLHost &upstream_host) - : LLAssetStorage(msg, xfer, vfs, static_vfs, upstream_host), +LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host) + : LLAssetStorage(msg, xfer, upstream_host), mAssetCoroCount(0), mCountRequests(0), mCountStarted(0), @@ -117,10 +114,8 @@ LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager * { } - -LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs) - : LLAssetStorage(msg, xfer, vfs, static_vfs), +LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer) + : LLAssetStorage(msg, xfer), mAssetCoroCount(0), mCountRequests(0), mCountStarted(0), @@ -157,13 +152,13 @@ void LLViewerAssetStorage::storeAssetData( if (mUpstreamHost.isOk()) { - if (mVFS->getExists(asset_id, asset_type)) + if (LLFileSystem::getExists(asset_id, asset_type)) { // Pack data into this packet if we can fit it. U8 buffer[MTUBYTES]; buffer[0] = 0; - LLVFile vfile(mVFS, asset_id, asset_type, LLVFile::READ); + LLFileSystem vfile(asset_id, asset_type, LLFileSystem::READ); S32 asset_size = vfile.getSize(); LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type); @@ -172,22 +167,20 @@ void LLViewerAssetStorage::storeAssetData( if (asset_size < 1) { - // This can happen if there's a bug in our code or if the VFS has been corrupted. - LL_WARNS("AssetStorage") << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the VFS, but it's not! " << asset_id << LL_ENDL; - // LLAssetStorage metric: Zero size VFS - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); + // This can happen if there's a bug in our code or if the cache has been corrupted. + LL_WARNS("AssetStorage") << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the cache, but it's not! " << asset_id << LL_ENDL; delete req; if (callback) { - callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_FAILED, LLExtStat::VFS_CORRUPT); + callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_FAILED, LLExtStat::CACHE_CORRUPT); } return; } else { // LLAssetStorage metric: Successful Request - S32 size = mVFS->getSize(asset_id, asset_type); + S32 size = LLFileSystem::getFileSize(asset_id, asset_type); const char *message = "Added to upload queue"; reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, size, MR_OKAY, __FILE__, __LINE__, message ); @@ -201,7 +194,7 @@ void LLViewerAssetStorage::storeAssetData( } } - // Read the data from the VFS if it'll fit in this packet. + // Read the data from the cache if it'll fit in this packet. if (asset_size + 100 < MTUBYTES) { BOOL res = vfile.read(buffer, asset_size); /* Flawfinder: ignore */ @@ -214,14 +207,11 @@ void LLViewerAssetStorage::storeAssetData( } else { - LL_WARNS("AssetStorage") << "Probable corruption in VFS file, aborting store asset data" << LL_ENDL; - - // LLAssetStorage metric: VFS corrupt - bogus size - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, asset_size, MR_VFS_CORRUPTION, __FILE__, __LINE__, "VFS corruption" ); + LL_WARNS("AssetStorage") << "Probable corruption in cache file, aborting store asset data" << LL_ENDL; if (callback) { - callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LLExtStat::VFS_CORRUPT); + callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LLExtStat::CACHE_CORRUPT); } return; } @@ -244,8 +234,7 @@ void LLViewerAssetStorage::storeAssetData( else { LL_WARNS("AssetStorage") << "AssetStorage: attempt to upload non-existent vfile " << asset_id << ":" << LLAssetType::lookup(asset_type) << LL_ENDL; - // LLAssetStorage metric: Zero size VFS - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); + reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (cache - can't tell which)" ); if (callback) { callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LLExtStat::NONEXISTENT_FILE); @@ -278,7 +267,6 @@ void LLViewerAssetStorage::storeAssetData( if(filename.empty()) { // LLAssetStorage metric: no filename - reportMetric( LLUUID::null, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_VFS_CORRUPTION, __FILE__, __LINE__, "Filename missing" ); LL_ERRS() << "No filename specified" << LL_ENDL; return; } @@ -303,9 +291,7 @@ void LLViewerAssetStorage::storeAssetData( legacy->mUpCallback = callback; legacy->mUserData = user_data; - LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE); - - file.setMaxSize(size); + LLFileSystem file(asset_id, asset_type, LLFileSystem::APPEND); const S32 buf_size = 65536; U8 copy_buf[buf_size]; @@ -539,21 +525,20 @@ void LLViewerAssetStorage::assetRequestCoro( // case. LLUUID temp_id; temp_id.generate(); - LLVFile vf(gAssetStorage->mVFS, temp_id, atype, LLVFile::WRITE); - vf.setMaxSize(size); + LLFileSystem vf(temp_id, atype, LLFileSystem::WRITE); req->mBytesFetched = size; if (!vf.write(raw.data(),size)) { // TODO asset-http: handle error LL_WARNS("ViewerAsset") << "Failure in vf.write()" << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; - ext_status = LLExtStat::VFS_CORRUPT; + ext_status = LLExtStat::CACHE_CORRUPT; } else if (!vf.rename(uuid, atype)) { LL_WARNS("ViewerAsset") << "rename failed" << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; - ext_status = LLExtStat::VFS_CORRUPT; + ext_status = LLExtStat::CACHE_CORRUPT; } else { diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index ef01d179b7..972c89de34 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -30,18 +30,16 @@ #include "llassetstorage.h" #include "llcorehttputil.h" -class LLVFile; +class LLFileSystem; class LLViewerAssetRequest; class LLViewerAssetStorage : public LLAssetStorage { public: - LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, const LLHost &upstream_host); + LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host); - LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs); + LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer); ~LLViewerAssetStorage(); diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index f2e887a678..7b5229d312 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -45,7 +45,7 @@ #include "llviewerassetupload.h" #include "llappviewer.h" #include "llviewerstats.h" -#include "llvfile.h" +#include "llfilesystem.h" #include "llgesturemgr.h" #include "llpreviewnotecard.h" #include "llpreviewgesture.h" @@ -467,7 +467,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() setAssetType(assetType); - // copy this file into the vfs for upload + // copy this file into the cache for upload S32 file_size; LLAPRFile infile; infile.open(filename, LL_APR_RB, NULL, &file_size); @@ -505,7 +505,7 @@ LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID itemId, LLAssetType: mContents(buffer), mInvnFinishFn(finish), mTaskFinishFn(nullptr), - mStoredToVFS(false) + mStoredToCache(false) { setItemId(itemId); setAssetType(assetType); @@ -519,7 +519,7 @@ LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID itemId, LLPointerrenameFile(getAssetId(), assetType, newAssetId, assetType); + LLFileSystem::renameFile(getAssetId(), assetType, newAssetId, assetType); } if (mTaskUpload) diff --git a/indra/newview/llviewerassetupload.h b/indra/newview/llviewerassetupload.h index d9eacf3167..e56ba7d8f7 100644 --- a/indra/newview/llviewerassetupload.h +++ b/indra/newview/llviewerassetupload.h @@ -197,7 +197,7 @@ private: std::string mContents; invnUploadFinish_f mInvnFinishFn; taskUploadFinish_f mTaskFinishFn; - bool mStoredToVFS; + bool mStoredToCache; }; //------------------------------------------------------------------------- diff --git a/indra/newview/llviewermedia_streamingaudio.cpp b/indra/newview/llviewermedia_streamingaudio.cpp index 3ccf3070ab..d3e24aece5 100644 --- a/indra/newview/llviewermedia_streamingaudio.cpp +++ b/indra/newview/llviewermedia_streamingaudio.cpp @@ -33,10 +33,8 @@ #include "llviewermedia_streamingaudio.h" #include "llmimetypes.h" -#include "llvfs.h" #include "lldir.h" - LLStreamingAudio_MediaPlugins::LLStreamingAudio_MediaPlugins() : mMediaPlugin(NULL), mGain(1.0) diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 4b231c7067..e59a263adc 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -53,8 +53,6 @@ #include "llviewercontrol.h" // gSavedSettings #include "llviewertexturelist.h" #include "lluictrlfactory.h" -#include "llvfile.h" -#include "llvfs.h" #include "llviewerinventory.h" #include "llviewermenu.h" // gMenuHolder #include "llviewerparcelmgr.h" diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 458fc3b13d..6ef452bd92 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -44,8 +44,7 @@ #include "llteleportflags.h" #include "lltoastnotifypanel.h" #include "lltransactionflags.h" -#include "llvfile.h" -#include "llvfs.h" +#include "llfilesystem.h" #include "llxfermanager.h" #include "mean_collision_data.h" @@ -6844,16 +6843,15 @@ void process_covenant_reply(LLMessageSystem* msg, void**) } } -void onCovenantLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void onCovenantLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LL_DEBUGS("Messaging") << "onCovenantLoadComplete()" << LL_ENDL; std::string covenant_text; if(0 == status) { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 file_length = file.getSize(); diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index 78829a6a56..1e5a69ae13 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -47,7 +47,6 @@ class LLInventoryObject; class LLInventoryItem; class LLMeanCollisionData; class LLMessageSystem; -class LLVFS; class LLViewerObject; class LLViewerRegion; @@ -189,8 +188,7 @@ void process_script_dialog(LLMessageSystem* msg, void**); void process_load_url(LLMessageSystem* msg, void**); void process_script_teleport_request(LLMessageSystem* msg, void**); void process_covenant_reply(LLMessageSystem* msg, void**); -void onCovenantLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, +void onCovenantLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index bbbacce8fa..e378c2448a 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -101,7 +101,6 @@ #include "v4math.h" #include "xform.h" -// Library includes from llvfs #include "lldir.h" // Library includes from llmessage project diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 05f88b0a75..35fb4de5a9 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -33,7 +33,6 @@ #include "llfloaterreg.h" #include "llmemory.h" #include "lltimer.h" -#include "llvfile.h" #include "llappviewer.h" @@ -145,7 +144,6 @@ LLTrace::SampleStatHandle<> FPS_SAMPLE("fpssample"), VISIBLE_AVATARS("visibleavatars", "Visible Avatars"), SHADER_OBJECTS("shaderobjects", "Object Shaders"), DRAW_DISTANCE("drawdistance", "Draw Distance"), - PENDING_VFS_OPERATIONS("vfspendingoperations"), WINDOW_WIDTH("windowwidth", "Window width"), WINDOW_HEIGHT("windowheight", "Window height"); @@ -383,7 +381,6 @@ void update_statistics() F64Bits layer_bits = gVLManager.getLandBits() + gVLManager.getWindBits() + gVLManager.getCloudBits(); add(LLStatViewer::LAYERS_NETWORK_DATA_RECEIVED, layer_bits); add(LLStatViewer::OBJECT_NETWORK_DATA_RECEIVED, gObjectData); - sample(LLStatViewer::PENDING_VFS_OPERATIONS, LLVFile::getVFSThread()->getPending()); add(LLStatViewer::ASSET_UDP_DATA_RECEIVED, F64Bits(gTransferManager.getTransferBitsIn(LLTCT_ASSET))); gTransferManager.resetTransferBitsIn(LLTCT_ASSET); diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index 04870e0c26..72ce336664 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -186,7 +186,6 @@ extern LLTrace::SampleStatHandle<> FPS_SAMPLE, VISIBLE_AVATARS, SHADER_OBJECTS, DRAW_DISTANCE, - PENDING_VFS_OPERATIONS, WINDOW_WIDTH, WINDOW_HEIGHT; diff --git a/indra/newview/llviewertexlayer.cpp b/indra/newview/llviewertexlayer.cpp index c501dd0035..4c2fbcf837 100644 --- a/indra/newview/llviewertexlayer.cpp +++ b/indra/newview/llviewertexlayer.cpp @@ -31,8 +31,6 @@ #include "llagent.h" #include "llimagej2c.h" #include "llnotificationsutil.h" -#include "llvfile.h" -#include "llvfs.h" #include "llviewerregion.h" #include "llglslshader.h" #include "llvoavatarself.h" diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 20a22ba45e..d69ab1b110 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -39,8 +39,6 @@ #include "llimagej2c.h" #include "llimagetga.h" #include "llstl.h" -#include "llvfile.h" -#include "llvfs.h" #include "message.h" #include "lltimer.h" diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 69568cc825..07997e02a5 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -54,7 +54,7 @@ class LLTexturePipelineTester ; typedef void (*loaded_callback_func)( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ); -class LLVFile; +class LLFileSystem; class LLMessageSystem; class LLViewerMediaImpl ; class LLVOVolume ; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 561319ca5d..38fccba169 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -41,9 +41,7 @@ #include "llsdserialize.h" #include "llsys.h" -#include "llvfs.h" -#include "llvfile.h" -#include "llvfsthread.h" +#include "llfilesystem.h" #include "llxmltree.h" #include "message.h" diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp index 9c4dfd1ca2..9a4607bb60 100644 --- a/indra/newview/llviewerwearable.cpp +++ b/indra/newview/llviewerwearable.cpp @@ -107,7 +107,6 @@ LLWearable::EImportResult LLViewerWearable::importStream( std::istream& input_st // Shouldn't really log the asset id for security reasons, but // we need it in this case. LL_WARNS() << "Bad Wearable asset header: " << mAssetID << LL_ENDL; - //gVFS->dumpMap(); return result; } diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index f69b9b3861..414a1da4fe 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1140,7 +1140,6 @@ void LLVOAvatar::initInstance() //------------------------------------------------------------------------- if (LLCharacter::sInstances.size() == 1) { - LLKeyframeMotion::setVFS(gStaticVFS); registerMotion( ANIM_AGENT_DO_NOT_DISTURB, LLNullMotion::create ); registerMotion( ANIM_AGENT_CROUCH, LLKeyframeStandMotion::create ); registerMotion( ANIM_AGENT_CROUCHWALK, LLKeyframeWalkMotion::create ); diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index ce400a3498..73eeec813c 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -218,10 +218,9 @@ public: void updateSculptTexture(); void setIndexInTex(U32 ch, S32 index) { mIndexInTex[ch] = index ;} void sculpt(); - static void rebuildMeshAssetCallback(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status); + static void rebuildMeshAssetCallback(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status); void updateRelativeXform(bool force_identity = false); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/indra/newview/skins/default/xui/da/floater_stats.xml b/indra/newview/skins/default/xui/da/floater_stats.xml index fe3fa9626e..d07f9e48ca 100644 --- a/indra/newview/skins/default/xui/da/floater_stats.xml +++ b/indra/newview/skins/default/xui/da/floater_stats.xml @@ -32,7 +32,6 @@ - diff --git a/indra/newview/skins/default/xui/da/strings.xml b/indra/newview/skins/default/xui/da/strings.xml index ec6ba4800d..814305c1bc 100644 --- a/indra/newview/skins/default/xui/da/strings.xml +++ b/indra/newview/skins/default/xui/da/strings.xml @@ -22,9 +22,6 @@ Initialiserer tekstur cache... - - Initialiserer VFS... - Gendanner... diff --git a/indra/newview/skins/default/xui/de/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/de/floater_scene_load_stats.xml index dff462a594..a3749f1cfa 100644 --- a/indra/newview/skins/default/xui/de/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/de/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/de/floater_stats.xml b/indra/newview/skins/default/xui/de/floater_stats.xml index 4e6f56cd94..1838c2081a 100644 --- a/indra/newview/skins/default/xui/de/floater_stats.xml +++ b/indra/newview/skins/default/xui/de/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 43327c132d..f021e03dc7 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -31,9 +31,6 @@ Textur-Cache wird initialisiert... - - VFS wird initialisiert... - Grafikinitialisierung fehlgeschlagen. Bitte aktualisieren Sie Ihren Grafiktreiber. @@ -73,7 +70,6 @@ LOD-Faktor: [LOD_FACTOR] Darstellungsqualität: [RENDER_QUALITY] Erweitertes Beleuchtungsmodell: [GPU_SHADERS] Texturspeicher: [TEXTURE_MEMORY] MB -Erstellungszeit VFS (Cache): [VFS_TIME] HiDPI-Anzeigemodus: [HIDPI] diff --git a/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml index 62cce3a1e3..35d4385487 100644 --- a/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml @@ -206,12 +206,6 @@ bar_max="1024.f" tick_spacing="128.f" precision="1" - show_bar="false"/> - diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml index e4f735740b..f9d44b2c69 100644 --- a/indra/newview/skins/default/xui/en/floater_stats.xml +++ b/indra/newview/skins/default/xui/en/floater_stats.xml @@ -198,10 +198,6 @@ stat="messagedataout" decimal_digits="1" show_history="false"/> - diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index f9f12e7f5c..619c869a3d 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -17,7 +17,6 @@ Loading [APP_NAME]... Clearing cache... Initializing texture cache... - Initializing VFS... Graphics initialization failed. Please update your graphics driver! @@ -56,9 +55,9 @@ LOD factor: [LOD_FACTOR] Render quality: [RENDER_QUALITY] Advanced Lighting Model: [GPU_SHADERS] Texture memory: [TEXTURE_MEMORY]MB -VFS (cache) creation time: [VFS_TIME] +Disk cache: [DISK_CACHE_INFO] - + HiDPI display mode: [HIDPI] diff --git a/indra/newview/skins/default/xui/es/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/es/floater_scene_load_stats.xml index f625d5257c..cfc5e524b5 100644 --- a/indra/newview/skins/default/xui/es/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/es/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/es/floater_stats.xml b/indra/newview/skins/default/xui/es/floater_stats.xml index d1c5e867db..0aec786f25 100644 --- a/indra/newview/skins/default/xui/es/floater_stats.xml +++ b/indra/newview/skins/default/xui/es/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index f5e7d0bf4e..ebb4ceaa7e 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -22,9 +22,6 @@ Iniciando la caché de las texturas... - - Iniciando VFS... - Error de inicialización de gráficos. Actualiza tu controlador de gráficos. @@ -65,7 +62,6 @@ Factor LOD: [LOD_FACTOR] Calidad de renderización: [RENDER_QUALITY] Modelo de iluminación avanzado: [GPU_SHADERS] Memoria de textura: [TEXTURE_MEMORY]MB -VFS (cache) hora de creación: [VFS_TIME] Modo de visualización HiDPi: [HIDPI] diff --git a/indra/newview/skins/default/xui/fr/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/fr/floater_scene_load_stats.xml index 62830054bf..3889b13f0c 100644 --- a/indra/newview/skins/default/xui/fr/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/fr/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/fr/floater_stats.xml b/indra/newview/skins/default/xui/fr/floater_stats.xml index fae17e3608..d0f7f42939 100644 --- a/indra/newview/skins/default/xui/fr/floater_stats.xml +++ b/indra/newview/skins/default/xui/fr/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index f26eac545a..9fde703d6c 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -31,9 +31,6 @@ Initialisation du cache des textures... - - Initialisation VFS... - Échec d'initialisation des graphiques. Veuillez mettre votre pilote graphique à jour. @@ -74,7 +71,6 @@ Facteur LOD (niveau de détail) : [LOD_FACTOR] Qualité de rendu : [RENDER_QUALITY] Modèle d’éclairage avancé : [GPU_SHADERS] Mémoire textures : [TEXTURE_MEMORY] Mo -Heure de création VFS (cache) : [VFS_TIME] Mode d'affichage HiDPI : [HIDPI] diff --git a/indra/newview/skins/default/xui/it/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/it/floater_scene_load_stats.xml index ca18812eb7..efceb05298 100644 --- a/indra/newview/skins/default/xui/it/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/it/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/it/floater_stats.xml b/indra/newview/skins/default/xui/it/floater_stats.xml index 7ef13aa37f..6434c3b66b 100644 --- a/indra/newview/skins/default/xui/it/floater_stats.xml +++ b/indra/newview/skins/default/xui/it/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index f0466cea81..3049828f46 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -28,9 +28,6 @@ Inizializzazione della cache texture... - - Inizializzazione VFS... - Inizializzazione grafica non riuscita. Aggiorna il driver della scheda grafica! @@ -71,7 +68,6 @@ Fattore livello di dettaglio: [LOD_FACTOR] Qualità di rendering: [RENDER_QUALITY] Modello illuminazione avanzato: [GPU_SHADERS] Memoria texture: [TEXTURE_MEMORY]MB -Data/ora creazione VFS (cache): [VFS_TIME] Modalità display HiDPI: [HIDPI] diff --git a/indra/newview/skins/default/xui/ja/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/ja/floater_scene_load_stats.xml index f6edce026f..f348ce3c4d 100644 --- a/indra/newview/skins/default/xui/ja/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/ja/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/ja/floater_stats.xml b/indra/newview/skins/default/xui/ja/floater_stats.xml index 3bc343639b..1da0e5ebc9 100644 --- a/indra/newview/skins/default/xui/ja/floater_stats.xml +++ b/indra/newview/skins/default/xui/ja/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index 52d6fb0c2b..dcd6e65d34 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -31,9 +31,6 @@ テクスチャキャッシュを初期化中です... - - VFS を初期化中です... - グラフィックを初期化できませんでした。グラフィックドライバを更新してください。 @@ -74,7 +71,6 @@ LOD 係数: [LOD_FACTOR] 描画の質: [RENDER_QUALITY] 高度なライティングモデル: [GPU_SHADERS] テクスチャメモリ: [TEXTURE_MEMORY]MB -VFS(キャッシュ)作成時間: [VFS_TIME] HiDPI 表示モード: [HIDPI] diff --git a/indra/newview/skins/default/xui/pl/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/pl/floater_scene_load_stats.xml index 6fdc7e19f6..8f5d0c5c70 100644 --- a/indra/newview/skins/default/xui/pl/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/pl/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/pl/floater_stats.xml b/indra/newview/skins/default/xui/pl/floater_stats.xml index 5dd7d19bab..21e37717c2 100644 --- a/indra/newview/skins/default/xui/pl/floater_stats.xml +++ b/indra/newview/skins/default/xui/pl/floater_stats.xml @@ -55,7 +55,6 @@ - diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml index 91fea234d2..cf033df3c9 100644 --- a/indra/newview/skins/default/xui/pl/strings.xml +++ b/indra/newview/skins/default/xui/pl/strings.xml @@ -15,9 +15,6 @@ Inicjowanie bufora danych tekstur... - - Inicjowanie wirtualnego systemu plików... - Nie można zainicjować grafiki. Zaktualizuj sterowniki! diff --git a/indra/newview/skins/default/xui/pt/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/pt/floater_scene_load_stats.xml index 027e1ef311..dbaab1d782 100644 --- a/indra/newview/skins/default/xui/pt/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/pt/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/pt/floater_stats.xml b/indra/newview/skins/default/xui/pt/floater_stats.xml index f41fe17778..3253984268 100644 --- a/indra/newview/skins/default/xui/pt/floater_stats.xml +++ b/indra/newview/skins/default/xui/pt/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index ee982b5b22..c72a41fd3a 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -22,9 +22,6 @@ Iniciando cache de texturas... - - Iniciando VFS... - Falha na inicialização dos gráficos. Atualize seu driver gráfico! @@ -65,7 +62,6 @@ LOD fator: [LOD_FACTOR] Qualidade de renderização: [RENDER_QUALITY] Modelo avançado de luzes: [GPU_SHADERS] Memória de textura: [TEXTURE_MEMORY]MB -VFS (cache) tempo de criação: [VFS_TIME] HiDPI modo de exibição: [HIDPI] diff --git a/indra/newview/skins/default/xui/ru/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/ru/floater_scene_load_stats.xml index a101e62627..c4f432023c 100644 --- a/indra/newview/skins/default/xui/ru/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/ru/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/ru/floater_stats.xml b/indra/newview/skins/default/xui/ru/floater_stats.xml index 10e9f5a7f4..a7d26029c2 100644 --- a/indra/newview/skins/default/xui/ru/floater_stats.xml +++ b/indra/newview/skins/default/xui/ru/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index e9592a0476..43a87b2b18 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -31,9 +31,6 @@ Инициализация кэша текстур... - - Инициализация виртуальной файловой системы... - Ошибка инициализации графики. Обновите графический драйвер! @@ -74,7 +71,6 @@ SLURL: <nolink>[SLURL]</nolink> Качество визуализации: [RENDER_QUALITY] Расширенная модель освещения: [GPU_SHADERS] Память текстур: [TEXTURE_MEMORY] МБ -Время создания VFS (кэш): [VFS_TIME] Режим отображения HiDPI: [HIDPI] diff --git a/indra/newview/skins/default/xui/tr/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/tr/floater_scene_load_stats.xml index ae0a94595d..7d5f4adb02 100644 --- a/indra/newview/skins/default/xui/tr/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/tr/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/tr/floater_stats.xml b/indra/newview/skins/default/xui/tr/floater_stats.xml index 1ae42ad382..bd36d4916f 100644 --- a/indra/newview/skins/default/xui/tr/floater_stats.xml +++ b/indra/newview/skins/default/xui/tr/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index 56fad978f5..982de76a5b 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -31,9 +31,6 @@ Doku önbelleği başlatılıyor... - - VFS Başlatılıyor... - Grafik başlatma başarılamadı. Lütfen grafik sürücünüzü güncelleştirin! @@ -74,7 +71,6 @@ LOD faktörü: [LOD_FACTOR] İşleme kalitesi: [RENDER_QUALITY] Gelişmiş Aydınlatma Modeli: [GPU_SHADERS] Doku belleği: [TEXTURE_MEMORY]MB -VFS (önbellek) oluşturma saati: [VFS_TIME] HiDPI görüntü modu: [HIDPI] diff --git a/indra/newview/skins/default/xui/zh/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/zh/floater_scene_load_stats.xml index 1a5c20abeb..20344e299f 100644 --- a/indra/newview/skins/default/xui/zh/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/zh/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/zh/floater_stats.xml b/indra/newview/skins/default/xui/zh/floater_stats.xml index f06eb5e78f..e233ece527 100644 --- a/indra/newview/skins/default/xui/zh/floater_stats.xml +++ b/indra/newview/skins/default/xui/zh/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml index e6c61a5d94..3221cde3b7 100644 --- a/indra/newview/skins/default/xui/zh/strings.xml +++ b/indra/newview/skins/default/xui/zh/strings.xml @@ -31,9 +31,6 @@ 正在初始化材質快取... - - VFS 初始化中... - 顯像初始化失敗。 請更新你的顯像卡驅動程式! @@ -74,7 +71,6 @@ 呈像品質:[RENDER_QUALITY] 進階照明模型:[GPU_SHADERS] 材質記憶體:[TEXTURE_MEMORY]MB -VFS(快取)建立時間:[VFS_TIME] HiDPI顯示模式:[HIDPI] diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 6d231040f7..8eee583a48 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -69,7 +69,6 @@ class ViewerManifest(LLManifest): self.exclude("logcontrol-dev.xml") self.path("*.ini") self.path("*.xml") - self.path("*.db2") # include the entire shaders directory recursively self.path("shaders") diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 87536e146b..b3bff3611a 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -8,12 +8,11 @@ include(LLCoreHttp) include(LLInventory) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLFileSystem) include(LLXML) include(Linking) include(Tut) include(LLAddBuildTest) - include(GoogleMock) include_directories( @@ -23,7 +22,7 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} ${LLINVENTORY_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS} ${GOOGLEMOCK_INCLUDE_DIRS} @@ -89,7 +88,7 @@ target_link_libraries(lltest ${LLINVENTORY_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLMATH_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/win_crash_logger/CMakeLists.txt b/indra/win_crash_logger/CMakeLists.txt index 86aa655f03..1b187d624a 100644 --- a/indra/win_crash_logger/CMakeLists.txt +++ b/indra/win_crash_logger/CMakeLists.txt @@ -8,7 +8,7 @@ include(LLCoreHttp) include(LLCrashLogger) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLFileSystem) include(LLWindow) include(LLXML) include(Linking) @@ -23,7 +23,7 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${BREAKPAD_INCLUDE_DIRECTORIES} ) include_directories(SYSTEM @@ -76,7 +76,7 @@ target_link_libraries(windows-crash-logger ${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES} ${LLCRASHLOGGER_LIBRARIES} ${LLWINDOW_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLXML_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLMATH_LIBRARIES} From d7518c7b4f5eca731d0ea143bab34b279bd4ee13 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Tue, 9 Mar 2021 18:33:35 -0800 Subject: [PATCH 5/6] Ansariel kindly offered their patch to help mitigate this round of file system issues - taken from https://vcs.firestormviewer.org/phoenix-firestorm/changeset/104a8600946be01e2de44d10ad069ba854272d1f --- indra/llfilesystem/lldiskcache.cpp | 4 ++-- indra/llfilesystem/llfilesystem.cpp | 27 +++++++++++++++++++++++ indra/newview/llmeshrepository.cpp | 34 ++++++++++++++++++++++++----- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index c9f7684b5a..61eb654693 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -192,8 +192,8 @@ const std::string LLDiskCache::metaDataToFilepath(const std::string id, file_path << id; file_path << "_"; file_path << (extra_info.empty() ? "0" : extra_info); - //file_path << "_"; - //file_path << assetTypeToString(at); // see SL-14210 Prune descriptive tag from new cache filenames + file_path << "_"; + file_path << assetTypeToString(at); // see SL-14210 Prune descriptive tag from new cache filenames // for details of why it was removed. Note that if you put it // back or change the format of the filename, the cache files // files will be invalidated (and perhaps, more importantly, diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 053b52014e..da44e8d98c 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -200,9 +200,36 @@ BOOL LLFileSystem::write(const U8* buffer, S32 bytes) { ofs.write((const char*)buffer, bytes); + mPosition = ofs.tellp(); // Fix asset caching + success = TRUE; } } + // Fix asset caching + else if (mMode == READ_WRITE) + { + // Don't truncate if file already exists + llofstream ofs(filename, std::ios::in | std::ios::binary); + if (ofs) + { + ofs.seekp(mPosition, std::ios::beg); + ofs.write((const char*)buffer, bytes); + mPosition += bytes; + success = TRUE; + } + else + { + // File doesn't exist - open in write mode + ofs.open(filename, std::ios::binary); + if (ofs.is_open()) + { + ofs.write((const char*)buffer, bytes); + mPosition += bytes; + success = TRUE; + } + } + } + // else { llofstream ofs(filename, std::ios::binary); diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 8e5bdc0225..c2404a7e67 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3248,13 +3248,29 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b // only allocate as much space in the cache as is needed for the local cache data_size = llmin(data_size, bytes); - LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::WRITE); + // Fix asset caching + //LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::WRITE); + LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); if (file.getMaxSize() >= bytes) { LLMeshRepository::sCacheBytesWritten += data_size; ++LLMeshRepository::sCacheWrites; file.write(data, data_size); + + // Fix asset caching + S32 remaining = bytes - file.tell(); + if (remaining > 0) + { + U8* block = new(std::nothrow) U8[remaining]; + if (block) + { + memset(block, 0, remaining); + file.write(block, remaining); + delete[] block; + } + } + // } } else @@ -3307,7 +3323,9 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body if (result == MESH_OK) { // good fetch from sim, write to cache - LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::WRITE); + // Fix asset caching + //LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::WRITE); + LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3371,7 +3389,9 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) { // good fetch from sim, write to cache - LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); + // Fix asset caching + //LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3419,7 +3439,9 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) { // good fetch from sim, write to cache - LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); + // Fix asset caching + //LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3466,7 +3488,9 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size) == MESH_OK) { // good fetch from sim, write to cache for caching - LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); + // Fix asset caching + //LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; From ad2edc1d8336c745fc897197e2e85debf3654c17 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Tue, 9 Mar 2021 18:35:30 -0800 Subject: [PATCH 6/6] Remove debugging tags --- indra/llfilesystem/lldiskcache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index 61eb654693..c9f7684b5a 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -192,8 +192,8 @@ const std::string LLDiskCache::metaDataToFilepath(const std::string id, file_path << id; file_path << "_"; file_path << (extra_info.empty() ? "0" : extra_info); - file_path << "_"; - file_path << assetTypeToString(at); // see SL-14210 Prune descriptive tag from new cache filenames + //file_path << "_"; + //file_path << assetTypeToString(at); // see SL-14210 Prune descriptive tag from new cache filenames // for details of why it was removed. Note that if you put it // back or change the format of the filename, the cache files // files will be invalidated (and perhaps, more importantly,