From 164912418ccfd8c7a8ee02443db18e5fce19c8c4 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 28 Oct 2025 10:03:25 -0700 Subject: Fix issue where mac was crashing during an attempt to unplug or replug microphone. (#4897) The mac audio device manager was being "helpful" by restarting playout and recording if the Default device was changed, assuming the application wouldn't care. However, we received an update of device change, and attempted to stop and start playout anyway, causing a conflict. The fix was simply to not deploy new devices when the device id didn't change. --- indra/llwebrtc/llwebrtc.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index b9f126e511..161d8d7e91 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -572,14 +572,20 @@ void LLWebRTCImpl::workerDeployDevices() void LLWebRTCImpl::setCaptureDevice(const std::string &id) { - mRecordingDevice = id; - deployDevices(); + if (mRecordingDevice != id) + { + mRecordingDevice = id; + deployDevices(); + } } void LLWebRTCImpl::setRenderDevice(const std::string &id) { - mPlayoutDevice = id; - deployDevices(); + if (mPlayoutDevice != id) + { + mPlayoutDevice = id; + deployDevices(); + } } // updateDevices needs to happen on the worker thread. -- cgit v1.3 From 5a0ba25d83862e7d220c1ae62173b537b7db5b52 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 28 Oct 2025 19:29:54 +0200 Subject: #4882 Log textures that failed to be created A bunch of 36x36 failed to create, there shouldn't have been any 36x36 textures, log the ids/type. --- indra/llappearance/lltexlayer.cpp | 5 ++++- indra/llappearance/lltexlayerparams.cpp | 5 ++++- indra/llrender/llcubemap.cpp | 10 ++++++++-- indra/newview/lldrawpoolbump.cpp | 10 ++++++++-- indra/newview/lllocalbitmaps.cpp | 5 ++++- indra/newview/llviewermedia.cpp | 10 ++++++++-- indra/newview/llviewertexture.cpp | 5 ++++- 7 files changed, 40 insertions(+), 10 deletions(-) (limited to 'indra') diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp index b3800e6981..7f7eaf1855 100644 --- a/indra/llappearance/lltexlayer.cpp +++ b/indra/llappearance/lltexlayer.cpp @@ -1890,7 +1890,10 @@ LLGLTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_name, image_raw->copyUnscaledAlphaMask(alpha_image_raw, LLColor4U::black); } - tex->createGLTexture(0, image_raw, 0, true, LLGLTexture::LOCAL); + if (!tex->createGLTexture(0, image_raw, 0, true, LLGLTexture::LOCAL)) + { + LL_WARNS() << "Failed to create GL texture for image: " << file_name << LL_ENDL; + } gGL.getTexUnit(0)->bind(tex); tex->setAddressMode(LLTexUnit::TAM_CLAMP); diff --git a/indra/llappearance/lltexlayerparams.cpp b/indra/llappearance/lltexlayerparams.cpp index 30551c115d..dd2499cf37 100644 --- a/indra/llappearance/lltexlayerparams.cpp +++ b/indra/llappearance/lltexlayerparams.cpp @@ -338,7 +338,10 @@ bool LLTexLayerParamAlpha::render(S32 x, S32 y, S32 width, S32 height) // Create the GL texture, and then hang onto it for future use. if (mNeedsCreateTexture) { - mCachedProcessedTexture->createGLTexture(0, mStaticImageRaw); + if (!mCachedProcessedTexture->createGLTexture(0, mStaticImageRaw)) + { + LL_WARNS() << "Failed to create GL texture for image: " << mCachedProcessedTexture->getID() << LL_ENDL; + } mNeedsCreateTexture = false; gGL.getTexUnit(0)->bind(mCachedProcessedTexture); mCachedProcessedTexture->setAddressMode(LLTexUnit::TAM_CLAMP); diff --git a/indra/llrender/llcubemap.cpp b/indra/llrender/llcubemap.cpp index 26e4aaad52..b15cec5804 100644 --- a/indra/llrender/llcubemap.cpp +++ b/indra/llrender/llcubemap.cpp @@ -86,7 +86,10 @@ void LLCubeMap::initGL() #endif mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP); mRawImages[i] = new LLImageRaw(RESOLUTION, RESOLUTION, 4); - mImages[i]->createGLTexture(0, mRawImages[i], texname); + if (!mImages[i]->createGLTexture(0, mRawImages[i], texname)) + { + LL_WARNS() << "Failed to create GL texture for environment cubemap face " << i << LL_ENDL; + } gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname); mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP); @@ -203,7 +206,10 @@ void LLCubeMap::initEnvironmentMap(const std::vector >& ra mImages[i] = new LLImageGL(resolution, resolution, components, true); mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP); mRawImages[i] = rawimages[i]; - mImages[i]->createGLTexture(0, mRawImages[i], texname); + if (!mImages[i]->createGLTexture(0, mRawImages[i], texname)) + { + LL_WARNS() << "Failed to create GL texture for environment cubemap face " << i << LL_ENDL; + } gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname); mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP); diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index bf593bff07..69003f88fd 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -802,7 +802,10 @@ void LLBumpImageList::onSourceStandardLoaded( bool success, LLViewerFetchedTextu } src_vi->setExplicitFormat(GL_RGBA, GL_RGBA); { - src_vi->createGLTexture(src_vi->getDiscardLevel(), nrm_image); + if (!src_vi->createGLTexture(src_vi->getDiscardLevel(), nrm_image)) + { + LL_WARNS() << "Failed to create bump image texture for image " << src_vi->getID() << LL_ENDL; + } } } } @@ -896,7 +899,10 @@ void LLBumpImageList::onSourceUpdated(LLViewerTexture* src, EBumpEffect bump_cod LLImageGL* src_img = src->getGLTexture(); LLImageGL* dst_img = bump->getGLTexture(); - dst_img->setSize(src->getWidth(), src->getHeight(), 4, 0); + if (!dst_img->setSize(src->getWidth(), src->getHeight(), 4, 0)) + { + LL_WARNS() << "Failed to setSize for image " << bump->getID() << LL_ENDL; + } dst_img->setUseMipMaps(true); dst_img->setDiscardLevel(0); dst_img->createGLTexture(); diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp index a99c9df0ff..6e56aac270 100644 --- a/indra/newview/lllocalbitmaps.cpp +++ b/indra/newview/lllocalbitmaps.cpp @@ -219,7 +219,10 @@ bool LLLocalBitmap::updateSelf(EUpdateType optional_firstupdate) LLPointer texture = new LLViewerFetchedTexture ("file://"+mFilename, FTT_LOCAL_FILE, mWorldID, LL_LOCAL_USE_MIPMAPS); - texture->createGLTexture(LL_LOCAL_DISCARD_LEVEL, raw_image); + if (!texture->createGLTexture(LL_LOCAL_DISCARD_LEVEL, raw_image)) + { + LL_WARNS() << "Failed to create GL texture for local bitmap: " << mFilename << " " << mWorldID << LL_ENDL; + } texture->ref(); gTextureList.addImage(texture, TEX_LIST_STANDARD); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 89861d74bc..70596c935c 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -3001,7 +3001,10 @@ void LLViewerMediaImpl::doMediaTexUpdate(LLViewerMediaTexture* media_tex, U8* da // -Cosmic,2023-04-04 // Allocate GL texture based on LLImageRaw but do NOT copy to GL LLGLuint tex_name = 0; - media_tex->createGLTexture(0, raw, 0, true, LLGLTexture::OTHER, true, &tex_name); + if (!media_tex->createGLTexture(0, raw, 0, true, LLGLTexture::OTHER, true, &tex_name)) + { + LL_WARNS("Media") << "Failed to create media texture" << LL_ENDL; + } // copy just the subimage covered by the image raw to GL media_tex->setSubImage(data, data_width, data_height, x_pos, y_pos, width, height, tex_name); @@ -3070,7 +3073,10 @@ LLViewerMediaTexture* LLViewerMediaImpl::updateMediaImage() mMediaSource->getTextureFormatSwapBytes()); int discard_level = 0; - media_tex->createGLTexture(discard_level, raw); + if (!media_tex->createGLTexture(discard_level, raw)) + { + LL_WARNS("Media") << "Failed to create media texture" << LL_ENDL; + } // MEDIAOPT: set this dynamically on play/stop // FIXME diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 6135e18840..1e83482752 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -406,7 +406,10 @@ void LLViewerTextureManager::init() } } } - imagep->createGLTexture(0, image_raw); + if (!imagep->createGLTexture(0, image_raw)) + { + LL_WARNS() << "Failed to create default texture " << IMG_DEFAULT << LL_ENDL; + } image_raw = NULL; #else LLViewerFetchedTexture::sDefaultImagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, true, LLGLTexture::BOOST_UI); -- cgit v1.3 From 0fd96e62471a4d6c95c5c3a8d6e1de0e59c83379 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:11:54 +0200 Subject: #4604 Turn off cef preloading on low end hardware 1. Cef instances are fairly heavy in viewer's implementation, don't preload them on systems <= 8GB 2. Move initialization into floaters where it belongs. 3. Minimal requirement is 8GB, drop gpu class to 1 on hardware under 8GB 4. Reduce cef instance count on systems under 8GB --- indra/newview/llfeaturemanager.cpp | 2 +- indra/newview/llfloateravatarwelcomepack.cpp | 8 +- indra/newview/llfloaterdestinations.cpp | 12 +++ indra/newview/llfloatermarketplace.cpp | 7 ++ indra/newview/llfloatersearch.cpp | 6 ++ indra/newview/llviewermedia.cpp | 106 ++++++++++++++++++++++++--- indra/newview/llviewermedia.h | 7 +- indra/newview/llviewerwindow.cpp | 45 ++++-------- 8 files changed, 150 insertions(+), 43 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index ba54d93c75..052278f731 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -465,7 +465,7 @@ bool LLFeatureManager::loadGPUClass() } #if LL_WINDOWS - const F32Gigabytes MIN_PHYSICAL_MEMORY(2); + const F32Gigabytes MIN_PHYSICAL_MEMORY(8); LLMemory::updateMemoryInfo(); F32Gigabytes physical_mem = LLMemory::getMaxMemKB(); diff --git a/indra/newview/llfloateravatarwelcomepack.cpp b/indra/newview/llfloateravatarwelcomepack.cpp index 82e44d1398..be384bf4d1 100644 --- a/indra/newview/llfloateravatarwelcomepack.cpp +++ b/indra/newview/llfloateravatarwelcomepack.cpp @@ -28,8 +28,10 @@ #include "llviewerprecompiledheaders.h" #include "llfloateravatarwelcomepack.h" -#include "lluictrlfactory.h" #include "llmediactrl.h" +#include "lluictrlfactory.h" +#include "llviewercontrol.h" +#include "llweb.h" LLFloaterAvatarWelcomePack::LLFloaterAvatarWelcomePack(const LLSD& key) : LLFloater(key) @@ -52,6 +54,10 @@ bool LLFloaterAvatarWelcomePack::postBuild() if (mAvatarPicker) { mAvatarPicker->clearCache(); + mAvatarPicker->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); + std::string url = gSavedSettings.getString("AvatarWelcomePack"); + url = LLWeb::expandURLSubstitutions(url, LLSD()); + mAvatarPicker->navigateTo(url, HTTP_CONTENT_TEXT_HTML); } return true; diff --git a/indra/newview/llfloaterdestinations.cpp b/indra/newview/llfloaterdestinations.cpp index fad9693e8f..84fc4afcdd 100644 --- a/indra/newview/llfloaterdestinations.cpp +++ b/indra/newview/llfloaterdestinations.cpp @@ -28,7 +28,10 @@ #include "llviewerprecompiledheaders.h" #include "llfloaterdestinations.h" +#include "llmediactrl.h" #include "lluictrlfactory.h" +#include "llviewercontrol.h" +#include "llweb.h" LLFloaterDestinations::LLFloaterDestinations(const LLSD& key) @@ -43,6 +46,15 @@ LLFloaterDestinations::~LLFloaterDestinations() bool LLFloaterDestinations::postBuild() { enableResizeCtrls(true, true, false); + LLMediaCtrl* destinations = getChild("destination_guide_contents"); + destinations->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); + std::string url = gSavedSettings.getString("DestinationGuideURL"); + url = LLWeb::expandURLSubstitutions(url, LLSD()); + destinations->navigateTo(url, HTTP_CONTENT_TEXT_HTML); + + // If cookie is there, will set it now. Otherwise will have to wait for login completion + // which will also update destinations instance if it already exists. + LLViewerMedia::getInstance()->getOpenIDCookie(destinations); return true; } diff --git a/indra/newview/llfloatermarketplace.cpp b/indra/newview/llfloatermarketplace.cpp index 4abea64302..d626786591 100644 --- a/indra/newview/llfloatermarketplace.cpp +++ b/indra/newview/llfloatermarketplace.cpp @@ -49,6 +49,13 @@ bool LLFloaterMarketplace::postBuild() LLFloaterWebContent::postBuild(); mWebBrowser = getChild("marketplace_contents"); mWebBrowser->addObserver(this); + mWebBrowser->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); + std::string url = gSavedSettings.getString("MarketplaceURL"); + mWebBrowser->navigateTo(url, HTTP_CONTENT_TEXT_HTML); + + // If cookie is there, will set it now, Otherwise will have to wait for login completion + // which will also update marketplace instance if it already exists. + LLViewerMedia::getInstance()->getOpenIDCookie(mWebBrowser); return true; } diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index 7ee1b88f05..ec9af865cf 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -170,6 +170,12 @@ bool LLFloaterSearch::postBuild() LLFloaterWebContent::postBuild(); mWebBrowser = getChild("search_contents"); mWebBrowser->addObserver(this); + mWebBrowser->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); + + // If cookie is there, will set it now, Otherwise will have to wait for login completion + // which will also update search instance if it already exists. + LLViewerMedia::getInstance()->getOpenIDCookie(mWebBrowser); + getChildView("address")->setEnabled(false); getChildView("popexternal")->setEnabled(false); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 70596c935c..ec8c00bebc 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -83,6 +83,8 @@ extern bool gCubeSnapshot; // *TODO: Consider enabling mipmaps (they have been disabled for a long time). Likely has a significant performance impact for tiled/high texture repeat media. Mip generation in a shader may also be an option if necessary. constexpr bool USE_MIPMAPS = false; +constexpr S32 MAX_MEDIA_INSTANCES_DEFAULT = 8; +constexpr S32 MEDIA_INSTANCES_MIN_LIMIT = 6; // 4 'permanent' floaters plus reserve for dynamic ones void init_threaded_picker_load_dialog(LLPluginClassMedia* plugin, LLFilePicker::ELoadFilter filter, bool get_multiple) { @@ -214,6 +216,7 @@ static bool sViewerMediaMuteListObserverInitialized = false; LLViewerMedia::LLViewerMedia(): mAnyMediaShowing(false), mAnyMediaPlaying(false), +mMaxIntances(MAX_MEDIA_INSTANCES_DEFAULT), mSpareBrowserMediaSource(NULL) { } @@ -222,6 +225,7 @@ LLViewerMedia::~LLViewerMedia() { gIdleCallbacks.deleteFunction(LLViewerMedia::onIdle, NULL); mTeleportFinishConnection.disconnect(); + mMaxInstancesConnection.disconnect(); if (mSpareBrowserMediaSource != NULL) { delete mSpareBrowserMediaSource; @@ -235,6 +239,35 @@ void LLViewerMedia::initSingleton() gIdleCallbacks.addFunction(LLViewerMedia::onIdle, NULL); mTeleportFinishConnection = LLViewerParcelMgr::getInstance()-> setTeleportFinishedCallback(boost::bind(&LLViewerMedia::onTeleportFinished, this)); + + LLControlVariable* ctrl = gSavedSettings.getControl("PluginInstancesTotal"); + if (ctrl) + { + setMaxInstances(ctrl->getValue().asInteger()); + mMaxInstancesConnection = ctrl->getSignal()->connect([this](LLControlVariable* control, const LLSD& new_val, const LLSD& old_val) + { + setMaxInstances(new_val.asInteger()); + }); + } + else + { + setMaxInstances(MAX_MEDIA_INSTANCES_DEFAULT); + } +} + +void LLViewerMedia::setMaxInstances(S32 max_instances) +{ + const F32Gigabytes MIN_PHYSICAL_MEMORY(8); + LLMemory::updateMemoryInfo(); + F32Gigabytes physical_mem = LLMemory::getMaxMemKB(); + if (MIN_PHYSICAL_MEMORY > physical_mem) + { + mMaxIntances = llmax(max_instances - 2, MEDIA_INSTANCES_MIN_LIMIT); + } + else + { + mMaxIntances = llmax(max_instances, MEDIA_INSTANCES_MIN_LIMIT); + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -688,7 +721,6 @@ void LLViewerMedia::updateMedia(void *dummy_arg) static LLCachedControl inworld_media_enabled(gSavedSettings, "AudioStreamingMedia", true); static LLCachedControl inworld_audio_enabled(gSavedSettings, "AudioStreamingMusic", true); - static LLCachedControl max_instances(gSavedSettings, "PluginInstancesTotal", 8); static LLCachedControl max_normal(gSavedSettings, "PluginInstancesNormal", 2); static LLCachedControl max_low(gSavedSettings, "PluginInstancesLow", 4); static LLCachedControl max_cpu(gSavedSettings, "PluginInstancesCPULimit", 0.9); @@ -709,7 +741,7 @@ void LLViewerMedia::updateMedia(void *dummy_arg) LLPluginClassMedia::EPriority new_priority = LLPluginClassMedia::PRIORITY_NORMAL; - if(pimpl->isForcedUnloaded() || (impl_count_total >= (int)max_instances)) + if(pimpl->isForcedUnloaded() || (impl_count_total >= mMaxIntances)) { // Never load muted or failed impls. // Hard limit on the number of instances that will be loaded at one time @@ -869,7 +901,7 @@ void LLViewerMedia::updateMedia(void *dummy_arg) sLowestLoadableImplInterest = 0.0f; // Only do this calculation if we've hit the impl count limit -- up until that point we always need to load media data. - if(lowest_interest_loadable && (impl_count_total >= (int)max_instances)) + if(lowest_interest_loadable && (impl_count_total >= mMaxIntances)) { // Get the interest value of this impl's object for use by isInterestingEnough LLVOVolume *object = lowest_interest_loadable->getSomeObject(); @@ -1201,6 +1233,54 @@ LLCore::HttpHeaders::ptr_t LLViewerMedia::getHttpHeaders() return headers; } +bool LLViewerMedia::getOpenIDCookie(LLMediaCtrl* media_instance) const +{ + if (mOpenIDCookie.empty()) + { + return false; + } + + std::string authority = mOpenIDURL.mAuthority; + std::string::size_type hostStart = authority.find('@'); + if (hostStart == std::string::npos) + { + // no username/password + hostStart = 0; + } + else + { + // Hostname starts after the @. + // (If the hostname part is empty, this may put host_start at the end of the string. In that case, it will end up passing through an empty hostname, which is correct.) + ++hostStart; + } + std::string::size_type hostEnd = authority.rfind(':'); + if ((hostEnd == std::string::npos) || (hostEnd < hostStart)) + { + // no port + hostEnd = authority.size(); + } + + std::string cookie_host = authority.substr(hostStart, hostEnd - hostStart); + std::string cookie_name = ""; + std::string cookie_value = ""; + std::string cookie_path = ""; + bool httponly = true; + bool secure = true; + if (!parseRawCookie(mOpenIDCookie, cookie_name, cookie_value, cookie_path, httponly, secure)) + { + return false; + } + std::string cefUrl(std::string(mOpenIDURL.mURI) + "://" + std::string(mOpenIDURL.mAuthority)); + if (media_instance && media_instance->getMediaPlugin()) + { + media_instance->getMediaPlugin()->setCookie(cefUrl, cookie_name, cookie_value, cookie_host, + cookie_path, httponly, secure); + + media_instance->getMediaPlugin()->storeOpenIDCookie(cefUrl, cookie_name, cookie_value, + cookie_host, cookie_path, httponly, secure); + } + return true; +} ///////////////////////////////////////////////////////////////////////////////////////// void LLViewerMedia::setOpenIDCookie(const std::string& url) @@ -1267,7 +1347,7 @@ void LLViewerMedia::getOpenIDCookieCoro(std::string url) bool secure = true; LLViewerMedia* inst = getInstance(); - if (inst->parseRawCookie(inst->mOpenIDCookie, cookie_name, cookie_value, cookie_path, httponly, secure)) + if (parseRawCookie(inst->mOpenIDCookie, cookie_name, cookie_value, cookie_path, httponly, secure)) { // MAINT-5711 - inexplicably, the CEF setCookie function will no longer set the cookie if the // url and domain are not the same. This used to be my.sl.com and id.sl.com respectively and worked. @@ -1290,14 +1370,18 @@ void LLViewerMedia::getOpenIDCookieCoro(std::string url) }; for (MediaCookieInstance mci : media_cookie_instances) { - LLMediaCtrl* media_instance = LLFloaterReg::getInstance(mci.floater_name)->getChild(mci.browser_name); - if (media_instance && media_instance->getMediaPlugin()) + LLFloater *floaterp = LLFloaterReg::findInstance(mci.floater_name); + if (floaterp) { - media_instance->getMediaPlugin()->setCookie(cefUrl, cookie_name, cookie_value, cookie_host, - cookie_path, httponly, secure); - - media_instance->getMediaPlugin()->storeOpenIDCookie(cefUrl, cookie_name, cookie_value, - cookie_host, cookie_path, httponly, secure); + LLMediaCtrl* media_instance = floaterp->getChild(mci.browser_name); + if (media_instance && media_instance->getMediaPlugin()) + { + media_instance->getMediaPlugin()->setCookie(cefUrl, cookie_name, cookie_value, cookie_host, + cookie_path, httponly, secure); + + media_instance->getMediaPlugin()->storeOpenIDCookie(cefUrl, cookie_name, cookie_value, + cookie_host, cookie_path, httponly, secure); + } } } } diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index c17cf59815..1fc5bbc9e0 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -69,6 +69,7 @@ private: }; class LLViewerMediaImpl; +class LLMediaCtrl; class LLViewerMedia: public LLSingleton { @@ -162,22 +163,26 @@ public: LLSD getHeaders(); LLCore::HttpHeaders::ptr_t getHttpHeaders(); + bool getOpenIDCookie(LLMediaCtrl* media_instance) const; private: void onAuthSubmit(const LLSD& notification, const LLSD& response); - bool parseRawCookie(const std::string raw_cookie, std::string& name, std::string& value, std::string& path, bool& httponly, bool& secure); + static bool parseRawCookie(const std::string raw_cookie, std::string& name, std::string& value, std::string& path, bool& httponly, bool& secure); void setOpenIDCookie(const std::string& url); void onTeleportFinished(); static void openIDSetupCoro(std::string openidUrl, std::string openidToken); static void getOpenIDCookieCoro(std::string url); + void setMaxInstances(S32 max_instances); bool mAnyMediaShowing; bool mAnyMediaPlaying; + S32 mMaxIntances = 8; LLURL mOpenIDURL; std::string mOpenIDCookie; LLPluginClassMedia* mSpareBrowserMediaSource; boost::signals2::connection mTeleportFinishConnection; + boost::signals2::connection mMaxInstancesConnection; }; // Implementation functions not exported into header file diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 670a3b2939..5fed98dfe4 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2322,36 +2322,23 @@ void LLViewerWindow::initWorldUI() gToolBarView->setVisible(true); } - if (!gNonInteractive) + // Don't preload cef instances on low end hardware + const F32Gigabytes MIN_PHYSICAL_MEMORY(8); + F32Gigabytes physical_mem = LLMemory::getMaxMemKB(); + if (physical_mem <= 0) { - LLMediaCtrl* destinations = LLFloaterReg::getInstance("destinations")->getChild("destination_guide_contents"); - if (destinations) - { - destinations->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); - std::string url = gSavedSettings.getString("DestinationGuideURL"); - url = LLWeb::expandURLSubstitutions(url, LLSD()); - destinations->navigateTo(url, HTTP_CONTENT_TEXT_HTML); - } - LLMediaCtrl* avatar_welcome_pack = LLFloaterReg::getInstance("avatar_welcome_pack")->findChild("avatar_picker_contents"); - if (avatar_welcome_pack) - { - avatar_welcome_pack->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); - std::string url = gSavedSettings.getString("AvatarWelcomePack"); - url = LLWeb::expandURLSubstitutions(url, LLSD()); - avatar_welcome_pack->navigateTo(url, HTTP_CONTENT_TEXT_HTML); - } - LLMediaCtrl* search = LLFloaterReg::getInstance("search")->findChild("search_contents"); - if (search) - { - search->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); - } - LLMediaCtrl* marketplace = LLFloaterReg::getInstance("marketplace")->getChild("marketplace_contents"); - if (marketplace) - { - marketplace->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); - std::string url = gSavedSettings.getString("MarketplaceURL"); - marketplace->navigateTo(url, HTTP_CONTENT_TEXT_HTML); - } + LLMemory::updateMemoryInfo(); + physical_mem = LLMemory::getMaxMemKB(); + } + + if (!gNonInteractive && physical_mem > MIN_PHYSICAL_MEMORY) + { + LL_INFOS() << "Preloading cef instances" << LL_ENDL; + + LLFloaterReg::getInstance("destinations"); + LLFloaterReg::getInstance("avatar_welcome_pack"); + LLFloaterReg::getInstance("search"); + LLFloaterReg::getInstance("marketplace"); } } -- cgit v1.3 From dab2fa75dd3ab599aecc18ace6c688b7e78dfaa0 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 22 Oct 2025 16:57:04 +0300 Subject: #4876 Fix material not applying 1. Dupplicate call in llselectmgr can cause race conditions 2. lltooldragndrop wasn't updating server in one case --- indra/newview/llselectmgr.cpp | 1 - indra/newview/lltooldraganddrop.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index be4b449e2a..4762fc555d 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -2073,7 +2073,6 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) objectp->clearTEWaterExclusion(te); // Blank out most override data on the object and send to server - objectp->setRenderMaterialID(te, asset_id); if (should_preserve_transforms && preserved_override) { // Apply material with preserved transforms diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index d0c0bdb5ce..f78ff2226c 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1537,7 +1537,7 @@ void LLToolDragAndDrop::dropMaterialAllFaces(LLViewerObject* hit_obj, } else { - hit_obj->setRenderMaterialID(te, asset_id, false, true); + hit_obj->setRenderMaterialID(te, asset_id); } } } -- cgit v1.3 From 1c393aed78753093e77fd5ca595d4e6108cf858c Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:38:13 +0300 Subject: #4884 Crash at resetRegionCrossingTimer --- indra/newview/llviewermessage.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 16b7ac33b8..e9f6d7175e 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3139,7 +3139,10 @@ void process_crossed_region(LLMessageSystem* msg, void**) return; } LL_INFOS("Messaging") << "process_crossed_region()" << LL_ENDL; - gAgentAvatarp->resetRegionCrossingTimer(); + if (isAgentAvatarValid()) + { + gAgentAvatarp->resetRegionCrossingTimer(); + } U32 sim_ip; msg->getIPAddrFast(_PREHASH_RegionData, _PREHASH_SimIP, sim_ip); -- cgit v1.3 From 1fc0eca1198c8a8fcfc17e94f2961c934d65bc69 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:06:25 +0300 Subject: #4886 Crash on LLTeleportRequestViaLure::toOstream --- indra/newview/llagent.cpp | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) (limited to 'indra') diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 085155714a..e95d192f72 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -167,7 +167,7 @@ std::map LLTeleportRequest::sTeleportStatusName = { { kPending class LLTeleportRequestViaLandmark : public LLTeleportRequest { public: - LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId); + LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId, bool log = true); virtual ~LLTeleportRequestViaLandmark(); virtual void toOstream(std::ostream& os) const; @@ -179,6 +179,7 @@ public: protected: inline const LLUUID &getLandmarkId() const {return mLandmarkId;}; + bool mLogOnDestruction = true; private: LLUUID mLandmarkId; @@ -5008,16 +5009,25 @@ void LLTeleportRequest::toOstream(std::ostream& os) const //----------------------------------------------------------------------------- // LLTeleportRequestViaLandmark //----------------------------------------------------------------------------- -LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId) - : LLTeleportRequest(), - mLandmarkId(pLandmarkId) +LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId, bool log) + : LLTeleportRequest() + , mLandmarkId(pLandmarkId) + , mLogOnDestruction(true) { - LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created, " << *this << LL_ENDL; + if (log) + { + // Workaround to not log twice for LLTeleportRequestViaLure, besides this wouldn't have logged fully. + LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created, " << *this << LL_ENDL; + } } LLTeleportRequestViaLandmark::~LLTeleportRequestViaLandmark() { - LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark, " << *this << LL_ENDL; + if (mLogOnDestruction) + { + // Workaround to not crash on toOstream for derived classes and to not log twice. + LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark, " << *this << LL_ENDL; + } } void LLTeleportRequestViaLandmark::toOstream(std::ostream& os) const @@ -5047,16 +5057,20 @@ void LLTeleportRequestViaLandmark::restartTeleport() // LLTeleportRequestViaLure //----------------------------------------------------------------------------- -LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID &pLureId, bool pIsLureGodLike) - : LLTeleportRequestViaLandmark(pLureId), +LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID& pLureId, bool pIsLureGodLike) + : LLTeleportRequestViaLandmark(pLureId, false), mIsLureGodLike(pIsLureGodLike) { - LL_INFOS("Teleport") << "LLTeleportRequestViaLure created" << LL_ENDL; + LL_INFOS("Teleport") << "LLTeleportRequestViaLure created: " << *this << LL_ENDL; } LLTeleportRequestViaLure::~LLTeleportRequestViaLure() { - LL_INFOS("Teleport") << "~LLTeleportRequestViaLure" << LL_ENDL; + if (mLogOnDestruction) + { + LL_INFOS("Teleport") << "~LLTeleportRequestViaLure: " << *this << LL_ENDL; + mLogOnDestruction = false; + } } void LLTeleportRequestViaLure::toOstream(std::ostream& os) const -- cgit v1.3 From a12552408570e2e0e689cbdd4671f316568d0205 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Fri, 31 Oct 2025 15:29:07 +0200 Subject: #4847 log session_jitter and period_jitter with FPS info --- indra/newview/llviewerdisplay.cpp | 5 ++++- indra/newview/llviewerstats.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 4c408ec17d..35ac7919ac 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -240,8 +240,11 @@ void display_stats() if (gRecentFPSTime.getElapsedTimeF32() >= FPS_LOG_FREQUENCY) { LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("DS - FPS"); + LLTrace::Recording& recording = LLTrace::get_frame_recording().getLastRecording(); + F64 normalized_session_jitter = recording.getLastValue(LLStatViewer::NOTRMALIZED_FRAMETIME_JITTER_SESSION); + F64 normalized_period_jitter = recording.getLastValue(LLStatViewer::NORMALIZED_FRAMTIME_JITTER_PERIOD); F32 fps = gRecentFrameCount / FPS_LOG_FREQUENCY; - LL_INFOS() << llformat("FPS: %.02f", fps) << LL_ENDL; + LL_INFOS() << llformat("FPS: %.02f SESSION JITTER: %.4f PERIOD JITTER: %.4f", fps, normalized_session_jitter, normalized_period_jitter) << LL_ENDL; gRecentFrameCount = 0; gRecentFPSTime.reset(); } diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index 011269d7ee..1ac8b2f66b 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -229,6 +229,9 @@ extern LLTrace::EventStatHandle AVATAR_EDIT_TIME, extern LLTrace::EventStatHandle > OBJECT_CACHE_HIT_RATE; +extern LLTrace::SampleStatHandle NOTRMALIZED_FRAMETIME_JITTER_SESSION; +extern LLTrace::SampleStatHandle NORMALIZED_FRAMTIME_JITTER_PERIOD; + } class LLViewerStats : public LLSingleton -- cgit v1.3 From f7516a463114e3982b7d4cbd86645fc4369ffce9 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Fri, 31 Oct 2025 18:47:01 +0200 Subject: #4918 Crash at LLPluginProcessParent::poll Bugsplat shows that mutex is null. It's possible that no instances exist thus mutex wasn't initialized. --- indra/llplugin/llpluginprocessparent.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'indra') diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index 02c7d9507d..4f55d75e7d 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -982,15 +982,18 @@ void LLPluginProcessParent::poll(F64 timeout) } } - // Remove instances in the done state from the sInstances map. - LLCoros::LockType lock(*sInstancesMutex); - mapInstances_t::iterator itClean = sInstances.begin(); - while (itClean != sInstances.end()) + if (sInstancesMutex) { - if ((*itClean).second->isDone()) - itClean = sInstances.erase(itClean); - else - ++itClean; + // Remove instances in the done state from the sInstances map. + LLCoros::LockType lock(*sInstancesMutex); + mapInstances_t::iterator itClean = sInstances.begin(); + while (itClean != sInstances.end()) + { + if ((*itClean).second->isDone()) + itClean = sInstances.erase(itClean); + else + ++itClean; + } } } -- cgit v1.3 From 6979b1ff501d3db226d540475c17ef9c85fd15a0 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Mon, 3 Nov 2025 19:05:46 +0200 Subject: #4920 fix group categories not being displayed when one below is expanded --- indra/llui/llaccordionctrl.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index 1a64c2699d..2cd394476e 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -303,11 +303,8 @@ void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S3 return; LLRect panel_rect = panel->getRect(); panel_rect.setLeftTopAndSize( left, top, width, height); - if (panel->getRect() != panel_rect) - { - panel->reshape( width, height, 1); - panel->setRect(panel_rect); - } + panel->reshape( width, height, 1); + panel->setRect(panel_rect); } void LLAccordionCtrl::ctrlShiftVertical(LLView* panel, S32 delta) -- cgit v1.3 From d23379939c73daa7236fd279458f6037f8baf57e Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:13:34 +0200 Subject: #4879 An Emoji in a display name can affect text in chat An emoji was spliting inlineview thus affecting displayed text. --- indra/llui/lltextbase.cpp | 30 ++++++++++++++++++++++++++++++ indra/llui/lltextbase.h | 2 ++ 2 files changed, 32 insertions(+) (limited to 'indra') diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 1de12896eb..44151a4355 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1038,8 +1038,37 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s { LLStyleSP emoji_style; LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL; + LLTextSegment* segmentp = nullptr; + segment_vec_t::iterator seg_iter; + if (segments && segments->size() > 0) + { + seg_iter = segments->begin(); + segmentp = *seg_iter; + } for (S32 text_kitty = 0, text_len = static_cast(wstr.size()); text_kitty < text_len; text_kitty++) { + if (segmentp) + { + if (segmentp->getEnd() <= pos + text_kitty) + { + seg_iter++; + if (seg_iter != segments->end()) + { + segmentp = *seg_iter; + } + else + { + segmentp = nullptr; + } + } + if (segmentp && !segmentp->getPermitsEmoji()) + { + // Some segments, like LLInlineViewSegment do not permit splitting + // and should not be interrupted by emoji segments + continue; + } + } + llwchar code = wstr[text_kitty]; bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code); if (isEmoji) @@ -3448,6 +3477,7 @@ S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offs void LLTextSegment::updateLayout(const LLTextBase& editor) {} F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) { return draw_rect.mLeft; } bool LLTextSegment::canEdit() const { return false; } +bool LLTextSegment::getPermitsEmoji() const { return true; } void LLTextSegment::unlinkFromDocument(LLTextBase*) {} void LLTextSegment::linkToDocument(LLTextBase*) {} const LLUIColor& LLTextSegment::getColor() const { static const LLUIColor white = LLUIColorTable::instance().getColor("White", LLColor4::white); return white; } diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 07cd9a1ee5..50767a35b3 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -87,6 +87,7 @@ public: virtual void updateLayout(const class LLTextBase& editor); virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); virtual bool canEdit() const; + virtual bool getPermitsEmoji() const; virtual void unlinkFromDocument(class LLTextBase* editor); virtual void linkToDocument(class LLTextBase* editor); @@ -255,6 +256,7 @@ public: /*virtual*/ void updateLayout(const class LLTextBase& editor); /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); /*virtual*/ bool canEdit() const { return false; } + /*virtual*/ bool getPermitsEmoji() const { return false; } /*virtual*/ void unlinkFromDocument(class LLTextBase* editor); /*virtual*/ void linkToDocument(class LLTextBase* editor); -- cgit v1.3 From 11ccdb5e32f4c997c0dd65464c1c7c7b469aa4d2 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 31 Oct 2025 16:39:17 +0100 Subject: Fix XUI parser warnings: * LLFloaterMarketplace::postBuild calls parent method which already tries to find the webbrowser control with a different name and adds itself as observer * Incorrect attribute name "tooltip" in panel_preferences_move.xml --- indra/newview/llfloatermarketplace.cpp | 6 +++--- indra/newview/skins/default/xui/en/floater_marketplace.xml | 2 +- indra/newview/skins/default/xui/en/panel_preferences_move.xml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatermarketplace.cpp b/indra/newview/llfloatermarketplace.cpp index d626786591..7316d7617d 100644 --- a/indra/newview/llfloatermarketplace.cpp +++ b/indra/newview/llfloatermarketplace.cpp @@ -46,9 +46,9 @@ void LLFloaterMarketplace::onClose(bool app_quitting) bool LLFloaterMarketplace::postBuild() { - LLFloaterWebContent::postBuild(); - mWebBrowser = getChild("marketplace_contents"); - mWebBrowser->addObserver(this); + if (!LLFloaterWebContent::postBuild()) + return false; + mWebBrowser->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); std::string url = gSavedSettings.getString("MarketplaceURL"); mWebBrowser->navigateTo(url, HTTP_CONTENT_TEXT_HTML); diff --git a/indra/newview/skins/default/xui/en/floater_marketplace.xml b/indra/newview/skins/default/xui/en/floater_marketplace.xml index 99fb3a1ad8..40bf674d2d 100644 --- a/indra/newview/skins/default/xui/en/floater_marketplace.xml +++ b/indra/newview/skins/default/xui/en/floater_marketplace.xml @@ -169,7 +169,7 @@ follows="all" layout="topleft" left="0" - name="marketplace_contents" + name="webbrowser" top="0"/> Date: Tue, 4 Nov 2025 18:08:20 +0100 Subject: Use correct widget name for marketplace floater --- indra/newview/llviewermedia.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index ec8c00bebc..6930165aac 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1365,7 +1365,7 @@ void LLViewerMedia::getOpenIDCookieCoro(std::string url) }; struct MediaCookieInstance media_cookie_instances[] = { {"search", "search_contents" }, - {"marketplace", "marketplace_contents" }, + {"marketplace", "webbrowser" }, {"destinations", "destination_guide_contents" }, }; for (MediaCookieInstance mci : media_cookie_instances) -- cgit v1.3 From 5e11448e4fccb56d03eb9a7b18774b8c550683b7 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 4 Nov 2025 18:43:51 +0100 Subject: Fix remaining cases of webbrowser-related XUI warnings --- indra/newview/llfloatersearch.cpp | 9 ++++----- indra/newview/llviewermedia.cpp | 2 +- indra/newview/skins/default/xui/en/floater_search.xml | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index ec9af865cf..9b7a4e5134 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -161,15 +161,14 @@ void LLFloaterSearch::initiateSearch(const LLSD& tokens) // Naviation to the calculated URL - we know it's HTML so we can // tell the media system not to bother with the MIME type check. - LLMediaCtrl* search_browser = findChild("search_contents"); - search_browser->navigateTo(url, HTTP_CONTENT_TEXT_HTML); + mWebBrowser->navigateTo(url, HTTP_CONTENT_TEXT_HTML); } bool LLFloaterSearch::postBuild() { - LLFloaterWebContent::postBuild(); - mWebBrowser = getChild("search_contents"); - mWebBrowser->addObserver(this); + if (!LLFloaterWebContent::postBuild()) + return false; + mWebBrowser->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); // If cookie is there, will set it now, Otherwise will have to wait for login completion diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 6930165aac..8f229850c0 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1364,7 +1364,7 @@ void LLViewerMedia::getOpenIDCookieCoro(std::string url) std::string browser_name; }; struct MediaCookieInstance media_cookie_instances[] = { - {"search", "search_contents" }, + {"search", "webbrowser" }, {"marketplace", "webbrowser" }, {"destinations", "destination_guide_contents" }, }; diff --git a/indra/newview/skins/default/xui/en/floater_search.xml b/indra/newview/skins/default/xui/en/floater_search.xml index 43c4aa1b9d..8891ebcd87 100644 --- a/indra/newview/skins/default/xui/en/floater_search.xml +++ b/indra/newview/skins/default/xui/en/floater_search.xml @@ -170,7 +170,7 @@ layout="topleft" left="0" trusted_content="true" - name="search_contents" + name="webbrowser" top="0"/> Date: Wed, 5 Nov 2025 12:33:24 +0200 Subject: #4785 The Apply button is not activated after changing a Material --- indra/newview/llfloaterregioninfo.cpp | 34 ++++++++++++++++++++++------------ indra/newview/llfloaterregioninfo.h | 1 + 2 files changed, 23 insertions(+), 12 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 334f32d051..2b243ef972 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -270,10 +270,12 @@ bool LLFloaterRegionInfo::postBuild() static LLCachedControl feature_pbr_terrain_transforms_enabled(gSavedSettings, "RenderTerrainPBRTransformsEnabled", false); if (!feature_pbr_terrain_transforms_enabled() || !feature_pbr_terrain_enabled()) { + LL_INFOS("Terrain") << "Building region terrain panel from panel_region_terrain.xml" << LL_ENDL; panel->buildFromFile("panel_region_terrain.xml"); } else { + LL_INFOS("Terrain") << "Building region terrain panel from panel_region_terrain_texture_transform.xml" << LL_ENDL; panel->buildFromFile("panel_region_terrain_texture_transform.xml"); } mTab->addTabPanel(panel); @@ -1999,18 +2001,7 @@ void LLPanelRegionTerrainInfo::initMaterialCtrl(LLTextureCtrl*& ctrl, const std: ctrl->setCommitCallback( [this, index](LLUICtrl* ctrl, const LLSD& param) { - if (!mMaterialScaleUCtrl[index] - || !mMaterialScaleVCtrl[index] - || !mMaterialRotationCtrl[index] - || !mMaterialOffsetUCtrl[index] - || !mMaterialOffsetVCtrl[index]) return; - - mMaterialScaleUCtrl[index]->setValue(1.f); - mMaterialScaleVCtrl[index]->setValue(1.f); - mMaterialRotationCtrl[index]->setValue(0.f); - mMaterialOffsetUCtrl[index]->setValue(0.f); - mMaterialOffsetVCtrl[index]->setValue(0.f); - onChangeAnything(); + callbackMaterialCommit(index); }); } @@ -2098,6 +2089,25 @@ bool LLPanelRegionTerrainInfo::callbackBakeTerrain(const LLSD& notification, con return false; } +void LLPanelRegionTerrainInfo::callbackMaterialCommit(S32 index) +{ + // These can be null if 'transforms' panel was not inited + if (mMaterialScaleUCtrl[index] + && mMaterialScaleVCtrl[index] + && mMaterialRotationCtrl[index] + && mMaterialOffsetUCtrl[index] + && mMaterialOffsetVCtrl[index]) + { + mMaterialScaleUCtrl[index]->setValue(1.f); + mMaterialScaleVCtrl[index]->setValue(1.f); + mMaterialRotationCtrl[index]->setValue(0.f); + mMaterialOffsetUCtrl[index]->setValue(0.f); + mMaterialOffsetVCtrl[index]->setValue(0.f); + } + + onChangeAnything(); +} + ///////////////////////////////////////////////////////////////////////////// // LLPanelEstateInfo // diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index a8631c36ca..0036df9c3d 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -272,6 +272,7 @@ public: static void onClickBakeTerrain(void*); bool callbackBakeTerrain(const LLSD& notification, const LLSD& response); bool callbackTextureHeights(const LLSD& notification, const LLSD& response); + void callbackMaterialCommit(S32 index); protected: bool sendUpdate() override; -- cgit v1.3 From 7a7215b85223aa8e8a1cd84478e1a7aa24b5307d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 5 Nov 2025 14:22:09 +0200 Subject: #1006 Assume default material is valid for terrain assigment --- indra/newview/llfloaterregioninfo.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indra') diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 2b243ef972..5ec7e94fcc 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -1492,6 +1492,11 @@ bool LLPanelRegionTerrainInfo::validateMaterials() const LLUUID& material_asset_id = material_ctrl->getImageAssetID(); llassert(material_asset_id.notNull()); if (material_asset_id.isNull()) { return false; } + if (material_asset_id == BLANK_MATERIAL_ASSET_ID) + { + // Default/Blank material is valid by default + continue; + } const LLFetchedGLTFMaterial* material = gGLTFMaterialList.getMaterial(material_asset_id); if (!material->isLoaded()) { -- cgit v1.3 From 3694df79bf11cf9ad37ed74b7b9821d674f78730 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Thu, 6 Nov 2025 18:52:56 +0200 Subject: #4656 Don't scroll to a child that is already in view and can not fit --- indra/llui/llaccordionctrltab.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index bdf93348bb..828bfb289b 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -479,7 +479,34 @@ void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl) // Translate to parent coordinatess to check if we are in visible rectangle rect.translate(getRect().mLeft, getRect().mBottom); - if (!getRect().contains(rect)) + bool needs_to_scroll = false; + const LLRect &acc_rect = getRect(); + if (!acc_rect.contains(rect)) + { + if (acc_rect.mTop < rect.mBottom || acc_rect.mBottom > rect.mTop) + { + // Content fully not in view + needs_to_scroll = true; + } + else if (acc_rect.getHeight() >= rect.getHeight()) + { + // Content can be displayed fully, but only partially in view + needs_to_scroll = true; + } + else if (acc_rect.mTop <= rect.mTop || acc_rect.mBottom >= rect.mBottom) + { + // Intersects, but too big to be displayed fully + S32 covered_height = acc_rect.mTop > rect.mTop ? rect.mTop - acc_rect.mBottom : acc_rect.mTop - rect.mBottom; + constexpr F32 covered_ratio = 0.7f; + if (covered_height < covered_ratio * acc_rect.getHeight()) + { + // Try to show bigger portion of the content + needs_to_scroll = true; + } + } + // else too big and in the middle of the view as is + } + if (needs_to_scroll) { // for accordition's scroll, height is in pixels // Back to local coords and calculate position for scroller -- cgit v1.3 From 4e78b4c9fe429f75f1ff99a8af59c277276c2bc1 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Thu, 6 Nov 2025 20:16:55 +0200 Subject: #4651 Distinguish logout crashes from other crashes --- indra/newview/llappviewerwin32.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index a951338138..64b506a335 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -179,7 +179,14 @@ namespace // If marker doesn't exist, create a marker with 'other' code for next launch // otherwise don't override existing file // Any unmarked crashes will be considered as freezes - app->createErrorMarker(LAST_EXEC_OTHER_CRASH); + if (app->logoutRequestSent()) + { + app->createErrorMarker(LAST_EXEC_LOGOUT_CRASH); + } + else + { + app->createErrorMarker(LAST_EXEC_OTHER_CRASH); + } } } // MDSCB_EXCEPTIONCODE -- cgit v1.3 From bcecb3dc10579c4403d92aeb040fc550b08457a9 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Sun, 9 Nov 2025 10:33:36 +0200 Subject: #4949 QueuedRequest destructor crash --- indra/llcommon/llqueuedthread.cpp | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index 0196a24b18..efeeb1340e 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -80,7 +80,7 @@ void LLQueuedThread::shutdown() mRequestQueue.close(); } - S32 timeout = 100; + S32 timeout = 50; for ( ; timeout>0; timeout--) { if (isStopped()) @@ -101,19 +101,34 @@ void LLQueuedThread::shutdown() } QueuedRequest* req; - S32 active_count = 0; + S32 queued_count = 0; + bool has_active = false; + lockData(); while ( (req = (QueuedRequest*)mRequestHash.pop_element()) ) { - if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS) + if (req->getStatus() == STATUS_INPROGRESS) + { + has_active = true; + req->setFlags(FLAG_ABORT | FLAG_AUTO_COMPLETE); + continue; + } + if (req->getStatus() == STATUS_QUEUED) { - ++active_count; + ++queued_count; req->setStatus(STATUS_ABORTED); // avoid assert in deleteRequest } req->deleteRequest(); } - if (active_count) + unlockData(); + if (queued_count) + { + LL_WARNS() << "~LLQueuedThread() called with unpocessed requests: " << queued_count << LL_ENDL; + } + if (has_active) { - LL_WARNS() << "~LLQueuedThread() called with active requests: " << active_count << LL_ENDL; + LL_WARNS() << "~LLQueuedThread() called with active requests!" << LL_ENDL; + ms_sleep(100); // last chance for request to finish + printQueueStats(); } mRequestQueue.close(); @@ -570,7 +585,12 @@ LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U3 LLQueuedThread::QueuedRequest::~QueuedRequest() { - llassert_always(mStatus == STATUS_DELETE); + if (mStatus != STATUS_DELETE) + { + // The only method to delete a request is deleteRequest(), + // it should have set the status to STATUS_DELETE + LL_ERRS() << "LLQueuedThread::QueuedRequest deleted with status " << mStatus << LL_ENDL; + } } //virtual -- cgit v1.3 From 0ceefe6d270a4688720287f88b3bd5e2509909c1 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Wed, 12 Nov 2025 12:14:19 -0800 Subject: Disable shader profiles on RDNA3.5 to prevent freezes on startup. (#4992) * Disable shader profiling for certain AMD Radeon GPUs Added logic to detect AMD Radeon 8060 GPUs and disable shader profiling to prevent client freezes and instability. Introduced sCanProfile flag in LLGLSLShader and mSkipProfiling in LLFeatureManager to control profiling behavior based on detected hardware. * Add RDNA3.5 and check the vendor string for a known current family of bad drivers * Update llfeaturemanager.cpp * Make sure to check that this is a Radeon. --- indra/llrender/llglslshader.cpp | 3 ++- indra/llrender/llglslshader.h | 1 + indra/newview/llfeaturemanager.cpp | 45 ++++++++++++++++++++++++++++++++++++++ indra/newview/llfeaturemanager.h | 2 ++ 4 files changed, 50 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 9cd5dc8145..a268ea07bb 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -57,6 +57,7 @@ S32 LLGLSLShader::sIndexedTextureChannels = 0; U32 LLGLSLShader::sMaxGLTFMaterials = 0; U32 LLGLSLShader::sMaxGLTFNodes = 0; bool LLGLSLShader::sProfileEnabled = false; +bool LLGLSLShader::sCanProfile = true; std::set LLGLSLShader::sInstances; LLGLSLShader::defines_map_t LLGLSLShader::sGlobalDefines; U64 LLGLSLShader::sTotalTimeElapsed = 0; @@ -267,7 +268,7 @@ void LLGLSLShader::placeProfileQuery(bool for_runtime) bool LLGLSLShader::readProfileQuery(bool for_runtime, bool force_read) { - if (sProfileEnabled || for_runtime) + if ((sProfileEnabled || for_runtime) && sCanProfile) { if (!mProfilePending) { diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 4702a27cc5..272a99aaa5 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -160,6 +160,7 @@ public: static std::set sInstances; static bool sProfileEnabled; + static bool sCanProfile; LLGLSLShader(); ~LLGLSLShader(); diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index 052278f731..9f3df54b05 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -404,8 +404,53 @@ F32 logExceptionBenchmark() } #endif +bool checkRDNA35() +{ + // This checks if we're running on an RDNA3.5 GPU. You're only going to see these on AMD's APUs. + // As of driver version 25, we're seeing stalls in some of our queries. + // This appears to be a driver bug, and appears to be specific RDNA3.5 APUs. + // There's multiples of these guys, so we just use this function to check if that GPU is on the list of known RDNA3.5 APUs. + // - Geenz 11/12/2025 + std::array rdna35GPUs = { + "8060S", + "8050S", + "8040S", + "860M", + "840M", + "890M", + "880M" + }; + + for (const auto& gpu_name : rdna35GPUs) + { + if (gGLManager.getRawGLString().find(gpu_name) != std::string::npos) + { + LL_WARNS("RenderInit") << "Detected AMD RDNA3.5 GPU (" << gpu_name << ")." << LL_ENDL; + return true; + } + } + + return false; +} + bool LLFeatureManager::loadGPUClass() { + // This is a hack for certain AMD GPUs in newer driver versions on certain APUs. + // These GPUs will show inconsistent freezes when attempting to run shader profiles against them. + // This is extremely problematic as it can lead to: + // - Login freezes + // - Inability to start the client + // - Completely random avatars triggering a freeze + // As a result, we filter out these GPUs for shader profiling. + // - Geenz 11/11/2025 + + if (gGLManager.getRawGLString().find("Radeon") != std::string::npos && checkRDNA35() && gGLManager.mDriverVersionVendorString.find("25.") != std::string::npos) + { + LL_WARNS("RenderInit") << "Detected AMD RDNA3.5 GPU on a known bad driver; disabling shader profiling to prevent freezes." << LL_ENDL; + mSkipProfiling = true; + LLGLSLShader::sCanProfile = false; + } + if (!gSavedSettings.getBOOL("SkipBenchmark")) { F32 class1_gbps = gSavedSettings.getF32("RenderClass1MemoryBandwidth"); diff --git a/indra/newview/llfeaturemanager.h b/indra/newview/llfeaturemanager.h index 22de6afbae..d04b89cb60 100644 --- a/indra/newview/llfeaturemanager.h +++ b/indra/newview/llfeaturemanager.h @@ -123,6 +123,7 @@ public: S32 getVersion() const { return mTableVersion; } void setSafe(const bool safe) { mSafe = safe; } bool isSafe() const { return mSafe; } + bool skipProfiling() const { return mSkipProfiling; } LLFeatureList *findMask(const std::string& name); bool maskFeatures(const std::string& name); @@ -170,6 +171,7 @@ protected: F32 mExpectedGLVersion; //expected GL version according to gpu table std::string mGPUString; bool mGPUSupported; + bool mSkipProfiling = false; }; inline -- cgit v1.3 From ad6008a5880dff8691f5fce56b7fbfc5ea8b1626 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Fri, 14 Nov 2025 11:40:05 -0800 Subject: Disable the benchmark & occlusion on RDNA3.5 (#4999) --- indra/newview/llfeaturemanager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index 9f3df54b05..bbf67ffb36 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -446,9 +446,9 @@ bool LLFeatureManager::loadGPUClass() if (gGLManager.getRawGLString().find("Radeon") != std::string::npos && checkRDNA35() && gGLManager.mDriverVersionVendorString.find("25.") != std::string::npos) { - LL_WARNS("RenderInit") << "Detected AMD RDNA3.5 GPU on a known bad driver; disabling shader profiling to prevent freezes." << LL_ENDL; - mSkipProfiling = true; - LLGLSLShader::sCanProfile = false; + LL_WARNS("RenderInit") << "Detected AMD RDNA3.5 GPU on a known bad driver; disabling benchmark and occlusion culling to prevent freezes." << LL_ENDL; + gSavedSettings.setBOOL("SkipBenchmark", true); + gSavedSettings.setBOOL("UseOcclusion", false); } if (!gSavedSettings.getBOOL("SkipBenchmark")) -- cgit v1.3 From c6f1b725bfb45af31182771a445edc3bac9cbeac Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 1 Dec 2025 20:03:07 +0200 Subject: #5078 MacOS Build fix --- indra/newview/llviewerwindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 692e192edb..c474bff2dc 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1884,7 +1884,8 @@ LLViewerWindow::LLViewerWindow(const Params& p) // pass its value right now. Instead, pass it a nullary function that // will, when we later need it, return the value of gKeyboard. // boost::lambda::var() constructs such a functor on the fly. - mWindowListener = std::make_unique(this, boost::lambda::var(gKeyboard)); + LLWindowListener::KeyboardGetter getter = [](){ return gKeyboard; }; + mWindowListener = std::make_unique(this, getter); mViewerWindowListener = std::make_unique(this); mSystemChannel.reset(new LLNotificationChannel("System", "Visible", LLNotificationFilters::includeEverything)); -- cgit v1.3 From c62735adc8fa5a31f6c306c6919c7e32e956976d Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 1 Dec 2025 17:29:44 -0500 Subject: Fix alpha debug overlay rendering in probes and breaking scene lighting (#5058) Signed-off-by: Rye --- indra/newview/lldrawpoolalpha.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index 87b6ce6cb3..bc45734e66 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -278,7 +278,7 @@ void LLDrawPoolAlpha::forwardRender(bool rigged) void LLDrawPoolAlpha::renderDebugAlpha() { - if (sShowDebugAlpha) + if (sShowDebugAlpha && !gCubeSnapshot) { gHighlightProgram.bind(); gGL.diffuseColor4f(1, 0, 0, 1); -- cgit v1.3 From 7991ae892ef0f717636c0fe3c0eff0a5f92e6bb8 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 2 Dec 2025 00:44:56 +0200 Subject: #5084 Ressurect Watchdog --- indra/newview/app_settings/settings.xml | 19 +++++++++++---- indra/newview/llappviewer.cpp | 42 ++++++++++++++++++--------------- indra/newview/llappviewer.h | 8 ++++--- indra/newview/llappviewerwin32.cpp | 11 +++++++-- indra/newview/llwatchdog.cpp | 12 +++++++++- 5 files changed, 63 insertions(+), 29 deletions(-) (limited to 'indra') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 3dbb5dca1f..fe31a00ba3 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4484,13 +4484,24 @@ MainloopTimeoutDefault Comment - Timeout duration for mainloop lock detection, in seconds. + Timeout duration for mainloop lock detection during teleports, login and logout, in seconds. Persist 1 Type F32 Value - 60.0 + 120.0 + + MainloopTimeoutStarted + + Comment + Timeout duration for mainloop lock detection when logged in and not teleporting, in seconds. + Persist + 1 + Type + F32 + Value + 30.0 MapScale @@ -13862,13 +13873,13 @@ WatchdogEnabled Comment - Controls whether the thread watchdog timer is activated. Value is boolean. Set to -1 to defer to built-in default. + Controls whether the thread watchdog timer is activated. Value is S32. Set to -1 to defer to built-in default. Persist 0 Type S32 Value - 0 + 1 WaterGLFogDensityScale diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 72bfdcf83c..f6f5f1717b 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3145,7 +3145,7 @@ bool LLAppViewer::initWindow() // Need to load feature table before cheking to start watchdog. bool use_watchdog = false; - int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled"); + S32 watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled"); if (watchdog_enabled_setting == -1) { use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled"); @@ -5820,12 +5820,12 @@ void LLAppViewer::forceExceptionThreadCrash() thread->start(); } -void LLAppViewer::initMainloopTimeout(std::string_view state, F32 secs) +void LLAppViewer::initMainloopTimeout(std::string_view state) { if (!mMainloopTimeout) { mMainloopTimeout = new LLWatchdogTimeout(); - resumeMainloopTimeout(state, secs); + resumeMainloopTimeout(state); } } @@ -5838,17 +5838,11 @@ void LLAppViewer::destroyMainloopTimeout() } } -void LLAppViewer::resumeMainloopTimeout(std::string_view state, F32 secs) +void LLAppViewer::resumeMainloopTimeout(std::string_view state) { if (mMainloopTimeout) { - if (secs < 0.0f) - { - static LLCachedControl mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60.f); - secs = mainloop_timeout; - } - - mMainloopTimeout->setTimeout(secs); + mMainloopTimeout->setTimeout(getMainloopTimeoutSec()); mMainloopTimeout->start(state); } } @@ -5861,23 +5855,33 @@ void LLAppViewer::pauseMainloopTimeout() } } -void LLAppViewer::pingMainloopTimeout(std::string_view state, F32 secs) +void LLAppViewer::pingMainloopTimeout(std::string_view state) { LL_PROFILE_ZONE_SCOPED_CATEGORY_APP; if (mMainloopTimeout) { - if (secs < 0.0f) - { - static LLCachedControl mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60); - secs = mainloop_timeout; - } - - mMainloopTimeout->setTimeout(secs); + mMainloopTimeout->setTimeout(getMainloopTimeoutSec()); mMainloopTimeout->ping(state); } } + +F32 LLAppViewer::getMainloopTimeoutSec() const +{ + if (LLStartUp::getStartupState() == STATE_STARTED + && gAgent.getTeleportState() == LLAgent::TELEPORT_NONE) + { + static LLCachedControl mainloop_started(gSavedSettings, "MainloopTimeoutStarted", 30.f); + return mainloop_started(); + } + else + { + static LLCachedControl mainloop_default(gSavedSettings, "MainloopTimeoutDefault", 120.f); + return mainloop_default(); + } +} + void LLAppViewer::handleLoginComplete() { gLoggedInTime.start(); diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index c12b2e83ef..61c206bcec 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -204,11 +204,13 @@ public: // For thread debugging. // llstartup needs to control init. // llworld, send_agent_pause() also controls pause/resume. - void initMainloopTimeout(std::string_view state, F32 secs = -1.0f); + void initMainloopTimeout(std::string_view state); void destroyMainloopTimeout(); void pauseMainloopTimeout(); - void resumeMainloopTimeout(std::string_view state = "", F32 secs = -1.0f); - void pingMainloopTimeout(std::string_view state, F32 secs = -1.0f); + void resumeMainloopTimeout(std::string_view state = ""); + void pingMainloopTimeout(std::string_view state); + + F32 getMainloopTimeoutSec() const; // Handle the 'login completed' event. // *NOTE:Mani Fix this for login abstraction!! diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index a951338138..0620b625d9 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -176,10 +176,17 @@ namespace LLAppViewer* app = LLAppViewer::instance(); if (!app->isSecondInstance() && !app->errorMarkerExists()) { - // If marker doesn't exist, create a marker with 'other' code for next launch + // If marker doesn't exist, create a marker with 'other' or 'logout' code for next launch // otherwise don't override existing file // Any unmarked crashes will be considered as freezes - app->createErrorMarker(LAST_EXEC_OTHER_CRASH); + if (app->logoutRequestSent()) + { + app->createErrorMarker(LAST_EXEC_LOGOUT_CRASH); + } + else + { + app->createErrorMarker(LAST_EXEC_OTHER_CRASH); + } } } // MDSCB_EXCEPTIONCODE diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp index bf171fe954..614d1afc2a 100644 --- a/indra/newview/llwatchdog.cpp +++ b/indra/newview/llwatchdog.cpp @@ -28,6 +28,7 @@ #include "llviewerprecompiledheaders.h" #include "llwatchdog.h" #include "llthread.h" +#include "llappviewer.h" constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U; @@ -240,7 +241,16 @@ void LLWatchdog::run() { mTimer->stop(); } - + if (LLAppViewer::instance()->logoutRequestSent()) + { + LLAppViewer::instance()->createErrorMarker(LAST_EXEC_LOGOUT_FROZE); + } + else + { + LLAppViewer::instance()->createErrorMarker(LAST_EXEC_FROZE); + } + // Todo1: warn user? + // Todo2: We probably want to report even if 5 seconds passed, just not error 'yet'. LL_ERRS() << "Watchdog timer expired; assuming viewer is hung and crashing" << LL_ENDL; } } -- cgit v1.3 From 35661866fbaa5d1b1437c998d61121cf651839a5 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 2 Dec 2025 15:49:30 +0200 Subject: #5086 Fix 'Copy SLURL' generating obsolete http link use https --- indra/llcommon/lluriparser.cpp | 2 +- indra/llprimitive/llmediaentry.cpp | 2 +- indra/llui/llurlentry.cpp | 4 ++-- indra/newview/llfilepicker_mac.h | 2 +- indra/newview/llfloater360capture.cpp | 2 +- indra/newview/llimprocessing.cpp | 12 +++++++++++- indra/newview/llpanelmediasettingssecurity.cpp | 2 +- indra/newview/llurllineeditorctrl.cpp | 4 +++- indra/newview/llviewernetwork.cpp | 4 ++-- indra/newview/llvovolume.cpp | 8 ++++---- indra/newview/llwindowlistener.cpp | 4 ++-- indra/newview/tests/llslurl_test.cpp | 20 ++++++++++---------- indra/newview/tests/llviewernetwork_test.cpp | 2 +- 13 files changed, 40 insertions(+), 28 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp index 33a48d970d..1d246bb70e 100644 --- a/indra/llcommon/lluriparser.cpp +++ b/indra/llcommon/lluriparser.cpp @@ -33,7 +33,7 @@ LLUriParser::LLUriParser(const std::string& u) : mTmpScheme(false), mNormalizedT { if (u.find("://") == std::string::npos) { - mNormalizedUri = "http://"; + mNormalizedUri = "https://"; mTmpScheme = true; } diff --git a/indra/llprimitive/llmediaentry.cpp b/indra/llprimitive/llmediaentry.cpp index b5b17c53aa..48c6ba1fa5 100644 --- a/indra/llprimitive/llmediaentry.cpp +++ b/indra/llprimitive/llmediaentry.cpp @@ -74,7 +74,7 @@ const char* LLMediaEntry::WHITELIST_KEY = MEDIA_WHITELIST_KEY_STR; const char* LLMediaEntry::PERMS_INTERACT_KEY = MEDIA_PERMS_INTERACT_KEY_STR; const char* LLMediaEntry::PERMS_CONTROL_KEY = MEDIA_PERMS_CONTROL_KEY_STR; -#define DEFAULT_URL_PREFIX "http://" +#define DEFAULT_URL_PREFIX "https://" // Constructor(s) LLMediaEntry::LLMediaEntry() : diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 95603d7ed5..bba80dd20e 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -234,7 +234,7 @@ bool LLUrlEntryBase::isWikiLinkCorrect(const std::string &labeled_url) const || label.find("www.") != std::string::npos) && label.find("://") == std::string::npos) { - label = "http://" + label; + label = "https://" + label; } return !LLUrlRegistry::instance().hasUrl(label); @@ -315,7 +315,7 @@ std::string LLUrlEntryHTTP::getUrl(const std::string &string) const { if (string.find("://") == std::string::npos) { - return "http://" + escapeUrl(string); + return "https://" + escapeUrl(string); } return escapeUrl(string); } diff --git a/indra/newview/llfilepicker_mac.h b/indra/newview/llfilepicker_mac.h index f0e9ce81dc..d0374c5a08 100644 --- a/indra/newview/llfilepicker_mac.h +++ b/indra/newview/llfilepicker_mac.h @@ -44,7 +44,7 @@ std::unique_ptr> doLoadDialog(const std::vector* allowed_types, unsigned int flags, void (*callback)(bool, std::vector&, void*), diff --git a/indra/newview/llfloater360capture.cpp b/indra/newview/llfloater360capture.cpp index 25970f8a08..95e7b9af41 100644 --- a/indra/newview/llfloater360capture.cpp +++ b/indra/newview/llfloater360capture.cpp @@ -716,7 +716,7 @@ void LLFloater360Capture::onSaveLocalBtn() { // region name and URL std::string region_name; // no sensible default - std::string region_url("http://secondlife.com"); + std::string region_url("https://secondlife.com"); LLViewerRegion* region = gAgent.getRegion(); if (region) { diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index fe567ac7ec..779ed725ac 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -202,9 +202,19 @@ void inventory_offer_handler(LLOfferInfo* info) auto indx = msg.find(" ( http://slurl.com/secondlife/"); if (indx == std::string::npos) { - // try to find new slurl host + // https + indx = msg.find(" ( https://slurl.com/secondlife/"); + } + if (indx == std::string::npos) + { + // try to find new slurl http host indx = msg.find(" ( http://maps.secondlife.com/secondlife/"); } + if (indx == std::string::npos) + { + // try to find new slurl https host + indx = msg.find(" ( https://maps.secondlife.com/secondlife/"); + } if (indx >= 0) { LLStringUtil::truncate(msg, indx); diff --git a/indra/newview/llpanelmediasettingssecurity.cpp b/indra/newview/llpanelmediasettingssecurity.cpp index 6e4e9f426d..68e2808a83 100644 --- a/indra/newview/llpanelmediasettingssecurity.cpp +++ b/indra/newview/llpanelmediasettingssecurity.cpp @@ -220,7 +220,7 @@ const std::string LLPanelMediaSettingsSecurity::makeValidUrl( const std::string& if ( candidate_url.scheme().empty() ) { // build a URL comprised of default scheme and the original fragment - const std::string default_scheme( "http://" ); + const std::string default_scheme( "https://" ); return default_scheme + src_url; }; diff --git a/indra/newview/llurllineeditorctrl.cpp b/indra/newview/llurllineeditorctrl.cpp index de0ed645eb..871bf36ed3 100644 --- a/indra/newview/llurllineeditorctrl.cpp +++ b/indra/newview/llurllineeditorctrl.cpp @@ -84,7 +84,9 @@ void LLURLLineEditor::copyEscapedURLToClipboard() const std::string unescaped_text = wstring_to_utf8str(mText.getWString().substr(left_pos, length)); LLWString text_to_copy; // *HACK: Because LLSLURL is currently broken we cannot use it to check if unescaped_text is a valid SLURL (see EXT-8335). - if (LLStringUtil::startsWith(unescaped_text, "http://") || LLStringUtil::startsWith(unescaped_text, "secondlife://")) // SLURL + if (LLStringUtil::startsWith(unescaped_text, "https://") + || LLStringUtil::startsWith(unescaped_text, "http://") + || LLStringUtil::startsWith(unescaped_text, "secondlife://")) // SLURL text_to_copy = utf8str_to_wstring(LLWeb::escapeURL(unescaped_text)); else // human-readable location text_to_copy = utf8str_to_wstring(unescaped_text); diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index f11fa09ce9..890580ddff 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -69,7 +69,7 @@ const std::string MAIN_GRID_LOGIN_URI = "https://login.agni.lindenlab.com/cgi-bi const std::string SL_UPDATE_QUERY_URL = "https://update.secondlife.com/update"; -const std::string MAIN_GRID_SLURL_BASE = "http://maps.secondlife.com/secondlife/"; +const std::string MAIN_GRID_SLURL_BASE = "https://maps.secondlife.com/secondlife/"; const std::string SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app"; const std::string MAIN_GRID_WEB_PROFILE_URL = "https://my.secondlife.com/"; @@ -281,7 +281,7 @@ bool LLGridManager::addGrid(LLSD& grid_data) // Populate to the default values if (!grid_data.has(GRID_LOGIN_PAGE_VALUE)) { - grid_data[GRID_LOGIN_PAGE_VALUE] = std::string("http://") + grid + "/app/login/"; + grid_data[GRID_LOGIN_PAGE_VALUE] = std::string("https://") + grid + "/app/login/"; } if (!grid_data.has(GRID_HELPER_URI_VALUE)) { diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 89cc05802d..9c0f4baf28 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -2693,10 +2693,10 @@ void LLVOVolume::mediaNavigateBounceBack(U8 texture_index) if (mep && impl) { std::string url = mep->getCurrentURL(); - // Look for a ":", if not there, assume "http://" + // Look for a ":", if not there, assume "https://" if (!url.empty() && std::string::npos == url.find(':')) { - url = "http://" + url; + url = "https://" + url; } // If the url we're trying to "bounce back" to is either empty or not // allowed by the whitelist, try the home url. If *that* doesn't work, @@ -2704,10 +2704,10 @@ void LLVOVolume::mediaNavigateBounceBack(U8 texture_index) if (url.empty() || !mep->checkCandidateUrl(url)) { url = mep->getHomeURL(); - // Look for a ":", if not there, assume "http://" + // Look for a ":", if not there, assume "https://" if (!url.empty() && std::string::npos == url.find(':')) { - url = "http://" + url; + url = "https://" + url; } } if (url.empty() || !mep->checkCandidateUrl(url)) diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp index edaa44aa70..6d234a9a34 100644 --- a/indra/newview/llwindowlistener.cpp +++ b/indra/newview/llwindowlistener.cpp @@ -54,7 +54,7 @@ LLWindowListener::LLWindowListener(LLViewerWindow *window, const KeyboardGetter& "Given [\"keysym\"], [\"keycode\"] or [\"char\"], inject the specified "; std::string keyExplain = "(integer keycode values, or keysym string from any addKeyName() call in\n" - "http://bitbucket.org/lindenlab/viewer-release/src/tip/indra/llwindow/llkeyboard.cpp )\n"; + "https://github.com/secondlife/viewer/blob/develop/indra/llwindow/llkeyboard.cpp )\n"; std::string mask = "Specify optional [\"mask\"] as an array containing any of \"CTL\", \"ALT\",\n" "\"SHIFT\" or \"MAC_CONTROL\"; the corresponding modifier bits will be combined\n" @@ -69,7 +69,7 @@ LLWindowListener::LLWindowListener(LLViewerWindow *window, const KeyboardGetter& "(button values \"LEFT\", \"MIDDLE\", \"RIGHT\")\n"; std::string paramsExplain = "[\"path\"] is as for LLUI::getInstance()->resolvePath(), described in\n" - "http://bitbucket.org/lindenlab/viewer-release/src/tip/indra/llui/llui.h\n" + "https://github.com/secondlife/viewer/blob/develop/indra/llui/llui.h\n" "If you omit [\"path\"], you must specify both [\"x\"] and [\"y\"].\n" "If you specify [\"path\"] without both [\"x\"] and [\"y\"], will synthesize (x, y)\n" "in the center of the LLView selected by [\"path\"].\n" diff --git a/indra/newview/tests/llslurl_test.cpp b/indra/newview/tests/llslurl_test.cpp index fc9f5b707a..3ff38ea372 100644 --- a/indra/newview/tests/llslurl_test.cpp +++ b/indra/newview/tests/llslurl_test.cpp @@ -177,15 +177,15 @@ namespace tut LLSLURL slurl = LLSLURL(""); ensure_equals("null slurl", (int)slurl.getType(), LLSLURL::LAST_LOCATION); - slurl = LLSLURL("http://slurl.com/secondlife/myregion"); + slurl = LLSLURL("https://slurl.com/secondlife/myregion"); ensure_equals("slurl.com slurl, region only - type", slurl.getType(), LLSLURL::LOCATION); ensure_equals("slurl.com slurl, region only", slurl.getSLURLString(), - "http://maps.secondlife.com/secondlife/myregion/128/128/0"); + "https://maps.secondlife.com/secondlife/myregion/128/128/0"); - slurl = LLSLURL("http://maps.secondlife.com/secondlife/myregion/1/2/3"); + slurl = LLSLURL("https://maps.secondlife.com/secondlife/myregion/1/2/3"); ensure_equals("maps.secondlife.com slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); ensure_equals("maps.secondlife.com slurl, region + coords", slurl.getSLURLString(), - "http://maps.secondlife.com/secondlife/myregion/1/2/3"); + "https://maps.secondlife.com/secondlife/myregion/1/2/3"); slurl = LLSLURL("secondlife://"); ensure_equals("secondlife: slurl, empty - type", slurl.getType(), LLSLURL::EMPTY); @@ -196,27 +196,27 @@ namespace tut slurl = LLSLURL("secondlife://myregion"); ensure_equals("secondlife: slurl, region only - type", slurl.getType(), LLSLURL::LOCATION); ensure_equals("secondlife: slurl, region only", slurl.getSLURLString(), - "http://maps.secondlife.com/secondlife/myregion/128/128/0"); + "https://maps.secondlife.com/secondlife/myregion/128/128/0"); slurl = LLSLURL("secondlife://myregion/1/2/3"); ensure_equals("secondlife: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); ensure_equals("secondlife slurl, region + coords", slurl.getSLURLString(), - "http://maps.secondlife.com/secondlife/myregion/1/2/3"); + "https://maps.secondlife.com/secondlife/myregion/1/2/3"); slurl = LLSLURL("/myregion"); ensure_equals("/region slurl, region- type", slurl.getType(), LLSLURL::LOCATION); ensure_equals("/region slurl, region ", slurl.getSLURLString(), - "http://maps.secondlife.com/secondlife/myregion/128/128/0"); + "https://maps.secondlife.com/secondlife/myregion/128/128/0"); slurl = LLSLURL("/myregion/1/2/3"); ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); ensure_equals("/ slurl, region + coords", slurl.getSLURLString(), - "http://maps.secondlife.com/secondlife/myregion/1/2/3"); + "https://maps.secondlife.com/secondlife/myregion/1/2/3"); slurl = LLSLURL("my region/1/2/3"); ensure_equals(" slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); ensure_equals(" slurl, region + coords", slurl.getSLURLString(), - "http://maps.secondlife.com/secondlife/my%20region/1/2/3"); + "https://maps.secondlife.com/secondlife/my%20region/1/2/3"); LLGridManager::getInstance()->setGridChoice("my.grid.com"); slurl = LLSLURL("https://my.grid.com/region/my%20region/1/2/3"); @@ -309,7 +309,7 @@ namespace tut slurl = LLSLURL("my region", LLVector3(1,2,3)); ensure_equals("default grid/region/vector - type", slurl.getType(), LLSLURL::LOCATION); ensure_equals(" default grid/region/vector", slurl.getSLURLString(), - "http://maps.secondlife.com/secondlife/my%20region/1/2/3"); + "https://maps.secondlife.com/secondlife/my%20region/1/2/3"); LLGridManager::getInstance()->setGridChoice("MyGrid"); slurl = LLSLURL("my region", LLVector3(1,2,3)); diff --git a/indra/newview/tests/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp index d9cb9e7538..94cf0fcf10 100644 --- a/indra/newview/tests/llviewernetwork_test.cpp +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -391,7 +391,7 @@ namespace tut std::string("https://minimal.long.name/helpers/")); ensure_equals("minimal grid login page", LLGridManager::getInstance()->getLoginPage("minimal.long.name"), - std::string("http://minimal.long.name/app/login/")); + std::string("https://minimal.long.name/app/login/")); } -- cgit v1.3 From 5e7e4dfa3900775a5482d296a8fa648c5805d018 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 2 Dec 2025 15:51:00 +0200 Subject: #5084 Better labeling of uncaught events, freezes, crashes At least while watchdog is enabled it's more fair to label these as unknowns --- indra/newview/llappviewer.cpp | 5 +++-- indra/newview/llappviewer.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f6f5f1717b..569fd30b21 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3950,7 +3950,7 @@ void LLAppViewer::processMarkerFiles() #if LL_WINDOWS && LL_BUGSPLAT // bugsplat will set correct state in bugsplatSendLog // Might be more accurate to rename this one into 'unknown' - gLastExecEvent = LAST_EXEC_FROZE; + gLastExecEvent = LAST_EXEC_UNKNOWN; #else gLastExecEvent = LAST_EXEC_OTHER_CRASH; #endif // LL_WINDOWS @@ -3996,7 +3996,8 @@ void LLAppViewer::processMarkerFiles() { if (markerIsSameVersion(logout_marker_file)) { - gLastExecEvent = LAST_EXEC_LOGOUT_FROZE; + // Either froze, got killed or somehow crash was not caught + gLastExecEvent = LAST_EXEC_LOGOUT_UNKNOWN; LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "', changing LastExecEvent to LOGOUT_FROZE" << LL_ENDL; } else diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 61c206bcec..6b0d3e0b27 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -77,6 +77,8 @@ typedef enum LAST_EXEC_BAD_ALLOC, LAST_EXEC_MISSING_FILES, LAST_EXEC_GRAPHICS_INIT, + LAST_EXEC_UNKNOWN, + LAST_EXEC_LOGOUT_UNKNOWN, LAST_EXEC_COUNT } eLastExecEvent; -- cgit v1.3 From 0b67b0509b9fdf5ce6c401cdb4d7b0edf101ab98 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 3 Dec 2025 00:34:40 +0200 Subject: Switch version to 2026.01.0 after promotion of 2025.08 --- indra/newview/VIEWER_VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 429dc57af3..4703009f54 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -7.2.3 +2026.01.0 -- cgit v1.3 From d249e88f34810de4e1baa9537aadc05710007976 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Wed, 3 Dec 2025 19:58:22 +0200 Subject: Build fix --- indra/llui/lltextbase.cpp | 1 - indra/llui/lltextbase.h | 1 - 2 files changed, 2 deletions(-) (limited to 'indra') diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index c9f439d085..2079ff17fc 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -3477,7 +3477,6 @@ S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offs void LLTextSegment::updateLayout(const LLTextBase& editor) {} F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) { return draw_rect.mLeft; } bool LLTextSegment::canEdit() const { return false; } -bool LLTextSegment::getPermitsEmoji() const { return true; } void LLTextSegment::unlinkFromDocument(LLTextBase*) {} void LLTextSegment::linkToDocument(LLTextBase*) {} const LLUIColor& LLTextSegment::getColor() const { static const LLUIColor white = LLUIColorTable::instance().getColor("White", LLColor4::white); return white; } diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 5549c6d7cd..3ab5e905e3 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -89,7 +89,6 @@ public: virtual void updateLayout(const class LLTextBase& editor); virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); virtual bool canEdit() const; - virtual bool getPermitsEmoji() const; virtual void unlinkFromDocument(class LLTextBase* editor); virtual void linkToDocument(class LLTextBase* editor); -- cgit v1.3 From b83c5fab496daf7ee8ca282d55b4b59008a70b1e Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 9 Dec 2025 22:26:09 +0200 Subject: #5123 A bunch of small performance optimizations mostly for turning control settings into cached variants --- indra/newview/lldrawable.cpp | 8 +++-- indra/newview/llhudnametag.cpp | 2 +- indra/newview/llvieweraudio.cpp | 59 +++++++++++++++++++------------------ indra/newview/llviewermedia.cpp | 14 +++++---- indra/newview/llviewerobject.cpp | 5 ++-- indra/newview/llviewerparcelmgr.cpp | 3 +- indra/newview/llviewerwindow.cpp | 18 ++++++----- indra/newview/llvovolume.cpp | 22 +++++++++----- indra/newview/pipeline.cpp | 2 +- 9 files changed, 77 insertions(+), 56 deletions(-) (limited to 'indra') diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index 322ee90541..da9378ad12 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -723,9 +723,13 @@ F32 LLDrawable::updateXform(bool undamped) mXform.setRotation(target_rot); mXform.setScale(LLVector3(1,1,1)); //no scale in drawable transforms (IT'S A RULE!) mXform.updateMatrix(); - if (isRoot() && mVObjp->isAnimatedObject() && mVObjp->getControlAvatar()) + if (isRoot() && mVObjp->isAnimatedObject()) { - mVObjp->getControlAvatar()->matchVolumeTransform(); + LLControlAvatar* cav = mVObjp->getControlAvatar(); + if (cav) + { + cav->matchVolumeTransform(); + } } if (mSpatialBridge) diff --git a/indra/newview/llhudnametag.cpp b/indra/newview/llhudnametag.cpp index 11f049564a..4327d281e5 100644 --- a/indra/newview/llhudnametag.cpp +++ b/indra/newview/llhudnametag.cpp @@ -301,7 +301,7 @@ void LLHUDNameTag::renderText() const S32 label_height = ll_round((mFontp->getLineHeight() * (F32)mLabelSegments.size() + (VERTICAL_PADDING / 3.f))); label_top_rect.mBottom = label_top_rect.mTop - label_height; LLColor4 label_top_color = text_color; - label_top_color.mV[VALPHA] = gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor; + label_top_color.mV[VALPHA] = bubble_opacity() * alpha_factor; mRoundedRectTopImgp->draw3D(render_position, x_pixel_vec, y_pixel_vec, label_top_rect, label_top_color); } diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index aa0cbac91f..a7441febd9 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -398,18 +398,12 @@ void init_audio() void audio_update_volume(bool force_update) { - F32 master_volume = gSavedSettings.getF32("AudioLevelMaster"); - bool mute_audio = gSavedSettings.getBOOL("MuteAudio"); - - LLProgressView* progress = gViewerWindow->getProgressView(); - bool progress_view_visible = false; - - if (progress) - { - progress_view_visible = progress->getVisible(); - } + static LLCachedControl master_volume(gSavedSettings, "AudioLevelMaster"); + static LLCachedControl mute_audio_setting(gSavedSettings, "MuteAudio"); + static LLCachedControl mute_when_minimized(gSavedSettings, "MuteWhenMinimized"); + bool mute_audio = mute_audio_setting(); - if (!gViewerWindow->getActive() && gSavedSettings.getBOOL("MuteWhenMinimized")) + if (!gViewerWindow->getActive() && mute_when_minimized()) { mute_audio = true; } @@ -419,7 +413,7 @@ void audio_update_volume(bool force_update) { // Sound Effects - gAudiop->setMasterGain ( master_volume ); + gAudiop->setMasterGain (master_volume()); const F32 AUDIO_LEVEL_DOPPLER = 1.f; gAudiop->setDopplerFactor(AUDIO_LEVEL_DOPPLER); @@ -435,6 +429,7 @@ void audio_update_volume(bool force_update) gAudiop->setRolloffFactor(AUDIO_LEVEL_UNDERWATER_ROLLOFF); } + bool progress_view_visible = gViewerWindow->getShowProgress(); gAudiop->setMuted(mute_audio || progress_view_visible); //Play any deferred sounds when unmuted @@ -448,13 +443,21 @@ void audio_update_volume(bool force_update) audio_update_wind(true); } + static LLCachedControl mute_sounds(gSavedSettings, "MuteSounds"); + static LLCachedControl mute_ui(gSavedSettings, "MuteUI"); + static LLCachedControl mute_ambient(gSavedSettings, "MuteAmbient"); + static LLCachedControl mute_music(gSavedSettings, "MuteMusic"); + static LLCachedControl al_sfx(gSavedSettings, "AudioLevelSFX"); + static LLCachedControl al_ui(gSavedSettings, "AudioLevelUI"); + static LLCachedControl al_ambient(gSavedSettings, "AudioLevelAmbient"); + static LLCachedControl al_music(gSavedSettings, "AudioLevelMusic"); // handle secondary gains gAudiop->setSecondaryGain(LLAudioEngine::AUDIO_TYPE_SFX, - gSavedSettings.getBOOL("MuteSounds") ? 0.f : gSavedSettings.getF32("AudioLevelSFX")); + mute_sounds() ? 0.f : al_sfx()); gAudiop->setSecondaryGain(LLAudioEngine::AUDIO_TYPE_UI, - gSavedSettings.getBOOL("MuteUI") ? 0.f : gSavedSettings.getF32("AudioLevelUI")); + mute_ui() ? 0.f : al_ui()); gAudiop->setSecondaryGain(LLAudioEngine::AUDIO_TYPE_AMBIENT, - gSavedSettings.getBOOL("MuteAmbient") ? 0.f : gSavedSettings.getF32("AudioLevelAmbient")); + mute_ambient() ? 0.f : al_ambient()); // Streaming Music @@ -464,31 +467,29 @@ void audio_update_volume(bool force_update) LLViewerAudio::getInstance()->setForcedTeleportFade(false); } - F32 music_volume = gSavedSettings.getF32("AudioLevelMusic"); - bool music_muted = gSavedSettings.getBOOL("MuteMusic"); F32 fade_volume = LLViewerAudio::getInstance()->getFadeVolume(); - music_volume = mute_volume * master_volume * music_volume * fade_volume; - gAudiop->setInternetStreamGain (music_muted ? 0.f : music_volume); + F32 music_volume = mute_volume * master_volume * al_music() * fade_volume; + gAudiop->setInternetStreamGain (mute_music() ? 0.f : music_volume); } // Streaming Media - F32 media_volume = gSavedSettings.getF32("AudioLevelMedia"); - bool media_muted = gSavedSettings.getBOOL("MuteMedia"); - media_volume = mute_volume * master_volume * media_volume; - LLViewerMedia::getInstance()->setVolume( media_muted ? 0.0f : media_volume ); + static LLCachedControl media_muted(gSavedSettings, "MuteMedia"); + static LLCachedControl media_volume(gSavedSettings, "AudioLevelMedia"); + LLViewerMedia::getInstance()->setVolume( media_muted() ? 0.0f : (mute_volume * master_volume() * media_volume())); // Voice, this is parametric singleton, it gets initialized when ready if (LLVoiceClient::instanceExists()) { - F32 voice_volume = gSavedSettings.getF32("AudioLevelVoice"); - voice_volume = mute_volume * master_volume * voice_volume; - bool voice_mute = gSavedSettings.getBOOL("MuteVoice"); + static LLCachedControl voice_mute(gSavedSettings, "MuteVoice"); + static LLCachedControl voice_volume_setting(gSavedSettings, "AudioLevelVoice"); + static LLCachedControl voice_mic_setting(gSavedSettings, "AudioLevelMic"); + F32 voice_volume = mute_volume * master_volume() * voice_volume_setting(); LLVoiceClient *voice_inst = LLVoiceClient::getInstance(); - voice_inst->setVoiceVolume(voice_mute ? 0.f : voice_volume); - voice_inst->setMicGain(voice_mute ? 0.f : gSavedSettings.getF32("AudioLevelMic")); + voice_inst->setVoiceVolume(voice_mute() ? 0.f : voice_volume); + voice_inst->setMicGain(voice_mute() ? 0.f : voice_mic_setting()); - if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized"))) + if (!gViewerWindow->getActive() && mute_when_minimized()) { voice_inst->setMuteMic(true); } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index bb956d455f..a77b9f6103 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -663,9 +663,10 @@ void LLViewerMedia::updateMedia(void *dummy_arg) LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; //LL_RECORD_BLOCK_TIME(FTM_MEDIA_UPDATE); llassert(!gCubeSnapshot); + static LLCachedControl use_read_thread(gSavedSettings, "PluginUseReadThread", true); // Enable/disable the plugin read thread - LLPluginProcessParent::setUseReadThread(gSavedSettings.getBOOL("PluginUseReadThread")); + LLPluginProcessParent::setUseReadThread(use_read_thread()); // SL-16418 We can't call LLViewerMediaImpl->update() if we are in the state of shutting down. if(LLApp::isExiting()) @@ -2189,16 +2190,19 @@ void LLViewerMediaImpl::updateVolume() if (mProximityCamera > 0) { - if (mProximityCamera > gSavedSettings.getF32("MediaRollOffMax")) + static LLCachedControl media_rolloff_min(gSavedSettings, "MediaRollOffMin"); + static LLCachedControl media_rolloff_max(gSavedSettings, "MediaRollOffMax"); + static LLCachedControl media_rolloff_rate(gSavedSettings, "MediaRollOffRate"); + if (mProximityCamera > media_rolloff_max()) { volume = 0; } - else if (mProximityCamera > gSavedSettings.getF32("MediaRollOffMin")) + else if (mProximityCamera > media_rolloff_min()) { // attenuated_volume = 1 / (roll_off_rate * (d - min))^2 // the +1 is there so that for distance 0 the volume stays the same - F64 adjusted_distance = mProximityCamera - gSavedSettings.getF32("MediaRollOffMin"); - F64 attenuation = 1.0 + (gSavedSettings.getF32("MediaRollOffRate") * adjusted_distance); + F64 adjusted_distance = mProximityCamera - media_rolloff_min(); + F64 attenuation = 1.0 + (media_rolloff_rate() * adjusted_distance); attenuation = 1.0 / (attenuation * attenuation); // the attenuation multiplier should never be more than one since that would increase volume volume = volume * (F32)llmin(1.0, attenuation); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 1675c44c5c..06089e48b8 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -7469,9 +7469,10 @@ const std::string& LLViewerObject::getAttachmentItemName() const LLVOAvatar* LLViewerObject::getAvatar() const { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; - if (getControlAvatar()) + LLControlAvatar* ca = getControlAvatar(); + if (ca) { - return getControlAvatar(); + return ca; } if (isAttachment()) { diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 432da2e990..6a029645fb 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -881,7 +881,8 @@ LLParcel* LLViewerParcelMgr::getCollisionParcel() const void LLViewerParcelMgr::render() { - if (mSelected && mRenderSelection && gSavedSettings.getBOOL("RenderParcelSelection") && !gDisconnected) + static LLCachedControl render_parcel_selection(gSavedSettings, "RenderParcelSelection"); + if (mSelected && mRenderSelection && render_parcel_selection() && !gDisconnected) { // Rendering is done in agent-coordinates, so need to supply // an appropriate offset to the render code. diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 187cfc9792..09d14858e5 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -3403,13 +3403,11 @@ void append_xui_tooltip(LLView* viewp, LLToolTip::Params& params) } } -static LLTrace::BlockTimerStatHandle ftm("Update UI"); - // Update UI based on stored mouse position from mouse-move // event processing. void LLViewerWindow::updateUI() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; //LL_RECORD_BLOCK_TIME(ftm); + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; static std::string last_handle_msg; @@ -3427,12 +3425,15 @@ void LLViewerWindow::updateUI() } } - LLConsole::updateClass(); + { + LL_PROFILE_ZONE_NAMED("UI updateClass"); + LLConsole::updateClass(); - // execute postponed arrange calls - LLAccordionCtrl::updateClass(); - // animate layout stacks so we have up to date rect for world view - LLLayoutStack::updateClass(); + // execute postponed arrange calls + LLAccordionCtrl::updateClass(); + // animate layout stacks so we have up to date rect for world view + LLLayoutStack::updateClass(); + } // use full window for world view when not rendering UI bool world_view_uses_full_window = gAgentCamera.cameraMouselook() || !gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI); @@ -3861,6 +3862,7 @@ void LLViewerWindow::updateUI() void LLViewerWindow::updateLayout() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; LLTool* tool = LLToolMgr::getInstance()->getCurrentTool(); if (gFloaterTools != NULL && tool != NULL diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 9c0f4baf28..06259f5446 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -3842,11 +3842,12 @@ void LLVOVolume::onReparent(LLViewerObject *old_parent, LLViewerObject *new_pare } if (old_volp && old_volp->isAnimatedObject()) { - if (old_volp->getControlAvatar()) + LLControlAvatar* cav = old_volp->getControlAvatar(); + if (cav) { // We have been removed from an animated object, need to do cleanup. - old_volp->getControlAvatar()->updateAttachmentOverrides(); - old_volp->getControlAvatar()->updateAnimations(); + cav->updateAttachmentOverrides(); + cav->updateAnimations(); } } } @@ -5131,7 +5132,7 @@ U32 LLVOVolume::getPartitionType() const { return LLViewerRegion::PARTITION_HUD; } - if (isAnimatedObject() && getControlAvatar()) + if (isAnimatedObjectFast() && getControlAvatar()) { return LLViewerRegion::PARTITION_CONTROL_AV; } @@ -5757,11 +5758,18 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } // Standard rigged mesh attachments: - bool rigged = !vobj->isAnimatedObject() && skinInfo && vobj->isAttachment(); + bool is_animated = vobj->isAnimatedObject(); + bool rigged = !is_animated && skinInfo && vobj->isAttachment(); // Animated objects. Have to check for isRiggedMesh() to // exclude static objects in animated object linksets. - rigged = rigged || (vobj->isAnimatedObject() && vobj->isRiggedMesh() && - vobj->getControlAvatar() && vobj->getControlAvatar()->mPlaying); + if (!rigged && is_animated && vobj->isRiggedMesh()) + { + LLControlAvatar* cav = vobj->getControlAvatar(); + if (cav) + { + rigged = cav->mPlaying; + } + } bool any_rigged_face = false; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index d2aebfbce5..d1ea8c2ee5 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -5525,7 +5525,7 @@ static F32 calc_light_dist(LLVOVolume* light, const LLVector3& cam_pos, F32 max_ if (light->mDrawable.notNull() && light->mDrawable->isState(LLDrawable::ACTIVE)) { // moving lights get a little higher priority (too much causes artifacts) - dist = llmax(dist - light->getLightRadius()*0.25f, 0.f); + dist = llmax(dist - radius * 0.25f, 0.f); } return dist; } -- cgit v1.3 From c85eadfd436fc44a45dd46f31adf823323c4dc3d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 9 Dec 2025 23:46:15 +0200 Subject: #5125 Negative pixel area in object's info --- indra/newview/llfloaterobjectweights.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llfloaterobjectweights.cpp b/indra/newview/llfloaterobjectweights.cpp index fa491a4b27..bd8530abd7 100644 --- a/indra/newview/llfloaterobjectweights.cpp +++ b/indra/newview/llfloaterobjectweights.cpp @@ -208,7 +208,7 @@ void LLFloaterObjectWeights::draw() toggleRenderLoadingIndicators(false); } mTrianglesShown->setText(llformat("%d", total_tris)); - mPixelArea->setText(llformat("%d", pixel_area)); + mPixelArea->setText(llformat("%ld", (S64)pixel_area)); // value capped at 10M } LLFloater::draw(); } -- cgit v1.3 From 00c90b55ce894e679f6d3e87037d1e59dccc897f Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 10 Dec 2025 13:59:22 +0100 Subject: Add missing static declaration for LLCachedControl --- indra/newview/llviewerwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 09d14858e5..c5054a7269 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -3330,7 +3330,7 @@ void LLViewerWindow::clearPopups() void LLViewerWindow::moveCursorToCenter() { bool mouse_warp = false; - LLCachedControl mouse_warp_mode(gSavedSettings, "MouseWarpMode", 1); + static LLCachedControl mouse_warp_mode(gSavedSettings, "MouseWarpMode", 1); switch (mouse_warp_mode()) { -- cgit v1.3 From 79f21027371f32a9268ce8d944e3d4727ddd8b55 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Sat, 15 Feb 2025 09:51:22 +0200 Subject: #2639 Reapplied GPU benchmark fix First test returns quarter to a half the throughput, do two tests. May be caused by driver, may be some 'energy saving', but not important enough to spend time investingating. It was working the same way prior to ExtraFPS, but viewer was running an extra CPU test that 'preheated' the system. Also increasing minimum throughput as numerous new features, like mirrors and pbr were added and requirements are now higher. --- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llglsandbox.cpp | 66 +++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 28 deletions(-) (limited to 'indra') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index fe31a00ba3..d7df8aeb44 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7335,7 +7335,7 @@ Type F32 Value - 32.0 + 48.0 RenderCPUBasis diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp index 112008172e..5484ce6276 100644 --- a/indra/newview/llglsandbox.cpp +++ b/indra/newview/llglsandbox.cpp @@ -903,6 +903,39 @@ private: }; +F32 shader_timer_benchmark(std::vector & dest, TextureHolder & texHolder, U32 textures_count, LLVertexBuffer * buff, F32 &seconds) +{ + // run GPU timer benchmark + + //number of samples to take + const S32 samples = 64; + + { + ShaderProfileHelper initProfile; + dest[0].bindTarget(); + gBenchmarkProgram.bind(); + for (S32 c = 0; c < samples; ++c) + { + for (U32 i = 0; i < textures_count; ++i) + { + texHolder.bind(i); + buff->setBuffer(); + buff->drawArrays(LLRender::TRIANGLES, 0, 3); + } + } + gBenchmarkProgram.unbind(); + dest[0].flush(); + } + + F32 ms = gBenchmarkProgram.mTimeElapsed / 1000000.f; + seconds = ms / 1000.f; + + F64 samples_drawn = (F64)gBenchmarkProgram.mSamplesDrawn; + F64 gpixels_drawn = samples_drawn / 1000000000.0; + F32 samples_sec = (F32)(gpixels_drawn / seconds); + return samples_sec * 4; // 4 bytes per sample +} + //----------------------------------------------------------------------------- // gpu_benchmark() // returns measured memory bandwidth of GPU in gigabytes per second @@ -944,9 +977,6 @@ F32 gpu_benchmark() //number of textures const U32 count = 32; - //number of samples to take - const S32 samples = 64; - //time limit, allocation operations shouldn't take longer then 30 seconds, same for actual benchmark. const F32 time_limit = 30; @@ -1036,33 +1066,15 @@ F32 gpu_benchmark() LLGLSLShader::unbind(); - // run GPU timer benchmark - { - ShaderProfileHelper initProfile; - dest[0].bindTarget(); - gBenchmarkProgram.bind(); - for (S32 c = 0; c < samples; ++c) - { - for (U32 i = 0; i < count; ++i) - { - texHolder.bind(i); - buff->setBuffer(); - buff->drawArrays(LLRender::TRIANGLES, 0, 3); - } - } - gBenchmarkProgram.unbind(); - dest[0].flush(); - } + // run GPU timer benchmark twice + F32 seconds = 0; + F32 gbps = shader_timer_benchmark(dest, texHolder, count, buff.get(), seconds); - F32 ms = gBenchmarkProgram.mTimeElapsed/1000000.f; - F32 seconds = ms/1000.f; + LL_INFOS("Benchmark") << "Memory bandwidth, 1st run is " << llformat("%.3f", gbps) << " GB/sec according to ARB_timer_query, total time " << seconds << " seconds" << LL_ENDL; - F64 samples_drawn = (F64)gBenchmarkProgram.mSamplesDrawn; - F64 gpixels_drawn = samples_drawn / 1000000000.0; - F32 samples_sec = (F32)(gpixels_drawn/seconds); - F32 gbps = samples_sec*4; // 4 bytes per sample + gbps = shader_timer_benchmark(dest, texHolder, count, buff.get(), seconds); - LL_INFOS("Benchmark") << "Memory bandwidth is " << llformat("%.3f", gbps) << " GB/sec according to ARB_timer_query, total time " << seconds << " seconds" << LL_ENDL; + LL_INFOS("Benchmark") << "Memory bandwidth, final run is " << llformat("%.3f", gbps) << " GB/sec according to ARB_timer_query, total time " << seconds << " seconds" << LL_ENDL; return gbps; } -- cgit v1.3 From 3ca3ea75c333078013914e174564340f894573e2 Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Sat, 25 Oct 2025 17:08:35 +0200 Subject: Add a new static function LLFile::size() to determine the size of a file_name. Replace LLAPRFile::remove(), LLAPRFile::size() and LLAPRFile::isExist() with according functions from LLFile and retire these LLAPRFile methods and the never used LLAPRFile::rename(), LLAPRFile::makeDir() and LLAPRFile::removeDir() functions. Also clean up remarks about the threading safety of the APRCachePool, which is not used in these locations anymore. --- indra/llcommon/llapr.cpp | 118 ---------------------------------- indra/llcommon/llapr.h | 9 --- indra/llcommon/llfile.cpp | 39 +++++++++++ indra/llcommon/llfile.h | 7 ++ indra/newview/llappviewer.cpp | 18 +++--- indra/newview/lltexturecache.cpp | 38 +++++------ indra/newview/llviewerassetupload.cpp | 2 +- indra/newview/llvocache.cpp | 4 +- 8 files changed, 74 insertions(+), 161 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index eeff2694a7..cffd6b7bc0 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -628,124 +628,6 @@ S32 LLAPRFile::writeEx(const std::string& filename, const void *buf, S32 offset, return (S32)bytes_written; } -//static -bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool) -{ - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_file_remove(filename.c_str(), scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to remove filename: " << filename << LL_ENDL; - return false; - } - return true; -} - -//static -bool LLAPRFile::rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool) -{ - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_file_rename(filename.c_str(), newname.c_str(), scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to rename filename: " << filename << LL_ENDL; - return false; - } - return true; -} - -//static -bool LLAPRFile::isExist(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags) -{ - apr_file_t* apr_file; - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS || !apr_file) - { - return false; - } - else - { - apr_file_close(apr_file) ; - return true; - } -} - -//static -S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool) -{ - apr_file_t* apr_file; - apr_finfo_t info; - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS || !apr_file) - { - return 0; - } - else - { - apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file); - - apr_file_close(apr_file) ; - - if (s == APR_SUCCESS) - { - return (S32)info.size; - } - else - { - return 0; - } - } -} - -//static -bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool) -{ - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL; - return false; - } - return true; -} - -//static -bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool) -{ - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_file_remove(dirname.c_str(), scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to remove directory: " << dirname << LL_ENDL; - return false; - } - return true; -} // //end of static components of LLAPRFile //******************************************************************************************************************************* diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index 11e474b5dd..5477cfcacd 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -185,19 +185,10 @@ private: static apr_status_t close(apr_file_t* file) ; static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset); public: - // returns false if failure: - static bool remove(const std::string& filename, LLVolatileAPRPool* pool = NULL); - static bool rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool = NULL); - static bool isExist(const std::string& filename, LLVolatileAPRPool* pool = NULL, apr_int32_t flags = APR_READ); - static S32 size(const std::string& filename, LLVolatileAPRPool* pool = NULL); - static bool makeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL); - static bool removeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL); - // Returns bytes read/written, 0 if read/write fails: static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); static S32 writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); // offset<0 means append //******************************************************************************************************************************* }; - #endif // LL_LLAPR_H diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index a539e4fe28..4ef39674c2 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -497,6 +497,45 @@ int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_e return warnif("stat", filename, rc, suppress_error); } +// static +S64 LLFile::size(const std::string& filename, int suppress_error) +{ +#if LL_WINDOWS + std::wstring utf16filename = utf8path_to_wstring(filename); + HANDLE file_handle = CreateFileW(utf16filename.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE != file_handle) + { + LARGE_INTEGER file_size; + if (GetFileSizeEx(file_handle, &file_size)) + { + CloseHandle(file_handle); + return file_size.QuadPart; + } + } + // Get last error before calling the CloseHandle() function + int rc = set_errno_from_oserror(GetLastError()); + if (INVALID_HANDLE_VALUE != file_handle) + { + CloseHandle(file_handle); + } +#else + llstat filestatus; + int rc = ::stat(filename.c_str(), &filestatus); + if (rc == 0) + { + if (S_ISREG(filestatus.st_mode) + { + return filestatus.st_size; + } + // We got something else than a file + rc = -1; + errno = EACCES; + } +#endif + warnif("size", filename, rc, suppress_error); + return 0; +} + // static unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error) { diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 04a2946ac4..61ec8a5e4e 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -148,6 +148,13 @@ public: /// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise /// together with the three access bits which under Windows only the write bit is relevant. + /// get the size of a file in bytes + static S64 size(const std::string& filename, int suppress_error = ENOENT); + ///> returns the size of a file in bytes + /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with + /// warnings when the file does not exist + /// @returns the file size on success and 0 on failure. + /// check if filename is an existing directory static bool isdir(const std::string& filename); ///< @returns true if the path is for an existing directory diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 569fd30b21..f6e3139cc4 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2268,7 +2268,7 @@ void errorCallback(LLError::ELevel level, const std::string &error_string) LLAppViewer::instance()->writeDebugInfo(); std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); - if (!LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB)) + if (!LLFile::isfile(error_marker_file)) { // If marker doesn't exist, create a marker with llerror code for next launch // otherwise don't override existing file @@ -3900,7 +3900,7 @@ void LLAppViewer::processMarkerFiles() bool marker_is_same_version = true; // first, look for the marker created at startup and deleted on a clean exit mMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,MARKER_FILE_NAME); - if (LLAPRFile::isExist(mMarkerFileName, NULL, LL_APR_RB)) + if (LLFile::isfile(mMarkerFileName)) { // File exists... // first, read it to see if it was created by the same version (we need this later) @@ -3992,7 +3992,7 @@ void LLAppViewer::processMarkerFiles() // check for any last exec event report based on whether or not it happened during logout // (the logout marker is created when logout begins) std::string logout_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME); - if(LLAPRFile::isExist(logout_marker_file, NULL, LL_APR_RB)) + if(LLFile::isfile(logout_marker_file)) { if (markerIsSameVersion(logout_marker_file)) { @@ -4004,11 +4004,11 @@ void LLAppViewer::processMarkerFiles() { LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "' found, but versions did not match" << LL_ENDL; } - LLAPRFile::remove(logout_marker_file); + LLFile::remove(logout_marker_file); } // and last refine based on whether or not a marker created during a non-llerr crash is found std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); - if(LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB)) + if(LLFile::isfile(error_marker_file)) { S32 marker_code = getMarkerErrorCode(error_marker_file); if (marker_code >= 0) @@ -4033,7 +4033,7 @@ void LLAppViewer::processMarkerFiles() { LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' marker found, but versions did not match" << LL_ENDL; } - LLAPRFile::remove(error_marker_file); + LLFile::remove(error_marker_file); } #if LL_DARWIN @@ -4060,7 +4060,7 @@ void LLAppViewer::removeMarkerFiles() if (mMarkerFile.getFileHandle()) { mMarkerFile.close() ; - LLAPRFile::remove( mMarkerFileName ); + LLFile::remove( mMarkerFileName ); LL_DEBUGS("MarkerFile") << "removed exec marker '"<getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); - return LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB); + return LLFile::isfile(error_marker_file); } void LLAppViewer::outOfMemorySoftQuit() diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 1a7ce74ccc..e59cf70177 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -180,7 +180,7 @@ private: bool LLTextureCacheLocalFileWorker::doRead() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - S32 local_size = LLAPRFile::size(mFileName, mCache->getLocalAPRFilePool()); + S32 local_size = (S32)LLFile::size(mFileName); if (local_size > 0 && mFileName.size() > 4) { @@ -296,7 +296,7 @@ bool LLTextureCacheRemoteWorker::doRead() // Is it a JPEG2000 file? { local_filename = filename + ".j2c"; - local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool()); + local_size = (S32)LLFile::size(local_filename); if (local_size > 0) { mImageFormat = IMG_CODEC_J2C; @@ -306,7 +306,7 @@ bool LLTextureCacheRemoteWorker::doRead() if (local_size == 0) { local_filename = filename + ".jpg"; - local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool()); + local_size = (S32)LLFile::size(local_filename); if (local_size > 0) { mImageFormat = IMG_CODEC_JPEG; @@ -317,7 +317,7 @@ bool LLTextureCacheRemoteWorker::doRead() if (local_size == 0) { local_filename = filename + ".tga"; - local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool()); + local_size = (S32)LLFile::size(local_filename); if (local_size > 0) { mImageFormat = IMG_CODEC_TGA; @@ -446,7 +446,7 @@ bool LLTextureCacheRemoteWorker::doRead() if (!done && (mState == BODY)) { std::string filename = mCache->getTextureFileName(mID); - S32 filesize = LLAPRFile::size(filename, mCache->getLocalAPRFilePool()); + S32 filesize = (S32)LLFile::size(filename); if (filesize && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset) { @@ -891,7 +891,7 @@ bool LLTextureCache::isInLocal(const LLUUID& id) // Is it a JPEG2000 file? { local_filename = filename + ".j2c"; - local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool()); + local_size = (S32)LLFile::size(local_filename); if (local_size > 0) { return true ; @@ -901,7 +901,7 @@ bool LLTextureCache::isInLocal(const LLUUID& id) // If not, is it a jpeg file? { local_filename = filename + ".jpg"; - local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool()); + local_size = (S32)LLFile::size(local_filename); if (local_size > 0) { return true ; @@ -911,7 +911,7 @@ bool LLTextureCache::isInLocal(const LLUUID& id) // Hmm... What about a targa file? (used for UI texture mostly) { local_filename = filename + ".tga"; - local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool()); + local_size = (S32)LLFile::size(local_filename); if (local_size > 0) { return true ; @@ -966,11 +966,10 @@ void LLTextureCache::purgeCache(ELLPath location, bool remove_dir) if(LLFile::isdir(mTexturesDirName)) { std::string file_name = gDirUtilp->getExpandedFilename(location, entries_filename); - // mHeaderAPRFilePoolp because we are under header mutex, and can be in main thread - LLAPRFile::remove(file_name, mHeaderAPRFilePoolp); + LLFile::remove(file_name); file_name = gDirUtilp->getExpandedFilename(location, cache_filename); - LLAPRFile::remove(file_name, mHeaderAPRFilePoolp); + LLFile::remove(file_name); purgeAllTextures(true); } @@ -1071,7 +1070,7 @@ void LLTextureCache::readEntriesHeader() { // mHeaderEntriesInfo initializes to default values so safe not to read it llassert_always(mHeaderAPRFile == NULL); - if (LLAPRFile::isExist(mHeaderEntriesFileName, mHeaderAPRFilePoolp)) + if (LLFile::isfile(mHeaderEntriesFileName)) { LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), mHeaderAPRFilePoolp); @@ -1795,8 +1794,7 @@ void LLTextureCache::purgeTextures(bool validate) { 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); + S32 bodysize = (S32)LLFile::size(filename); if (bodysize != entries[idx].mBodySize) { LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mBodySize << filename << LL_ENDL; @@ -2147,7 +2145,7 @@ void LLTextureCache::openFastCache(bool first_time) mFastCachePadBuffer = (U8*)ll_aligned_malloc_16(TEXTURE_FAST_CACHE_ENTRY_SIZE); } mFastCachePoolp = new LLVolatileAPRPool(); // is_local= true by default, so not thread safe by default - if (LLAPRFile::isExist(mFastCacheFileName, mFastCachePoolp)) + if (LLFile::isfile(mFastCacheFileName)) { mFastCachep = new LLAPRFile(mFastCacheFileName, APR_READ|APR_WRITE|APR_BINARY, mFastCachePoolp) ; } @@ -2230,9 +2228,7 @@ void LLTextureCache::removeCachedTexture(const LLUUID& id) mTexturesSizeMap.erase(id); } mHeaderIDMap.erase(id); - // We are inside header's mutex so mHeaderAPRFilePoolp is safe to use, - // but getLocalAPRFilePool() is not safe, it might be in use by worker - LLAPRFile::remove(getTextureFileName(id), mHeaderAPRFilePoolp); + LLFile::remove(getTextureFileName(id)); } //called after mHeaderMutex is locked. @@ -2245,9 +2241,7 @@ void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename) if (entry.mBodySize == 0) // Always attempt to remove when mBodySize > 0. { // Sanity check. Shouldn't exist when body size is 0. - // We are inside header's mutex so mHeaderAPRFilePoolp is safe to use, - // but getLocalAPRFilePool() is not safe, it might be in use by worker - if (LLAPRFile::isExist(filename, mHeaderAPRFilePoolp)) + if (LLFile::isfile(filename)) { LL_WARNS("TextureCache") << "Entry has body size of zero but file " << filename << " exists. Deleting this file, too." << LL_ENDL; } @@ -2267,7 +2261,7 @@ void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename) if (file_maybe_exists) { - LLAPRFile::remove(filename, mHeaderAPRFilePoolp); + LLFile::remove(filename); } } diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 65a69acc88..69c63a6ac8 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -484,7 +484,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() } else { - S32 size = LLAPRFile::size(getFileName()); + S32 size = (S32)LLFile::size(getFileName()); U8* buffer = new(std::nothrow) U8[size]; if (!buffer) { diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index 5d456b1a19..9537ad745b 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -1370,7 +1370,7 @@ void LLVOCache::removeFromCache(HeaderEntryInfo* entry) std::string filename; getObjectCacheFilename(entry->mHandle, filename); LL_WARNS("GLTF", "VOCache") << "Removing object cache for handle " << entry->mHandle << "Filename: " << filename << LL_ENDL; - LLAPRFile::remove(filename, mLocalAPRFilePoolp); + LLFile::remove(filename); // Note: `removeFromCache` should take responsibility for cleaning up all cache artefacts specfic to the handle/entry. // as such this now includes the generic extras @@ -1394,7 +1394,7 @@ void LLVOCache::readCacheHeader() clearCacheInMemory(); bool success = true ; - if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp)) + if (LLFile::isfile(mHeaderFileName)) { LLAPRFile apr_file(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp); -- cgit v1.3 From 9756b4b70b3cd24c03142d7e52eb2acae52002ef Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Sun, 26 Oct 2025 19:59:02 +0100 Subject: Clarify some documentation and add an LLFile:read() and LLFile::write() static function. These functions will be used to replace LLAPRFile::readEx() and LLAPRFile::writeEx() in an upcoming patch. --- indra/llcommon/llfile.cpp | 157 +++++++++++++++++++++++++++++++++++----------- indra/llcommon/llfile.h | 33 +++++++--- 2 files changed, 144 insertions(+), 46 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 4ef39674c2..9fff614efb 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -155,12 +155,15 @@ static std::wstring utf8path_to_wstring(const std::string& utf8path) } return ll_convert(utf8path); } - -static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false) +// This function retrieves the file attributes as they are used in Posix +// Like the Posix file functions it returns 0 on success, on failure it returns -1 and sets the errno variable +// if dontfollowSymlink is true it returns the attributes of the symlink itself if it is one +static int get_fileattr(const std::wstring& utf16path, unsigned short& st_mode, bool dontFollowSymLink = false) { - unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS; + unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS; // This allows CreateFile() to open a handle to a directory if (dontFollowSymLink) { + // This lets CreateFile() open a handle to a symlink rather than to the resolved target flags |= FILE_FLAG_OPEN_REPARSE_POINT; } HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES, @@ -175,27 +178,27 @@ static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollo bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || (iswalpha(utf16path[0]) && utf16path[1] == ':' && (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3]))); - unsigned short st_mode = is_directory ? S_IFDIR : - (attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG); + st_mode = is_directory ? S_IFDIR : + (attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG); st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE; - // we do not try to guess executable flag + // we do not try to guess the executable flag // propagate user bits to group/other fields: st_mode |= (st_mode & 0700) >> 3; st_mode |= (st_mode & 0700) >> 6; CloseHandle(file_handle); - return st_mode; + return 0; } } // Retrieve last error and set errno before calling CloseHandle() - set_errno_from_oserror(GetLastError()); + int rc = set_errno_from_oserror(GetLastError()); if (file_handle != INVALID_HANDLE_VALUE) { CloseHandle(file_handle); } - return 0; + return rc; } #else @@ -343,7 +346,11 @@ int LLFile::rmdir(const std::string& dirname, int suppress_error) { #if LL_WINDOWS std::wstring utf16dirname = utf8path_to_wstring(dirname); - int rc = _wrmdir(utf16dirname.c_str()); + if (RemoveDirectoryW(utf16dirname.c_str())) + { + return 0; + } + int rc = set_errno_from_oserror(GetLastError()); #else int rc = ::rmdir(dirname.c_str()); #endif @@ -402,27 +409,31 @@ int LLFile::remove(const std::string& filename, int suppress_error) // as its siblings unlink() and _wunlink(). // If we really only want to support files we should instead use // unlink() in the non-Windows part below too - int rc = -1; std::wstring utf16filename = utf8path_to_wstring(filename); - unsigned short st_mode = get_fileattr(utf16filename); - if (S_ISDIR(st_mode)) - { - rc = _wrmdir(utf16filename.c_str()); - } - else if (S_ISREG(st_mode)) - { - rc = _wunlink(utf16filename.c_str()); - } - else if (st_mode) - { - // it is something else than a file or directory - // this should not really happen as long as we do not allow for symlink - // detection in the optional parameter to get_fileattr() - rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER); - } - else + unsigned short st_mode; + int rc = get_fileattr(utf16filename, st_mode); + if (rc == 0) { - // get_fileattr() failed and already set errno, preserve it for correct error reporting + if (S_ISDIR(st_mode)) + { + if (RemoveDirectoryW(utf16filename.c_str())) + { + return 0; + } + } + else if (S_ISREG(st_mode)) + { + if (DeleteFileW(utf16filename.c_str())) + { + return 0; + } + } + else + { + SetLastError(ERROR_INVALID_PARAMETER); + } + // Deleting the file or directory failed + rc = set_errno_from_oserror(GetLastError()); } #else int rc = ::remove(filename.c_str()); @@ -439,17 +450,91 @@ int LLFile::rename(const std::string& filename, const std::string& newname, int // API MoveFileEx() and use its flags to specify that overwrite is allowed. std::wstring utf16filename = utf8path_to_wstring(filename); std::wstring utf16newname = utf8path_to_wstring(newname); - int rc = 0; - if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) + if (MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) { - rc = set_errno_from_oserror(GetLastError()); + return 0; } + int rc = set_errno_from_oserror(GetLastError()); #else int rc = ::rename(filename.c_str(),newname.c_str()); #endif return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error); } +// static +U64 LLFile::read(const std::string& filename, void* buf, U64 offset, U64 nbytes) +{ + LLFILE* file_handle = LLFile::fopen(filename, "rb"); + if (file_handle == nullptr) + { + warnif("read, opening file", filename, -1); + return 0; + } + + if (offset > 0) + { +#if LL_WINDOWS + // On Windows fseek() uses a long value as offset which is always 32-bit + int rc = _fseeki64(file_handle, (long long)offset, SEEK_SET); +#else + // offset is of type long which is 64-bit in 64-bit GCC + int rc = fseek(file_handle, (long)offset, SEEK_SET); +#endif + if (rc) + { + warnif("read, setting file offset", filename, rc); + fclose(file_handle); + return 0; + } + } + + // element_count is of size_t which is 64-bit on all 64-bit systems + U64 bytes_read = fread(buf, 1, nbytes, file_handle); + if (bytes_read == 0) + { + warnif("read from file", filename, -1); + } + fclose(file_handle); + return bytes_read; +} + +// static +U64 LLFile::write(const std::string& filename, const void* buf, U64 offset, U64 nbytes) +{ + LLFILE* file_handle = LLFile::fopen(filename, "wb"); + if (file_handle == nullptr) + { + warnif("write, opening file", filename, -1); + return 0; + } + + if (offset > 0) + { +#if LL_WINDOWS + // On Windows fseek() uses a long value as offset which is always 32-bit + int rc = _fseeki64(file_handle, (long long)offset, SEEK_SET); +#else + // offset is of type long which is 64-bit in 64-bit GCC + int rc = fseek(file_handle, (long)offset, SEEK_SET); +#endif + if (rc) + { + warnif("write, setting file offset", filename, rc); + fclose(file_handle); + return 0; + } + } + + // element_count is of size_t which is 64-bit on all 64-bit systems + U64 bytes_written = fwrite(buf, 1, nbytes, file_handle); + if (bytes_written == 0) + { + warnif("write to file", filename, -1); + } + fclose(file_handle); + return bytes_written; +} + // Make this a define rather than using magic numbers multiple times in the code #define LLFILE_COPY_BUFFER_SIZE 16384 @@ -523,7 +608,7 @@ S64 LLFile::size(const std::string& filename, int suppress_error) int rc = ::stat(filename.c_str(), &filestatus); if (rc == 0) { - if (S_ISREG(filestatus.st_mode) + if (S_ISREG(filestatus.st_mode)) { return filestatus.st_size; } @@ -542,10 +627,10 @@ unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLi #if LL_WINDOWS // _wstat64() is a bit heavyweight on Windows, use a more lightweight API // to just get the attributes - int rc = -1; std::wstring utf16filename = utf8path_to_wstring(filename); - unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink); - if (st_mode) + unsigned short st_mode; + int rc = get_fileattr(utf16filename, st_mode, dontFollowSymLink); + if (rc == 0) { return st_mode; } diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 61ec8a5e4e..a856f34be6 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -45,8 +45,8 @@ typedef FILE LLFILE; // We use _stat64 here to support 64-bit st_size and time_t values typedef struct _stat64 llstat; #else -typedef struct stat llstat; #include +typedef struct stat llstat; #endif #ifndef S_ISREG @@ -74,8 +74,11 @@ typedef struct stat llstat; class LL_COMMON_API LLFile { public: +//---------------------------------------------------------------------------------------- +// Static member functions +//---------------------------------------------------------------------------------------- /// open a file with the specified access mode - static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */ + static LLFILE* fopen(const std::string& filename, const char* accessmode); ///< 'accessmode' follows the rules of the Posix fopen() mode parameter /// "r" open the file for reading only and positions the stream at the beginning /// "r+" open the file for reading and writing and positions the stream at the beginning @@ -91,14 +94,13 @@ public: /// This means that it is always a good idea to append "b" specifically for binary file access to /// avoid corruption of the binary consistency of the data stream when reading or writing /// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter - /// @returns a valid LLFILE* pointer on success or NULL on failure + /// @returns a valid LLFILE* pointer on success that can be passed to the fread() and fwrite() functions + /// and some other f functions in the Standard C library that accept a FILE* as parameter + /// or NULL on failure + /// Close a file handle opened with fopen() above static int close(LLFILE * file); - /// retrieve the content of a file into a string - static std::string getContents(const std::string& filename); - ///< @returns the content of the file or an empty string on failure - /// create a directory static int mkdir(const std::string& filename, int perms = 0700); ///< perms is a permissions mask like 0777 or 0700. In most cases it will be @@ -127,11 +129,22 @@ public: /// does not make such guarantees. /// @returns 0 on success and -1 on failure. - - /// copy the contents of file from 'from' to 'to' filename + /// copy the contents of the file from 'from' to 'to' filename static bool copy(const std::string& from, const std::string& to); ///< @returns true on success and false on failure. + /// retrieve the content of a file into a string + static std::string getContents(const std::string& filename); + ///< @returns the content of the file or an empty string on failure + + /// read nBytes from the file into the buffer, starting at offset in the file + static U64 read(const std::string& filename, void* buf, U64 offset, U64 nbytes); + ///< @returns bytes read on success, 0 on failure + + /// write nBytes from the buffer into the file, starting at offset in the file + static U64 write(const std::string& filename, const void* buf, U64 offset, U64 nbytes); + ///< @returns bytes written on success, 0 on failure + /// return the file stat structure for filename static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT); ///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the @@ -145,7 +158,7 @@ public: /// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with /// warnings when the file or directory does not exist - /// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise + /// @returns 0 on failure and a st_mode value with either S_IFDIR, S_IFREG or S_IFLNK set, /// together with the three access bits which under Windows only the write bit is relevant. /// get the size of a file in bytes -- cgit v1.3 From f07762a46830005b6ff4218c1f070ce27a9ecebe Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Wed, 12 Nov 2025 19:19:59 +0100 Subject: Refactoring of LLFile class to support additional methods - LLFile with its own class method interface to access files for read and write - Remove rudimentary LLUniqueFile class as LLFile supports now all of that and more - Implement most of the filename based functions using std::filesystem functions - Replace LLFile::rmdir() with LLFile::remove() since this function now supports deleting files and directories on all platforms. --- indra/llcommon/llerror.cpp | 10 +- indra/llcommon/llfile.cpp | 1159 +++++++++++++++++++++---------- indra/llcommon/llfile.h | 396 +++++++---- indra/llcrashlogger/llcrashlock.cpp | 7 +- indra/llfilesystem/lldir.cpp | 51 +- indra/llfilesystem/lldir_win32.cpp | 13 +- indra/llfilesystem/llfilesystem.cpp | 19 +- indra/llfilesystem/tests/lldir_test.cpp | 4 +- indra/llrender/llshadermgr.cpp | 17 +- indra/llxml/tests/llcontrol_test.cpp | 2 +- indra/newview/llappviewer.cpp | 10 +- indra/newview/llfloaterpreference.cpp | 12 +- indra/newview/llfloateruipreview.cpp | 11 +- indra/newview/llpreviewnotecard.cpp | 3 +- indra/newview/llpreviewscript.cpp | 3 +- indra/newview/lltexturecache.cpp | 2 +- indra/newview/llviewerdisplay.cpp | 5 +- indra/newview/llviewermedia.cpp | 5 +- indra/newview/llvocache.cpp | 2 +- indra/newview/llvoicevivox.cpp | 3 +- indra/test/llmessageconfig_tut.cpp | 2 +- indra/test/message_tut.cpp | 2 +- 22 files changed, 1136 insertions(+), 602 deletions(-) mode change 100644 => 100755 indra/llcommon/llerror.cpp mode change 100644 => 100755 indra/llcommon/llfile.cpp mode change 100644 => 100755 indra/llcommon/llfile.h mode change 100644 => 100755 indra/llcrashlogger/llcrashlock.cpp mode change 100644 => 100755 indra/llfilesystem/lldir.cpp mode change 100644 => 100755 indra/llfilesystem/lldir_win32.cpp mode change 100644 => 100755 indra/llfilesystem/llfilesystem.cpp mode change 100644 => 100755 indra/llfilesystem/tests/lldir_test.cpp mode change 100644 => 100755 indra/llrender/llshadermgr.cpp mode change 100644 => 100755 indra/llxml/tests/llcontrol_test.cpp mode change 100644 => 100755 indra/newview/llappviewer.cpp mode change 100644 => 100755 indra/newview/llfloaterpreference.cpp mode change 100644 => 100755 indra/newview/llfloateruipreview.cpp mode change 100644 => 100755 indra/newview/llpreviewnotecard.cpp mode change 100644 => 100755 indra/newview/llpreviewscript.cpp mode change 100644 => 100755 indra/newview/lltexturecache.cpp mode change 100644 => 100755 indra/newview/llviewerdisplay.cpp mode change 100644 => 100755 indra/newview/llviewermedia.cpp mode change 100644 => 100755 indra/newview/llvocache.cpp mode change 100644 => 100755 indra/newview/llvoicevivox.cpp mode change 100644 => 100755 indra/test/llmessageconfig_tut.cpp mode change 100644 => 100755 indra/test/message_tut.cpp (limited to 'indra') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp old mode 100644 new mode 100755 index b14464382b..bfa8bca224 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -435,13 +435,9 @@ namespace std::string file = user_dir + "/logcontrol-dev.xml"; - llstat stat_info; - if (LLFile::stat(file, &stat_info)) { - // NB: stat returns non-zero if it can't read the file, for example - // if it doesn't exist. LLFile has no better abstraction for - // testing for file existence. - - file = app_dir + "/logcontrol.xml"; + if (!LLFile::isfile(file)) + { + file = app_dir + "/logcontrol.xml"; } return * new LogControlFile(file); // NB: This instance is never freed diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp old mode 100644 new mode 100755 index 9fff614efb..6ccf01dd78 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -34,17 +34,13 @@ #include "stringize.h" #if LL_WINDOWS -#include "llwin32headers.h" -#include +#include #else #include +#include #endif -using namespace std; - -static std::string empty; - -// Many of the methods below use OS-level functions that mess with errno. Wrap +// Some of the methods below use OS-level functions that mess with errno. Wrap // variants of strerror() to report errors. #if LL_WINDOWS @@ -79,6 +75,7 @@ static errentry const errtable[] { ERROR_CURRENT_DIRECTORY, EACCES }, // 16 { ERROR_NOT_SAME_DEVICE, EXDEV }, // 17 { ERROR_NO_MORE_FILES, ENOENT }, // 18 + { ERROR_SHARING_VIOLATION, EACCES }, // 32 { ERROR_LOCK_VIOLATION, EACCES }, // 33 { ERROR_BAD_NETPATH, ENOENT }, // 53 { ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65 @@ -109,22 +106,25 @@ static errentry const errtable[] { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816 }; -static int set_errno_from_oserror(unsigned long oserr) +static int get_errno_from_oserror(int oserr) { if (!oserr) return 0; // Check the table for the Windows OS error code - for (const struct errentry &entry : errtable) + for (const struct errentry& entry : errtable) { if (oserr == entry.oserr) { - _set_errno(entry.errcode); - return -1; + return entry.errcode; } } + return EINVAL; +} - _set_errno(EINVAL); +static int set_errno_from_oserror(unsigned long oserr) +{ + _set_errno(get_errno_from_oserror(oserr)); return -1; } @@ -136,72 +136,8 @@ std::string strerr(int errn) return buffer; } -inline bool is_slash(wchar_t const c) -{ - return c == L'\\' || c == L'/'; -} - -static std::wstring utf8path_to_wstring(const std::string& utf8path) -{ - if (utf8path.size() >= MAX_PATH) - { - // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names - std::wstring utf16path = L"\\\\?\\" + ll_convert(utf8path); - // We need to make sure that the path does not contain forward slashes as above - // prefix does bypass the path normalization that replaces slashes with backslashes - // before passing the path to kernel mode APIs - std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\'); - return utf16path; - } - return ll_convert(utf8path); -} -// This function retrieves the file attributes as they are used in Posix -// Like the Posix file functions it returns 0 on success, on failure it returns -1 and sets the errno variable -// if dontfollowSymlink is true it returns the attributes of the symlink itself if it is one -static int get_fileattr(const std::wstring& utf16path, unsigned short& st_mode, bool dontFollowSymLink = false) -{ - unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS; // This allows CreateFile() to open a handle to a directory - if (dontFollowSymLink) - { - // This lets CreateFile() open a handle to a symlink rather than to the resolved target - flags |= FILE_FLAG_OPEN_REPARSE_POINT; - } - HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - nullptr, OPEN_EXISTING, flags, nullptr); - if (file_handle != INVALID_HANDLE_VALUE) - { - FILE_ATTRIBUTE_TAG_INFO attribute_info; - if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info))) - { - // A volume path alone (only drive letter) is not recognized as directory while it technically is - bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || - (iswalpha(utf16path[0]) && utf16path[1] == ':' && - (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3]))); - st_mode = is_directory ? S_IFDIR : - (attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG); - st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE; - // we do not try to guess the executable flag - - // propagate user bits to group/other fields: - st_mode |= (st_mode & 0700) >> 3; - st_mode |= (st_mode & 0700) >> 6; - - CloseHandle(file_handle); - return 0; - } - } - // Retrieve last error and set errno before calling CloseHandle() - int rc = set_errno_from_oserror(GetLastError()); - - if (file_handle != INVALID_HANDLE_VALUE) - { - CloseHandle(file_handle); - } - return rc; -} - #else + // On Posix we want to call strerror_r(), but alarmingly, there are two // different variants. The one that returns int always populates the passed // buffer (except in case of error), whereas the other one always returns a @@ -248,8 +184,49 @@ std::string strerr(int errn) return message_from(errn, buffer, sizeof(buffer), strerror_r(errn, buffer, sizeof(buffer))); } + #endif // ! LL_WINDOWS +#if LL_WINDOWS && 0 // turn on to debug file-locking problems +#define PROCESS_LOCKING_CHECK 1 +static void find_locking_process(const std::string& filename) +{ + // Only do any of this stuff (before LL_ENDL) if it will be logged. + LL_DEBUGS("LLFile") << ""; + // wrong way + std::string TEMP = LLFile::tmpdir(); + if (TEMP.empty()) + { + LL_CONT << "No $TEMP, not running 'handle'"; + } + else + { + std::string tf(TEMP); + tf += "\\handle.tmp"; + // http://technet.microsoft.com/en-us/sysinternals/bb896655 + std::string cmd(STRINGIZE("handle \"" << filename + // "openfiles /query /v | fgrep -i \"" << filename + << "\" > \"" << tf << '"')); + LL_CONT << cmd; + if (system(cmd.c_str()) != 0) + { + LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655"; + } + else + { + std::ifstream inf(tf); + std::string line; + while (std::getline(inf, line)) + { + LL_CONT << '\n' << line; + } + } + LLFile::remove(tf); + } + LL_CONT << LL_ENDL; +} +#endif // LL_WINDOWS hack to identify processes holding file open + static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0) { if (rc < 0) @@ -262,437 +239,905 @@ static int warnif(const std::string& desc, const std::string& filename, int rc, // EEXIST. Don't warn if caller explicitly says this errno is okay. if (errn != accept) { - LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename - << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL; + LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL; } -#if 0 && LL_WINDOWS // turn on to debug file-locking problems +#if PROCESS_LOCKING_CHECK // If the problem is "Permission denied," maybe it's because another // process has the file open. Try to find out. - if (errn == EACCES) // *not* EPERM + if (errn == EACCES) // *not* EPERM { - // Only do any of this stuff (before LL_ENDL) if it will be logged. - LL_DEBUGS("LLFile") << empty; - // would be nice to use LLDir for this, but dependency goes the - // wrong way - const char* TEMP = LLFile::tmpdir(); - if (! (TEMP && *TEMP)) - { - LL_CONT << "No $TEMP, not running 'handle'"; - } - else - { - std::string tf(TEMP); - tf += "\\handle.tmp"; - // http://technet.microsoft.com/en-us/sysinternals/bb896655 - std::string cmd(STRINGIZE("handle \"" << filename - // "openfiles /query /v | fgrep -i \"" << filename - << "\" > \"" << tf << '"')); - LL_CONT << cmd; - if (system(cmd.c_str()) != 0) - { - LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655"; - } - else - { - std::ifstream inf(tf); - std::string line; - while (std::getline(inf, line)) - { - LL_CONT << '\n' << line; - } - } - LLFile::remove(tf); - } - LL_CONT << LL_ENDL; + find_locking_process(filename); } -#endif // LL_WINDOWS hack to identify processes holding file open +#endif } return rc; } -// static -int LLFile::mkdir(const std::string& dirname, int perms) +static int warnif(const std::string& desc, const std::string& filename, std::error_code& ec, int accept = 0) { - // We often use mkdir() to ensure the existence of a directory that might - // already exist. There is no known case in which we want to call out as - // an error the requested directory already existing. + if (ec) + { + // get Posix errno from the std::error_code so we can compare it to the accept parameter to see + // when a caller wants us to not generate a warning for a particular error code #if LL_WINDOWS - // permissions are ignored on Windows - int rc = 0; - std::wstring utf16dirname = utf8path_to_wstring(dirname); - if (!CreateDirectoryW(utf16dirname.c_str(), nullptr)) - { - // Only treat other errors than an already existing file as a real error - unsigned long oserr = GetLastError(); - if (oserr != ERROR_ALREADY_EXISTS) + int errn = get_errno_from_oserror(ec.value()); +#else + int errn = ec.value(); +#endif + // For certain operations, a particular errno value might be acceptable + // Don't warn if caller explicitly says this errno is okay. + if (errn != accept) + { + LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << ec.message() << LL_ENDL; + } +#if PROCESS_LOCKING_CHECK + // Try to detect locked files by other processes + if (ec.value() == ERROR_SHARING_VIOLATION || ec.value() == ERROR_LOCK_VIOLATION) { - rc = set_errno_from_oserror(oserr); + find_locking_process(filename); } +#endif + return -1; } + return 0; +} + +#if LL_WINDOWS + +inline int set_ec_from_system_error(std::error_code& ec, DWORD error) +{ + ec.assign(error, std::system_category()); + return -1; +} + +static int set_ec_from_system_error(std::error_code& ec) +{ + return set_ec_from_system_error(ec, GetLastError()); +} + +inline int set_ec_to_parameter_error(std::error_code& ec) +{ + return set_ec_from_system_error(ec, ERROR_INVALID_PARAMETER); +} + +inline int set_ec_to_outofmemory_error(std::error_code& ec) +{ + return set_ec_from_system_error(ec, ERROR_NOT_ENOUGH_MEMORY); +} + +inline DWORD decode_access_mode(std::ios_base::openmode omode) +{ + switch (omode & (LLFile::in | LLFile::out)) + { + case LLFile::in: + return GENERIC_READ; + case LLFile::out: + return GENERIC_WRITE; + case LLFile::in | LLFile::out: + return GENERIC_READ | GENERIC_WRITE; + } + if (omode & LLFile::app) + { + return GENERIC_WRITE; + } + return 0; +} + +inline DWORD decode_open_create_flags(std::ios_base::openmode omode) +{ + if (omode & LLFile::noreplace) + { + return CREATE_NEW; // create if it does not exist, otherwise fail + } + if (omode & LLFile::trunc) + { + if (!(omode & LLFile::out)) + { + return TRUNCATE_EXISTING; // open and truncatte if it exists, otherwise fail + } + return CREATE_ALWAYS; // open and truncate if it exists, otherwise create it + } + if (!(omode & LLFile::out)) + { + return OPEN_EXISTING; // open if exists, otherwise fail + } + // LLFile::app or (LLFile::out and (!LLFile::trunc or !LLFile::noreplace)) + return OPEN_ALWAYS; // open if it exists, otherwise create it +} + +inline DWORD decode_share_mode(int omode) +{ + if (omode & LLFile::exclusive) + { + return 0; // allow no other access + } + if (omode & LLFile::shared) + { + return FILE_SHARE_READ; // allow read access + } + return FILE_SHARE_READ | FILE_SHARE_WRITE; // allow read and write access to others +} + +inline DWORD decode_attributes(std::ios_base::openmode omode, int perm) +{ + return (perm & S_IWRITE) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_READONLY; +} + +// Under Windows the values for the std::ios_base::seekdir constants match the according FILE_BEGIN +// and other constants but we do a programmatic translation for now to be sure +static DWORD seek_mode_from_dir(std::ios_base::seekdir seekdir) +{ + switch (seekdir) + { + case LLFile::beg: + return FILE_BEGIN; + case LLFile::cur: + return FILE_CURRENT; + case LLFile::end: + return FILE_END; + } + return FILE_BEGIN; +} + #else - int rc = ::mkdir(dirname.c_str(), (mode_t)perms); - if (rc < 0 && errno == EEXIST) + +inline int set_ec_from_system_error(std::error_code& ec, int error) +{ + ec.assign(error, std::system_category()); + return -1; +} + +static int set_ec_from_system_error(std::error_code& ec) +{ + return set_ec_from_system_error(ec, errno); +} + +inline int set_ec_to_parameter_error(std::error_code& ec) +{ + return set_ec_from_system_error(ec, EINVAL); +} + +inline int set_ec_to_outofmemory_error(std::error_code& ec) +{ + return set_ec_from_system_error(ec, ENOMEM); +} + +inline int decode_access_mode(std::ios_base::openmode omode) +{ + switch (omode & (LLFile::in | LLFile::out)) { - // this is not the error you want, move along - return 0; + case LLFile::out: + return O_WRONLY; + case LLFile::in | LLFile::out: + return O_RDWR; + } + return O_RDONLY; +} + +inline int decode_open_mode(std::ios_base::openmode omode) +{ + int flags = O_CREAT | decode_access_mode(omode); + if (omode & LLFile::app) + { + flags |= O_APPEND; + } + if (omode & LLFile::trunc) + { + flags |= O_TRUNC; + } + if (omode & LLFile::binary) + { + // Not a thing under *nix + } + if (omode & LLFile::noreplace) + { + flags |= O_EXCL; + } + return flags; +} + +inline int decode_lock_mode(std::ios_base::openmode omode) +{ + int lmode = omode & LLFile::noblock ? LOCK_NB : 0; + if (omode & LLFile::lock_mask) + { + if (omode & LLFile::exclusive) + { + return lmode | LOCK_EX; + } + return lmode | LOCK_SH; + } + return lmode | LOCK_UN; +} + +// Under Linux and Mac the values for the std::ios_base::seekdir constants match the according SEEK_SET +// and other constants but we do a programmatic translation for now to be sure +inline int seek_mode_from_dir(std::ios_base::seekdir seekdir) +{ + switch (seekdir) + { + case LLFile::beg: + return SEEK_SET; + case LLFile::cur: + return SEEK_CUR; + case LLFile::end: + return SEEK_END; + } + return SEEK_SET; +} + +#endif + +inline int clear_error(std::error_code& ec) +{ + ec.clear(); + return 0; +} + +inline bool are_open_mode_flags_invalid(std::ios_base::openmode omode) +{ + // at least one of input or output needs to be specified + if (!(omode & (LLFile::in | LLFile::out))) + { + return true; + } + // output must be possible for any of the extra options + if (!(omode & LLFile::out) && (omode & (LLFile::trunc | LLFile::app | LLFile::noreplace))) + { + return true; + } + // invalid combination, mutually exclusive + if ((omode & LLFile::app) && (omode & (LLFile::trunc | LLFile::noreplace))) + { + return true; + } + return false; +} + +//---------------------------------------------------------------------------------------- +// class member functions +//---------------------------------------------------------------------------------------- +int LLFile::open(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm) +{ + close(ec); + if (are_open_mode_flags_invalid(omode)) + { + return set_ec_to_parameter_error(ec); + } +#if LL_WINDOWS + DWORD access = decode_access_mode(omode), + share = decode_share_mode(omode), + create = decode_open_create_flags(omode), + attributes = decode_attributes(omode, perm); + + std::wstring file_path = utf8StringToWstring(filename); + mHandle = CreateFileW(file_path.c_str(), access, share, nullptr, create, attributes, nullptr); +#else + int oflags = decode_open_mode(omode); + int lmode = LLFile::noblock | (omode & LLFile::lock_mask); + mHandle = ::open(filename.c_str(), oflags, perm); + if (mHandle != InvalidHandle && omode & LLFile::lock_mask && + lock(omode | LLFile::noblock, ec) != 0) + { + close(); + return -1; } #endif - // anything else might be a problem - return warnif("mkdir", dirname, rc); + if (mHandle == InvalidHandle) + { + return set_ec_from_system_error(ec); + } + + if (omode & LLFile::ate && seek(0, LLFile::end, ec) != 0) + { + close(); + return -1; + } + mOpen = omode; + return clear_error(ec); } -// static -int LLFile::rmdir(const std::string& dirname, int suppress_error) +S64 LLFile::size(std::error_code& ec) { #if LL_WINDOWS - std::wstring utf16dirname = utf8path_to_wstring(dirname); - if (RemoveDirectoryW(utf16dirname.c_str())) + LARGE_INTEGER value = { 0 }; + if (GetFileSizeEx(mHandle, &value)) { - return 0; + clear_error(ec); + return value.QuadPart; } - int rc = set_errno_from_oserror(GetLastError()); #else - int rc = ::rmdir(dirname.c_str()); + struct stat statval; + if (fstat(mHandle, &statval) == 0) + { + clear_error(ec); + return statval.st_size; + } #endif - return warnif("rmdir", dirname, rc, suppress_error); + return set_ec_from_system_error(ec); } -// static -LLFILE* LLFile::fopen(const std::string& filename, const char* mode) +S64 LLFile::tell(std::error_code& ec) { #if LL_WINDOWS - std::wstring utf16filename = utf8path_to_wstring(filename); - std::wstring utf16mode = ll_convert(std::string(mode)); - return _wfopen(utf16filename.c_str(), utf16mode.c_str()); + LARGE_INTEGER value = { 0 }; + if (SetFilePointerEx(mHandle, value, &value, FILE_CURRENT)) + { + clear_error(ec); + return value.QuadPart; + } #else - return ::fopen(filename.c_str(),mode); + off_t offset = lseek(mHandle, 0, SEEK_CUR); + if (offset != -1) + { + clear_error(ec); + return offset; + } #endif + return set_ec_from_system_error(ec); } -// static -int LLFile::close(LLFILE * file) +int LLFile::seek(S64 pos, std::error_code& ec) { - int ret_value = 0; - if (file) + return seek(pos, LLFile::beg, ec); +} + +int LLFile::seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec) +{ + S64 newOffset = 0; +#if LL_WINDOWS + DWORD seekdir = seek_mode_from_dir(dir); + LARGE_INTEGER value; + value.QuadPart = offset; + if (SetFilePointerEx(mHandle, value, (PLARGE_INTEGER)&newOffset, seekdir)) +#else + newOffset = lseek(mHandle, offset, seek_mode_from_dir(dir)); + if (newOffset != -1) +#endif { - ret_value = fclose(file); + return clear_error(ec); } - return ret_value; + return set_ec_from_system_error(ec); } -// static -std::string LLFile::getContents(const std::string& filename) +#if LL_WINDOWS +inline DWORD next_buffer_size(S64 nbytes) +{ + return nbytes > 0x80000000 ? 0x80000000 : (DWORD)nbytes; +} +#endif + +S64 LLFile::read(void* buffer, S64 nbytes, std::error_code& ec) +{ + if (nbytes == 0) + { + // Nothing to do + clear_error(ec); + return 0; + } + else if (!buffer || nbytes < 0) + { + return set_ec_to_parameter_error(ec); + } +#if LL_WINDOWS + S64 totalBytes = 0; + char *ptr = (char*)buffer; + DWORD bytesRead, bytesToRead = next_buffer_size(nbytes); + + // Read in chunks to support >4GB which the S64 nbytes value makes possible + while (ReadFile(mHandle, ptr, bytesToRead, &bytesRead, nullptr)) + { + totalBytes += bytesRead; + if (nbytes <= totalBytes || // requested amount read + bytesRead < bytesToRead) // ReadFile encountered eof + { + clear_error(ec); + return totalBytes; + } + ptr += bytesRead; + bytesToRead = next_buffer_size(nbytes - totalBytes); + } +#else + ssize_t bytesRead = ::read(mHandle, buffer, nbytes); + if (bytesRead != -1) + { + clear_error(ec); + return bytesRead; + } +#endif + return set_ec_from_system_error(ec); +} + +S64 LLFile::write(const void* buffer, S64 nbytes, std::error_code& ec) { - LLFILE* fp = LLFile::fopen(filename, "rb"); - if (fp) + if (nbytes == 0) + { + // Nothing to do here + clear_error(ec); + return 0; + } + else if (!buffer || nbytes < 0) + { + return set_ec_to_parameter_error(ec); + } +#if LL_WINDOWS + // If this was opened in append mode, we emulate it on Windows + if (mOpen & LLFile::app && seek(0, LLFile::end, ec) != 0) { - fseek(fp, 0, SEEK_END); - U32 length = ftell(fp); - fseek(fp, 0, SEEK_SET); + return -1; + } - std::vector buffer(length); - size_t nread = fread(buffer.data(), 1, length, fp); - fclose(fp); + S64 totalBytes = 0; + char* ptr = (char*)buffer; + DWORD bytesWritten, bytesToWrite = next_buffer_size(nbytes); - return std::string(buffer.data(), nread); + // Write in chunks to support >4GB which the S64 nbytes value makes possible + while (WriteFile(mHandle, ptr, bytesToWrite, &bytesWritten, nullptr)) + { + totalBytes += bytesWritten; + if (nbytes <= totalBytes) + { + clear_error(ec); + return totalBytes; + } + ptr += bytesWritten; + bytesToWrite = next_buffer_size(nbytes - totalBytes); } +#else + ssize_t bytesWritten = ::write(mHandle, buffer, nbytes); + if (bytesWritten != -1) + { + clear_error(ec); + return bytesWritten; + } +#endif + return set_ec_from_system_error(ec); +} - return LLStringUtil::null; +S64 LLFile::printf(const char* fmt, ...) +{ + va_list args1; + va_start(args1, fmt); + va_list args2; + va_copy(args2, args1); + int length = vsnprintf(NULL, 0, fmt, args1); + va_end(args1); + if (length < 0) + { + va_end(args2); + return -1; + } + void* buffer = malloc(length + 1); + if (!buffer) + { + va_end(args2); + return -1; + } + length = vsnprintf((char*)buffer, length + 1, fmt, args2); + va_end(args2); + std::error_code ec; + S64 written = write(buffer, length, ec); + free(buffer); + return written; } -// static -int LLFile::remove(const std::string& filename, int suppress_error) +int LLFile::lock(int mode, std::error_code& ec) { #if LL_WINDOWS - // Posix remove() works on both files and directories although on Windows - // remove() and its wide char variant _wremove() only removes files just - // as its siblings unlink() and _wunlink(). - // If we really only want to support files we should instead use - // unlink() in the non-Windows part below too - std::wstring utf16filename = utf8path_to_wstring(filename); - unsigned short st_mode; - int rc = get_fileattr(utf16filename, st_mode); - if (rc == 0) + if (!(mode & LLFile::lock_mask)) { - if (S_ISDIR(st_mode)) + if (UnlockFile(mHandle, 0, 0, MAXDWORD, MAXDWORD)) { - if (RemoveDirectoryW(utf16filename.c_str())) - { - return 0; - } + return clear_error(ec); } - else if (S_ISREG(st_mode)) + } + else + { + OVERLAPPED overlapped = { 0 }; + DWORD flags = (mode & LLFile::noblock) ? LOCKFILE_FAIL_IMMEDIATELY : 0; + if (mode & LLFile::exclusive) { - if (DeleteFileW(utf16filename.c_str())) - { - return 0; - } + flags |= LOCKFILE_EXCLUSIVE_LOCK; } - else + // We lock the maximum range, since flock only supports locking the entire file too + if (LockFileEx(mHandle, flags, 0, MAXDWORD, MAXDWORD, &overlapped)) { - SetLastError(ERROR_INVALID_PARAMETER); + return clear_error(ec); } - // Deleting the file or directory failed - rc = set_errno_from_oserror(GetLastError()); } #else - int rc = ::remove(filename.c_str()); + if (flock(mHandle, decode_lock_mode(mode)) == 0) + { + return clear_error(ec); + } #endif - return warnif("remove", filename, rc, suppress_error); + return set_ec_from_system_error(ec); +} + +int LLFile::close(std::error_code& ec) +{ + if (mHandle != InvalidHandle) + { + llfile_handle_t handle = InvalidHandle; + std::swap(handle, mHandle); +#if LL_WINDOWS + if (!CloseHandle(handle)) +#else + if (::close(handle)) +#endif + { + return set_ec_from_system_error(ec); + } + } + return clear_error(ec); +} + +int LLFile::close() +{ + std::error_code ec; + return close(ec); } +//---------------------------------------------------------------------------------------- +// static member functions +//---------------------------------------------------------------------------------------- + // static -int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error) +LLFILE* LLFile::fopen(const std::string& filename, const char* mode, int lmode) { + LLFILE* file; #if LL_WINDOWS - // Posix rename() will gladly overwrite a file at newname if it exists, the Windows - // rename(), respectively _wrename(), will bark on that. Instead call directly the Windows - // API MoveFileEx() and use its flags to specify that overwrite is allowed. - std::wstring utf16filename = utf8path_to_wstring(filename); - std::wstring utf16newname = utf8path_to_wstring(newname); - if (MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) + int shflag = _SH_DENYNO; + switch (lmode) { - return 0; + case LLFile::exclusive: + shflag = _SH_DENYRW; + break; + case LLFile::shared: + shflag = _SH_DENYWR; + break; } - int rc = set_errno_from_oserror(GetLastError()); + std::wstring file_path = utf8StringToWstring(filename); + std::wstring utf16mode = ll_convert(std::string(mode)); + file = _wfsopen(file_path.c_str(), utf16mode.c_str(), shflag); #else - int rc = ::rename(filename.c_str(),newname.c_str()); + file = ::fopen(filename.c_str(), mode); + if (file && (lmode & (LLFile::lock_mask))) + { + // Rather fail on a sharing conflict than block + if (flock(fileno(file), decode_lock_mode(lmode | LLFile::noblock))) + { + LLFile::close(file); + file = nullptr; + } + } #endif - return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error); + return file; } // static -U64 LLFile::read(const std::string& filename, void* buf, U64 offset, U64 nbytes) +int LLFile::close(LLFILE* file) { - LLFILE* file_handle = LLFile::fopen(filename, "rb"); - if (file_handle == nullptr) + int ret_value = 0; + if (file) { - warnif("read, opening file", filename, -1); - return 0; + // Read the current errno and restore it if it was not 0 + int errn = errno; + ret_value = ::fclose(file); + if (errn) + { + errno = errn; + } } + return ret_value; +} - if (offset > 0) +// static +std::string LLFile::getContents(const std::string& filename) +{ + std::error_code ec; + return getContents(filename, ec); +} + +// static +std::string LLFile::getContents(const std::string& filename, std::error_code& ec) +{ + std::string buffer; + LLFile file(filename, LLFile::in | LLFile::binary, ec); + if (file) { -#if LL_WINDOWS - // On Windows fseek() uses a long value as offset which is always 32-bit - int rc = _fseeki64(file_handle, (long long)offset, SEEK_SET); -#else - // offset is of type long which is 64-bit in 64-bit GCC - int rc = fseek(file_handle, (long)offset, SEEK_SET); -#endif - if (rc) + S64 length = file.size(ec); + if (!ec && length > 0) { - warnif("read, setting file offset", filename, rc); - fclose(file_handle); - return 0; + buffer = std::string(length, 0); + file.read(&buffer[0], length, ec); + if (ec) + { + buffer.clear(); + } } } + return buffer; +} - // element_count is of size_t which is 64-bit on all 64-bit systems - U64 bytes_read = fread(buf, 1, nbytes, file_handle); - if (bytes_read == 0) - { - warnif("read from file", filename, -1); - } - fclose(file_handle); - return bytes_read; +// static +int LLFile::mkdir(const std::string& dirname) +{ + std::error_code ec; + std::filesystem::path file_path = utf8StringToPath(dirname); + // We often use mkdir() to ensure the existence of a directory that might + // already exist. There is no known case in which we want to call out as + // an error the requested directory already existing. + std::filesystem::create_directory(file_path, ec); + // The return value is only true if the directory was actually created. + // But if it already existed, ec still indicates success. + return warnif("mkdir", dirname, ec); +} + +// static +int LLFile::remove(const std::string& filename, int suppress_error) +{ + std::error_code ec; + std::filesystem::path file_path = utf8StringToPath(filename); + std::filesystem::remove(file_path, ec); + return warnif("remove", filename, ec, suppress_error); +} + +// static +int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error) +{ + std::error_code ec; + std::filesystem::path file_path = utf8StringToPath(filename); + std::filesystem::path new_path = utf8StringToPath(newname); + std::filesystem::rename(file_path, new_path, ec); + return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, ec, suppress_error); +} + +// static +S64 LLFile::read(const std::string& filename, void* buf, S64 offset, S64 nbytes) +{ + std::error_code ec; + return read(filename, buf, offset, nbytes, ec); } // static -U64 LLFile::write(const std::string& filename, const void* buf, U64 offset, U64 nbytes) +S64 LLFile::read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec) { - LLFILE* file_handle = LLFile::fopen(filename, "wb"); - if (file_handle == nullptr) + // if number of bytes is 0 or less there is nothing to do here + if (nbytes <= 0) { - warnif("write, opening file", filename, -1); + clear_error(ec); return 0; } - if (offset > 0) + std::ios_base::openmode omode = LLFile::in | LLFile::binary; + + LLFile file(filename, omode, ec); + if (file) { -#if LL_WINDOWS - // On Windows fseek() uses a long value as offset which is always 32-bit - int rc = _fseeki64(file_handle, (long long)offset, SEEK_SET); -#else - // offset is of type long which is 64-bit in 64-bit GCC - int rc = fseek(file_handle, (long)offset, SEEK_SET); -#endif - if (rc) + S64 bytes_read = 0; + if (offset > 0) { - warnif("write, setting file offset", filename, rc); - fclose(file_handle); - return 0; + file.seek(offset, ec); + } + if (!ec) + { + bytes_read = file.read(buf, nbytes, ec); + if (!ec) + { + return bytes_read; + } } } - - // element_count is of size_t which is 64-bit on all 64-bit systems - U64 bytes_written = fwrite(buf, 1, nbytes, file_handle); - if (bytes_written == 0) - { - warnif("write to file", filename, -1); - } - fclose(file_handle); - return bytes_written; + return warnif("read from file failed", filename, ec); } -// Make this a define rather than using magic numbers multiple times in the code -#define LLFILE_COPY_BUFFER_SIZE 16384 +// static +S64 LLFile::write(const std::string& filename, const void* buf, S64 offset, S64 nbytes) +{ + std::error_code ec; + return write(filename, buf, offset, nbytes, ec); +} // static -bool LLFile::copy(const std::string& from, const std::string& to) +S64 LLFile::write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec) { - bool copied = false; - LLFILE* in = LLFile::fopen(from, "rb"); - if (in) + // if number of bytes is 0 or less there is nothing to do here + if (nbytes <= 0) + { + clear_error(ec); + return 0; + } + + std::ios_base::openmode omode = LLFile::out | LLFile::binary; + if (offset < 0) + { + omode |= LLFile::app; + } + + LLFile file(filename, omode, ec); + if (file) { - LLFILE* out = LLFile::fopen(to, "wb"); - if (out) + S64 bytes_written = 0; + if (offset > 0) { - char buf[LLFILE_COPY_BUFFER_SIZE]; - size_t readbytes; - bool write_ok = true; - while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in))) - { - if (fwrite(buf, 1, readbytes, out) != readbytes) - { - LL_WARNS("LLFile") << "Short write" << LL_ENDL; - write_ok = false; - } - } - if ( write_ok ) + file.seek(offset, ec); + } + if (!ec) + { + bytes_written = file.write(buf, nbytes, ec); + if (!ec) { - copied = true; + return bytes_written; } - fclose(out); } - fclose(in); + } + return warnif("write to file failed", filename, ec); +} + +// static +bool LLFile::copy(const std::string& from, const std::string& to) +{ + std::error_code ec; + std::filesystem::path from_path = utf8StringToPath(from); + std::filesystem::path to_path = utf8StringToPath(to); + bool copied = std::filesystem::copy_file(from_path, to_path, ec); + if (!copied) + { + LL_WARNS("LLFile") << "copy failed" << LL_ENDL; } return copied; } // static -int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error) +int LLFile::stat(const std::string& filename, llstat* filestatus, const char *fname, int suppress_warning) { #if LL_WINDOWS - std::wstring utf16filename = utf8path_to_wstring(filename); - int rc = _wstat64(utf16filename.c_str(), filestatus); + std::wstring file_path = utf8StringToWstring(filename); + int rc = _wstat64(file_path.c_str(), filestatus); #else int rc = ::stat(filename.c_str(), filestatus); #endif - return warnif("stat", filename, rc, suppress_error); + return warnif(fname ? fname : "stat", filename, rc, suppress_warning); } // static -S64 LLFile::size(const std::string& filename, int suppress_error) +std::time_t LLFile::getCreationTime(const std::string& filename, int suppress_warning) { -#if LL_WINDOWS - std::wstring utf16filename = utf8path_to_wstring(filename); - HANDLE file_handle = CreateFileW(utf16filename.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE != file_handle) - { - LARGE_INTEGER file_size; - if (GetFileSizeEx(file_handle, &file_size)) - { - CloseHandle(file_handle); - return file_size.QuadPart; - } - } - // Get last error before calling the CloseHandle() function - int rc = set_errno_from_oserror(GetLastError()); - if (INVALID_HANDLE_VALUE != file_handle) + // As if C++20 there is no functionality in std::filesystem to retrieve this information + llstat filestat; + int rc = stat(filename, &filestat, "getCreationTime", suppress_warning); + if (rc == 0) { - CloseHandle(file_handle); - } +#if LL_DARWIN + return filestat.st_birthtime; #else - llstat filestatus; - int rc = ::stat(filename.c_str(), &filestatus); + // Linux stat() doesn't have a creation/birth time (st_ctime really is the last status + // change or inode attributes change) unless we would use statx() instead. But that is + // a major effort, which would require Linux specific changes to LLFile::stat() above + // and possibly adaptions for other platforms that we leave for a later exercise if it + // is ever desired. + return filestat.st_ctime; +#endif + } + return 0; +} + +// static +std::time_t LLFile::getModificationTime(const std::string& filename, int suppress_warning) +{ + // tried to use std::filesystem::last_write_time() but the whole std::chrono infrastructure is as of + // C++20 still not fully implemented on all platforms. Specifically MacOS C++20 seems lacking here, + // and Windows requires a roundabout through std::chrono::utc_clock to then get a + // std::chrono::system_clock that can return a more useful time_t. + // So we take the easy way out in a similar way as with getCreationTime(). + llstat filestat; + int rc = stat(filename, &filestat, "getModificationTime", suppress_warning); if (rc == 0) { - if (S_ISREG(filestatus.st_mode)) - { - return filestatus.st_size; - } - // We got something else than a file - rc = -1; - errno = EACCES; + return filestat.st_mtime; } -#endif - warnif("size", filename, rc, suppress_error); return 0; } // static -unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error) +S64 LLFile::size(const std::string& filename, int suppress_warning) { -#if LL_WINDOWS - // _wstat64() is a bit heavyweight on Windows, use a more lightweight API - // to just get the attributes - std::wstring utf16filename = utf8path_to_wstring(filename); - unsigned short st_mode; - int rc = get_fileattr(utf16filename, st_mode, dontFollowSymLink); - if (rc == 0) + std::error_code ec; + std::filesystem::path file_path = utf8StringToPath(filename); + std::intmax_t size = (std::intmax_t)std::filesystem::file_size(file_path, ec); + return warnif("size", filename, ec, suppress_warning) ? -1 : size; +} + +// static +std::filesystem::file_status LLFile::getStatus(const std::string& filename, bool dontFollowSymLink, int suppress_warning) +{ + std::error_code ec; + std::filesystem::path file_path = utf8StringToPath(filename); + std::filesystem::file_status status; + if (dontFollowSymLink) { - return st_mode; + status = std::filesystem::status(file_path, ec); } -#else - llstat filestatus; - int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus); - if (rc == 0) + else { - return filestatus.st_mode; + status = std::filesystem::symlink_status(file_path, ec); } -#endif - warnif("getattr", filename, rc, suppress_error); - return 0; + warnif("getattr", filename, ec, suppress_warning); + return status; +} + +// static +bool LLFile::exists(const std::string& filename) +{ + std::filesystem::file_status status = getStatus(filename); + return std::filesystem::exists(status); } // static bool LLFile::isdir(const std::string& filename) { - return S_ISDIR(getattr(filename)); + std::filesystem::file_status status = getStatus(filename); + return std::filesystem::is_directory(status); } // static bool LLFile::isfile(const std::string& filename) { - return S_ISREG(getattr(filename)); + std::filesystem::file_status status = getStatus(filename); + return std::filesystem::is_regular_file(status); } // static bool LLFile::islink(const std::string& filename) { - return S_ISLNK(getattr(filename, true)); + std::filesystem::file_status status = getStatus(filename, true); + return std::filesystem::is_symlink(status); } // static -const char *LLFile::tmpdir() +const std::string& LLFile::tmpdir() { - static std::string utf8path; - - if (utf8path.empty()) + static std::string temppath; + if (temppath.empty()) { - char sep; -#if LL_WINDOWS - sep = '\\'; + temppath = std::filesystem::temp_directory_path().string(); + } + return temppath; +} - std::vector utf16path(MAX_PATH + 1); - GetTempPathW(static_cast(utf16path.size()), &utf16path[0]); - utf8path = ll_convert_wide_to_string(&utf16path[0]); +// static +std::filesystem::path LLFile::utf8StringToPath(const std::string& pathname) +{ +#if LL_WINDOWS + return ll_convert(pathname); #else - sep = '/'; - - utf8path = LLStringUtil::getenv("TMPDIR", "/tmp/"); + return pathname; #endif - if (utf8path[utf8path.size() - 1] != sep) - { - utf8path += sep; - } - } - return utf8path.c_str(); } #if LL_WINDOWS +// static +std::wstring LLFile::utf8StringToWstring(const std::string& pathname) +{ + std::wstring utf16string(ll_convert(pathname)); + if (utf16string.size() >= MAX_PATH) + { + // By going through std::filesystem::path we get a lot of path sanitation done for us that + // is needed when passing a path with a kernel object space prefix to Windows API functions + // since this prefix disables the kernel32 path normalization + std::filesystem::path utf16path(utf16string); + + // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names + utf16string.assign(L"\\\\?\\").append(utf16path); + + /* remove trailing spaces and dots (yes, Windows really does that) */ + return utf16string.substr(0, utf16string.find_last_not_of(L" \t.")); + } + return utf16string; +} + /************** input file stream ********************************/ llifstream::llifstream() {} @@ -735,11 +1180,11 @@ std::streamsize llifstream_size(llifstream& ifstr) { if(!ifstr.is_open()) return 0; std::streampos pos_old = ifstr.tellg(); - ifstr.seekg(0, ios_base::beg); + ifstr.seekg(0, std::ios_base::beg); std::streampos pos_beg = ifstr.tellg(); - ifstr.seekg(0, ios_base::end); + ifstr.seekg(0, std::ios_base::end); std::streampos pos_end = ifstr.tellg(); - ifstr.seekg(pos_old, ios_base::beg); + ifstr.seekg(pos_old, std::ios_base::beg); return pos_end - pos_beg; } @@ -747,11 +1192,11 @@ std::streamsize llofstream_size(llofstream& ofstr) { if(!ofstr.is_open()) return 0; std::streampos pos_old = ofstr.tellp(); - ofstr.seekp(0, ios_base::beg); + ofstr.seekp(0, std::ios_base::beg); std::streampos pos_beg = ofstr.tellp(); - ofstr.seekp(0, ios_base::end); + ofstr.seekp(0, std::ios_base::end); std::streampos pos_end = ofstr.tellp(); - ofstr.seekp(pos_old, ios_base::beg); + ofstr.seekp(pos_old, std::ios_base::beg); return pos_end - pos_beg; } diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h old mode 100644 new mode 100755 index a856f34be6..2bfc4d1ffb --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -35,57 +35,218 @@ * Attempts to mostly mirror the POSIX style IO functions. */ -typedef FILE LLFILE; - #include +#include #include #if LL_WINDOWS +#include // The Windows version of stat function and stat data structure are called _stat64 // We use _stat64 here to support 64-bit st_size and time_t values -typedef struct _stat64 llstat; +typedef struct _stat64 llstat; #else #include typedef struct stat llstat; #endif -#ifndef S_ISREG -# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) -#endif - -#ifndef S_ISDIR -# define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) -#endif - -// Windows C runtime library does not define this and does not support symlink detection in the -// stat functions but we do in our getattr() function -#ifndef S_IFLNK -#define S_IFLNK 0xA000 /* symlink */ -#endif - -#ifndef S_ISLNK -#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK) -#endif +typedef FILE LLFILE; #include "llstring.h" // safe char* -> std::string conversion -/// LLFile is a class of static functions operating on paths /// All the functions with a path string input take UTF8 path/filenames class LL_COMMON_API LLFile { public: -//---------------------------------------------------------------------------------------- -// Static member functions -//---------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------- + // constants + //-------------------------------------------------------------------------------------- + // These can be passed to the omode parameter of LLFile::open() and its constructor + /* + This is similar to the openmode flags for std:fstream but not exactly the same + std::fstream open() does not allow to open a file for writing without either + forcing the file to be truncated on open or all write operations being always + appended to the end of the file. + But to allow implementing the LLAPRFile::writeEx() functionality we need to be + able to write at a random position to the file without truncating it on open. + + any other combinations than listed here are not allowed and will cause an error + + bin in out trunc app norep File exists File does not exist + ------------------------------------------------------------------------------- + - + - - - - Open at begin Failure to open + + + - - - - " " + - - + - - - " Create new + + - + - - - " " + - + + - - - " " + + + + - - - " " + ------------------------------------------------------------------------------- + - - + + - - Destroy contents Create new + + - + + - - " " + - + + + - - " " + + + + + - - " " + ------------------------------------------------------------------------------- + - - + x - + Failure to open Create new + + - + x - + " " + - + + x - + " " + + + + x - + " " + ------------------------------------------------------------------------------- + - - + - + - Write to end Create new + + - + - + - " " + - + + - + - " " + + + + - + - " " + ------------------------------------------------------------------------------- + */ + static const std::ios_base::openmode app = 1 << 1; // append to end + static const std::ios_base::openmode ate = 1 << 2; // initialize to end + static const std::ios_base::openmode binary = 1 << 3; // binary mode + static const std::ios_base::openmode in = 1 << 4; // for reading + static const std::ios_base::openmode out = 1 << 5; // for writing + static const std::ios_base::openmode trunc = 1 << 6; // truncate on open + static const std::ios_base::openmode noreplace = 1 << 7; // no replace if it exists + + // Additional optional omode flags to open() and lmode to fopen() or mode flags to lock() + // to indicate which sort of lock if any to attempt to get + static const std::ios_base::openmode exclusive = 1 << 16; + static const std::ios_base::openmode shared = 1 << 17; + // When used either in omode for LLFile::open() or in lmode for LLFile::fopen() there is a + // difference between platforms. + // On Windows this lock is mandatory as it is part of the API to open a file and other processes + // can not avoid it. If a file was opened denying other processes read and/or write access, + // trying to open the same file with that access will fail. + // On Mac and Linux it is only an advisory lock. This means that the other application + // needs to also attempt to at least acquire a shared lock on the file in order to notice that + // the file is actually already locked. It can therefore not be used to prevent random other + // applications from accessing the file, but it works for other viewer processes when they use + // either the LLFile::open() or LLFile::fopen() functions with the appropriate lock flags to + // open a file. + + // Additional mode flag to indicate to rather fail instead of blocking when trying + // to acquire a lock with LLFile::lock() + static const std::ios_base::openmode noblock = 1 << 18; + + static const std::ios_base::openmode lock_mask = exclusive | shared; + + // These can be passed to the dir parameter of LLFile::seek() + static const std::ios_base::seekdir beg = std::ios_base::beg; + static const std::ios_base::seekdir cur = std::ios_base::cur; + static const std::ios_base::seekdir end = std::ios_base::end; + + //-------------------------------------------------------------------------------------- + // constructor/deconstructor + //-------------------------------------------------------------------------------------- + LLFile() : mHandle(InvalidHandle) {} + + // no copy + LLFile(const LLFile&) = delete; + + // move construction + LLFile(LLFile&& other) noexcept + { + mHandle = other.mHandle; + other.mHandle = InvalidHandle; + } + + // open a file + LLFile(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666) : + mHandle(InvalidHandle) + { + open(filename, omode, ec, perm); + } + + // Make it a RAII class + ~LLFile() { close(); } + + //-------------------------------------------------------------------------------------- + // operators + //-------------------------------------------------------------------------------------- + // copy assignment deleted + LLFile& operator=(const LLFile&) = delete; + + // move assignment + LLFile& operator=(LLFile&& other) noexcept + { + close(); + std::swap(mHandle, other.mHandle); + return *this; + } + + // detect whether the wrapped file descriptor/handle is open or not + explicit operator bool() const { return (mHandle != InvalidHandle); } + bool operator!() { return (mHandle == InvalidHandle); } + + //-------------------------------------------------------------------------------------- + // class member methods + //-------------------------------------------------------------------------------------- + + /// All of these functions take as one of their parameters a std::error_code object which can be used to + /// determine in more detail what error occurred if required + + /// Open a file with the specific open mode flags + int open(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666); + ///< @returns 0 on success, -1 on failure + + /// Determine the size of the opened file + S64 size(std::error_code& ec); + ///< @returns the number of bytes in the file or -1 on failure + + /// Return the current file pointer into the file + S64 tell(std::error_code& ec); + ///< @returns the absolute offset of the file pointer in bytes relative to the start of the file or -1 on failure + + /// Move the file pointer to the specified absolute position relative to the start of the file + int seek(S64 pos, std::error_code& ec); + ///< @returns 0 on success, -1 on failure + + /// Move the file pointer to the specified position relative to dir + int seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec); + ///< @returns 0 on success, -1 on failure + + /// Read a number of bytes into the buffer starting at the current file pointer + S64 read(void* buffer, S64 nbytes, std::error_code& ec); + ///< If the file ends before the requested amount of bytes could be read, the function succeeds and + /// returns the bytes up to the end of the file. The return value indicates the number of actually + /// read bytes and can be therefore smaller than the requested amount. + /// @returns the number of bytes read from the file or -1 on failure + + /// Write a number of bytes to the file starting at the current file pointer + S64 write(const void* buffer, S64 nbytes, std::error_code& ec); + /// @returns the number of bytes written to the file or -1 on failure + + /// Write into the file starting at the current file pointer using printf style format + S64 printf(const char* fmt, ...); + /// @returns the number of bytes written to the file or -1 on failure + + /// Attempt to acquire or release a lock on the file + int lock(int mode, std::error_code& ec); + ///< mode can be one of LLFile::exclusive or LLFile::shared to acquire the according lock + /// or 0 to give up an earlier acquired lock. Adding LLFile::noblock together with one of + /// the lock requests will cause the function to fail if the lock can not be acquired, + /// otherwise the function will block until the lock can be acquired. + /// @returns 0 on success, -1 on failure + + /// close the file explicitely + int close(std::error_code& ec); + ///< @returns 0 on success, -1 on failure + + /// Convinience function to close the file without parameters + int close(); + ///< @returns 0 on success, -1 on failure + + //---------------------------------------------------------------------------------------- + // static member functions + // + // All filename parameters are UTF8 file paths + // + //---------------------------------------------------------------------------------------- /// open a file with the specified access mode - static LLFILE* fopen(const std::string& filename, const char* accessmode); + static LLFILE* fopen(const std::string& filename, const char* accessmode, int lmode = 0); ///< 'accessmode' follows the rules of the Posix fopen() mode parameter /// "r" open the file for reading only and positions the stream at the beginning /// "r+" open the file for reading and writing and positions the stream at the beginning /// "w" open the file for reading and writing and truncate it to zero length /// "w+" open or create the file for reading and writing and truncate to zero length if it existed - /// "a" open the file for reading and writing and position the stream at the end of the file - /// "a+" open or create the file for reading and writing and position the stream at the end of the file + /// "a" open the file for reading and writing and and before every write position the stream at the end of the file + /// "a+" open or create the file for reading and writing and before every write position the stream at the end of the file /// /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not @@ -93,35 +254,37 @@ public: /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT. /// This means that it is always a good idea to append "b" specifically for binary file access to /// avoid corruption of the binary consistency of the data stream when reading or writing - /// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter + /// Other characters in 'accessmode', while possible on some platforms (Windows), will usually + /// cause an error as fopen will verify this parameter + /// + /// lmode is optional and allows to lock the file for other processes either as a shared lock or an + /// exclusive lock. If the requested lock conflicts with an already existing lock, the open fails. + /// Pass either LLFIle::exclusive or LLFile::shared to this parameter if you want to prevent other + /// processes from reading (exclusive lock) or writing (shared lock) to the file. It will always use + /// LLFile::noblock, meaning the open will immediately fail if it conflicts with an existing lock on the + /// file. + /// /// @returns a valid LLFILE* pointer on success that can be passed to the fread() and fwrite() functions /// and some other f functions in the Standard C library that accept a FILE* as parameter /// or NULL on failure /// Close a file handle opened with fopen() above static int close(LLFILE * file); - - /// create a directory - static int mkdir(const std::string& filename, int perms = 0700); - ///< perms is a permissions mask like 0777 or 0700. In most cases it will be - /// overridden by the user's umask. It is ignored on Windows. - /// mkdir() considers "directory already exists" to be not an error. /// @returns 0 on success and -1 on failure. - //// remove a directory - static int rmdir(const std::string& filename, int suppress_error = 0); - ///< pass ENOENT in the optional 'suppress_error' parameter - /// if you don't want a warning in the log when the directory does not exist + /// create a directory + static int mkdir(const std::string& filename); + ///< mkdir() considers "directory already exists" to be not an error. /// @returns 0 on success and -1 on failure. /// remove a file or directory - static int remove(const std::string& filename, int suppress_error = 0); - ///< pass ENOENT in the optional 'suppress_error' parameter - /// if you don't want a warning in the log when the directory does not exist + static int remove(const std::string& filename, int suppress_warning = 0); + ///< pass ENOENT in the optional 'suppress_warning' parameter if you don't want + /// a warning in the log when the directory does not exist /// @returns 0 on success and -1 on failure. /// rename a file - static int rename(const std::string& filename, const std::string& newname, int suppress_error = 0); + static int rename(const std::string& filename, const std::string& newname, int suppress_warning = 0); ///< it will silently overwrite newname if it exists without returning an error /// Posix guarantees that if newname already exists, then there will be no moment /// in which for other processes newname does not exist. There is no such guarantee @@ -135,116 +298,103 @@ public: /// retrieve the content of a file into a string static std::string getContents(const std::string& filename); + static std::string getContents(const std::string& filename, std::error_code& ec); ///< @returns the content of the file or an empty string on failure /// read nBytes from the file into the buffer, starting at offset in the file - static U64 read(const std::string& filename, void* buf, U64 offset, U64 nbytes); - ///< @returns bytes read on success, 0 on failure + static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes); + static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec); + ///< @returns bytes read on success, or -1 on failure /// write nBytes from the buffer into the file, starting at offset in the file - static U64 write(const std::string& filename, const void* buf, U64 offset, U64 nbytes); - ///< @returns bytes written on success, 0 on failure + static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes); + static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec); + ///< A negative offset will append the data to the end of the file + /// @returns bytes written on success, or -1 on failure /// return the file stat structure for filename - static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT); + static int stat(const std::string& filename, llstat* file_status, const char *fname = nullptr, int suppress_warning = ENOENT); ///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the - /// optional 'suppress_error' parameter to avoid spamming the log with warnings when the API + /// optional 'suppress_warning' parameter to avoid spamming the log with warnings when the API /// is used to detect if a file exists /// @returns 0 on success and -1 on failure. + /// get the creation data and time of a file + static std::time_t getCreationTime(const std::string& filename, int suppress_warning = 0); + ///< @returns the creation time of the file or 0 on error + + /// get the last modification data and time of a file + static std::time_t getModificationTime(const std::string& filename, int suppress_warning = 0); + ///< @returns the modification time of the file or 0 on error + /// get the file or directory attributes for filename - static unsigned short getattr(const std::string& filename, bool dontFollowSymLink = false, int suppress_error = ENOENT); - ///< a more lightweight function on Windows to stat, that just returns the file attribute flags - /// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it - /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with + static std::filesystem::file_status getStatus(const std::string& filename, bool dontFollowSymLink = false, int suppress_warning = ENOENT); + ///< dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it + /// we pass by default ENOENT in the optional 'suppress_warning' parameter to not spam the log with /// warnings when the file or directory does not exist - /// @returns 0 on failure and a st_mode value with either S_IFDIR, S_IFREG or S_IFLNK set, - /// together with the three access bits which under Windows only the write bit is relevant. + /// @returns a std::filesystem::file_status value that can be passed to the according std::filesystem::exists() + /// and other APIs accepting a file_status. /// get the size of a file in bytes - static S64 size(const std::string& filename, int suppress_error = ENOENT); - ///> returns the size of a file in bytes - /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with - /// warnings when the file does not exist - /// @returns the file size on success and 0 on failure. + static S64 size(const std::string& filename, int suppress_warning = ENOENT); + ///< we pass by default ENOENT in the optional 'suppress_warning' parameter to not spam + /// the log with warnings when the file does not exist + /// @returns the file size on success or -1 on failure. + + /// check if filename is an existing file or directory + static bool exists(const std::string& filename); + ///< @returns true if the path is for an existing file or directory /// check if filename is an existing directory - static bool isdir(const std::string& filename); + static bool isdir(const std::string& filename); ///< @returns true if the path is for an existing directory /// check if filename is an existing file - static bool isfile(const std::string& filename); + static bool isfile(const std::string& filename); ///< @returns true if the path is for an existing file /// check if filename is a symlink - static bool islink(const std::string& filename); + static bool islink(const std::string& filename); ///< @returns true if the path is pointing at a symlink /// return a path to the temporary directory on the system - static const char * tmpdir(); -}; + static const std::string& tmpdir(); -/// RAII class -class LLUniqueFile -{ -public: - // empty - LLUniqueFile(): mFileHandle(nullptr) {} - // wrap (e.g.) result of LLFile::fopen() - LLUniqueFile(LLFILE* f): mFileHandle(f) {} - // no copy - LLUniqueFile(const LLUniqueFile&) = delete; - // move construction - LLUniqueFile(LLUniqueFile&& other) noexcept - { - mFileHandle = other.mFileHandle; - other.mFileHandle = nullptr; - } - // The point of LLUniqueFile is to close on destruction. - ~LLUniqueFile() - { - close(); - } - - // simple assignment - LLUniqueFile& operator=(LLFILE* f) - { - close(); - mFileHandle = f; - return *this; - } - // copy assignment deleted - LLUniqueFile& operator=(const LLUniqueFile&) = delete; - // move assignment - LLUniqueFile& operator=(LLUniqueFile&& other) noexcept - { - close(); - std::swap(mFileHandle, other.mFileHandle); - return *this; - } - - // explicit close operation - void close() - { - if (mFileHandle) - { - // in case close() throws, set mFileHandle null FIRST - LLFILE* h{nullptr}; - std::swap(h, mFileHandle); - LLFile::close(h); - } - } - - // detect whether the wrapped LLFILE is open or not - explicit operator bool() const { return bool(mFileHandle); } - bool operator!() { return ! mFileHandle; } - - // LLUniqueFile should be usable for any operation that accepts LLFILE* - // (or FILE* for that matter) - operator LLFILE*() const { return mFileHandle; } + /// converts a string containing a path in utf8 encoding into an explicit filesystem path + static std::filesystem::path utf8StringToPath(const std::string& pathname); + ///< @returns the path as a std::filesystem::path private: - LLFILE* mFileHandle; +#if LL_WINDOWS + typedef HANDLE llfile_handle_t; + const llfile_handle_t InvalidHandle = INVALID_HANDLE_VALUE; +#else + typedef int llfile_handle_t; + const llfile_handle_t InvalidHandle = -1; +#endif + + //---------------------------------------------------------------------------------------- + // static member functions + //---------------------------------------------------------------------------------------- +#if LL_WINDOWS + /// convert a string containing a path in utf8 encoding into a Windows format std::wstring + static std::wstring utf8StringToWstring(const std::string& pathname); + ///< this will prepend the path with the Windows kernel object space prefix when the path is + /// equal or longer than MAX_PATH characters and do some sanitation on the path. + /// This allows the underlaying Windows APIs to process long path names. Do not pass such a path + /// to std::filesystem functions. These functions are not guaranteed to handle such paths properly. + /// It's only useful to pass the resulting string buffer to Microsoft Windows widechar APIs or + /// the Microsoft C runtime widechar file functions. + /// + /// Example: + /// + /// std::wstring file_path = utf8StringToWstring(filename); + /// HANDLE CreateFileW(file_path.c_str(), ......); + /// + /// @returns the path as a std::wstring path +#endif + llfile_handle_t mHandle; + std::ios_base::openmode mOpen; // Used to emulate std::ios_base::app under Windows }; #if LL_WINDOWS diff --git a/indra/llcrashlogger/llcrashlock.cpp b/indra/llcrashlogger/llcrashlock.cpp old mode 100644 new mode 100755 index bc34f6798f..731b53b7e9 --- a/indra/llcrashlogger/llcrashlock.cpp +++ b/indra/llcrashlogger/llcrashlock.cpp @@ -188,12 +188,7 @@ LLSD LLCrashLock::getProcessList() //static bool LLCrashLock::fileExists(std::string filename) { -#ifdef LL_WINDOWS // or BOOST_WINDOWS_API - boost::filesystem::path file_path(ll_convert(filename)); -#else - boost::filesystem::path file_path(filename); -#endif - return boost::filesystem::exists(file_path); + return LLFile::exists(filename); } void LLCrashLock::cleanupProcess(std::string proc_dir) diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp old mode 100644 new mode 100755 index 190539cea5..4c51cc12ab --- a/indra/llfilesystem/lldir.cpp +++ b/indra/llfilesystem/lldir.cpp @@ -94,29 +94,19 @@ LLDir::~LLDir() std::vector LLDir::getFilesInDir(const std::string &dirname) { //Returns a vector of fullpath filenames. - -#ifdef LL_WINDOWS // or BOOST_WINDOWS_API - boost::filesystem::path p(ll_convert(dirname)); -#else - boost::filesystem::path p(dirname); -#endif - + std::filesystem::path p = LLFile::utf8StringToPath(dirname); std::vector v; - - boost::system::error_code ec; - if (exists(p, ec) && !ec.failed()) + std::error_code ec; + if (std::filesystem::is_directory(p, ec) && !ec) { - if (is_directory(p, ec) && !ec.failed()) + std::filesystem::directory_iterator end_iter; + for (std::filesystem::directory_iterator dir_itr(p); + dir_itr != end_iter; + ++dir_itr) { - boost::filesystem::directory_iterator end_iter; - for (boost::filesystem::directory_iterator dir_itr(p); - dir_itr != end_iter; - ++dir_itr) + if (std::filesystem::is_regular_file(dir_itr->status())) { - if (boost::filesystem::is_regular_file(dir_itr->status())) - { - v.push_back(dir_itr->path().filename().string()); - } + v.push_back(dir_itr->path().string()); } } } @@ -186,28 +176,23 @@ U32 LLDir::deleteDirAndContents(const std::string& dir_name) //Removes the directory and its contents. Returns number of files deleted. U32 num_deleted = 0; + std::filesystem::path dir_path = LLFile::utf8StringToPath(dir_name); try { -#ifdef LL_WINDOWS // or BOOST_WINDOWS_API - boost::filesystem::path dir_path(ll_convert(dir_name)); -#else - boost::filesystem::path dir_path(dir_name); -#endif - - if (boost::filesystem::exists(dir_path)) + if (std::filesystem::is_directory(dir_path)) { - if (!boost::filesystem::is_empty(dir_path)) + if (!std::filesystem::is_empty(dir_path)) { // Directory has content - num_deleted = (U32)boost::filesystem::remove_all(dir_path); + num_deleted = (U32)std::filesystem::remove_all(dir_path); } else { // Directory is empty - boost::filesystem::remove(dir_path); + std::filesystem::remove(dir_path); } } } - catch (boost::filesystem::filesystem_error &er) + catch (std::filesystem::filesystem_error &er) { LL_WARNS() << "Failed to delete " << dir_name << " with error " << er.code().message() << LL_ENDL; } @@ -1105,15 +1090,15 @@ void dir_exists_or_crash(const std::string &dir_name) #if LL_WINDOWS // *FIX: lame - it doesn't do the same thing on windows. not so // important since we don't deploy simulator to windows boxes. - LLFile::mkdir(dir_name, 0700); + LLFile::mkdir(dir_name); #else - struct stat dir_stat; + llstat dir_stat; if(0 != LLFile::stat(dir_name, &dir_stat)) { S32 stat_rv = errno; if(ENOENT == stat_rv) { - if(0 != LLFile::mkdir(dir_name, 0700)) // octal + if(0 != LLFile::mkdir(dir_name)) { LL_ERRS() << "Unable to create directory: " << dir_name << LL_ENDL; } diff --git a/indra/llfilesystem/lldir_win32.cpp b/indra/llfilesystem/lldir_win32.cpp old mode 100644 new mode 100755 index 58c080c982..2b478e5dce --- a/indra/llfilesystem/lldir_win32.cpp +++ b/indra/llfilesystem/lldir_win32.cpp @@ -376,18 +376,7 @@ std::string LLDir_Win32::getCurPath() bool LLDir_Win32::fileExists(const std::string &filename) const { - llstat stat_data; - // Check the age of the file - // Now, we see if the files we've gathered are recent... - int res = LLFile::stat(filename, &stat_data); - if (!res) - { - return true; - } - else - { - return false; - } + return LLFile::exists(filename); } diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp old mode 100644 new mode 100755 index 541266af4f..4238dd64e9 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -77,13 +77,8 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil LL_PROFILE_ZONE_SCOPED; const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type); - llifstream file(filename, std::ios::binary); - if (file.is_open()) - { - file.seekg(0, std::ios::end); - return file.tellg() > 0; - } - return false; + // not only test for existence but for the file to be not empty + return LLFile::size(filename) > 0; } // static @@ -120,15 +115,7 @@ S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType fi { const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type); - S32 file_size = 0; - llifstream file(filename, std::ios::binary); - if (file.is_open()) - { - file.seekg(0, std::ios::end); - file_size = (S32)file.tellg(); - } - - return file_size; + return (S32)LLFile::size(filename); } bool LLFileSystem::read(U8* buffer, S32 bytes) diff --git a/indra/llfilesystem/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp old mode 100644 new mode 100755 index 13db6fad80..17e4ffecf1 --- a/indra/llfilesystem/tests/lldir_test.cpp +++ b/indra/llfilesystem/tests/lldir_test.cpp @@ -558,8 +558,8 @@ namespace tut LLFile::remove(dir1files[i]); LLFile::remove(dir2files[i]); } - LLFile::rmdir(dir1); - LLFile::rmdir(dir2); + LLFile::remove(dir1); + LLFile::remove(dir2); } template<> template<> diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp old mode 100644 new mode 100755 index 2c35a6acae..49df9cd88c --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1059,7 +1059,7 @@ void LLShaderMgr::clearShaderCache() LL_INFOS("ShaderMgr") << "Removing shader cache at " << shader_cache << LL_ENDL; const std::string mask = "*"; gDirUtilp->deleteFilesInDir(shader_cache, mask); - LLFile::rmdir(shader_cache); + LLFile::remove(shader_cache); mShaderBinaryCache.clear(); } @@ -1131,11 +1131,11 @@ bool LLShaderMgr::loadCachedProgramBinary(LLGLSLShader* shader) { std::vector in_data; in_data.resize(shader_info.mBinaryLength); - - LLUniqueFile filep = LLFile::fopen(in_path, "rb"); + std::error_code ec; + LLFile filep = LLFile(in_path, LLFile::in | LLFile::binary, ec); if (filep) { - size_t result = fread(in_data.data(), sizeof(U8), in_data.size(), filep); + size_t result = filep.read(in_data.data(), in_data.size(), ec); filep.close(); if (result == in_data.size()) @@ -1180,11 +1180,12 @@ bool LLShaderMgr::saveCachedProgramBinary(LLGLSLShader* shader) if (error == GL_NO_ERROR) { std::string out_path = gDirUtilp->add(mShaderCacheDir, shader->mShaderHash.asString() + ".shaderbin"); - LLUniqueFile outfile = LLFile::fopen(out_path, "wb"); - if (outfile) + std::error_code ec; + LLFile filep = LLFile(out_path, LLFile::out | LLFile::binary, ec); + if (filep) { - fwrite(program_binary.data(), sizeof(U8), program_binary.size(), outfile); - outfile.close(); + filep.write(program_binary.data(), program_binary.size(), ec); + filep.close(); binary_info.mLastUsedTime = (F32)LLTimer::getTotalSeconds(); diff --git a/indra/llxml/tests/llcontrol_test.cpp b/indra/llxml/tests/llcontrol_test.cpp old mode 100644 new mode 100755 index 4192e029c5..e15117fc29 --- a/indra/llxml/tests/llcontrol_test.cpp +++ b/indra/llxml/tests/llcontrol_test.cpp @@ -69,7 +69,7 @@ namespace tut LLFile::remove(filename); } LLFile::remove(mTestConfigFile); - LLFile::rmdir(mTestConfigDir); + LLFile::remove(mTestConfigDir); } void writeSettingsFile(const LLSD& config) { diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp old mode 100644 new mode 100755 index f6e3139cc4..962a91bb73 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3031,13 +3031,11 @@ void LLAppViewer::initStrings() } else { - llstat st; - int rc = LLFile::stat(strings_path_full, &st); - if (rc != 0) + if (!LLFile::exists(strings_path_full)) { - crash_reason = "The file '" + strings_path_full + "' failed to get status. Error code: " + std::to_string(rc); + crash_reason = "The file '" + strings_path_full + "' doesn't seem to exist"; } - else if (S_ISDIR(st.st_mode)) + else if (LLFile::isdir(strings_path_full)) { crash_reason = "The filename '" + strings_path_full + "' is a directory name"; } @@ -4293,7 +4291,7 @@ void LLAppViewer::migrateCacheDirectory() LLFile::remove(ds_store); } #endif - if (LLFile::rmdir(old_cache_dir) != 0) + if (LLFile::remove(old_cache_dir) != 0) { LL_WARNS() << "could not delete old cache directory " << old_cache_dir << LL_ENDL; } diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp old mode 100644 new mode 100755 index c5c1e01538..66066a45b2 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -1500,7 +1500,7 @@ bool LLFloaterPreference::moveTranscriptsAndLog() //Couldn't move the log and created a new directory so remove the new directory if(madeDirectory) { - LLFile::rmdir(chatLogPath); + LLFile::remove(chatLogPath); } return false; } @@ -1526,7 +1526,7 @@ bool LLFloaterPreference::moveTranscriptsAndLog() if(madeDirectory) { - LLFile::rmdir(chatLogPath); + LLFile::remove(chatLogPath); } return false; @@ -2031,17 +2031,15 @@ void LLFloaterPreference::changed() { if (LLConversationLog::instance().getIsLoggingEnabled()) { - getChild("clear_log")->setEnabled(LLConversationLog::instance().getConversations().size() > 0); + getChild("clear_log")->setEnabled(LLConversationLog::instance().getConversations().size() > 0); } else { // onClearLog clears list, then notifies changed() and only then clears file, // so check presence of conversations before checking file, file will cleared later. - llstat st; bool has_logs = LLConversationLog::instance().getConversations().size() > 0 - && LLFile::stat(LLConversationLog::instance().getFileName(), &st) == 0 - && S_ISREG(st.st_mode) - && st.st_size > 0; + && LLFile::isfile(LLConversationLog::instance().getFileName()) + && LLFile::size(LLConversationLog::instance().getFileName()) > 0; getChild("clear_log")->setEnabled(has_logs); } diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp old mode 100644 new mode 100755 index c3bc24c6b9..db98a70573 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -892,8 +892,7 @@ void LLFloaterUIPreview::displayFloater(bool click, S32 ID) // Add localization to title so user knows whether it's localized or defaulted to en std::string full_path = getLocalizedDirectory() + path; std::string floater_lang = "EN"; - llstat dummy; - if(!LLFile::stat(full_path.c_str(), &dummy)) // if the file does not exist + if (LLFile::isfile(full_path.c_str())) // if the file does not exist { floater_lang = getLocStr(ID); } @@ -966,9 +965,8 @@ void LLFloaterUIPreview::onClickEditFloater() } file_path = getLocalizedDirectory() + file_name; - // stat file to see if it exists (some localized versions may not have it there are no diffs, and then we try to open an nonexistent file) - llstat dummy; - if(LLFile::stat(file_path.c_str(), &dummy)) // if the file does not exist + // does it exist? (some localized versions may not have it there are no diffs, and then we try to open an nonexistent file + if(!LLFile::isfile(file_path.c_str())) // if the file does not exist { popupAndPrintWarning("No file for this floater exists in the selected localization. Opening the EN version instead."); file_path = get_xui_dir() + mDelim + "en" + mDelim + file_name; // open the en version instead, by default @@ -1124,8 +1122,7 @@ void LLFloaterUIPreview::onClickToggleDiffHighlighting() error = true; } - llstat dummy; - if(LLFile::stat(path_in_textfield.c_str(), &dummy) && !error) // check if the file exists (empty check is reduntant but useful for the informative error message) + if (!LLFile::isfile(path_in_textfield.c_str()) && !error) // check if the file exists (empty check is reduntant but useful for the informative error message) { std::string warning = std::string("Unable to highlight differences because an invalid path to a difference file was provided:\"") + path_in_textfield + "\""; popupAndPrintWarning(warning); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp old mode 100644 new mode 100755 index 9a991727b2..4fe661b055 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -575,8 +575,7 @@ void LLPreviewNotecard::syncExternal() { // Sync with external editor. std::string tmp_file = getTmpFileName(); - llstat s; - if (LLFile::stat(tmp_file, &s) == 0) // file exists + if (LLFile::isfile(tmp_file)) // file exists { if (mLiveFile) mLiveFile->ignoreNextUpdate(); writeToFile(tmp_file); diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp old mode 100644 new mode 100755 index c2aa4925bd..2c436198e3 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -694,8 +694,7 @@ void LLScriptEdCore::sync() if (mLiveFile) { std::string tmp_file = mLiveFile->filename(); - llstat s; - if (LLFile::stat(tmp_file, &s) == 0) // file exists + if (LLFile::isfile(tmp_file)) // file exists { mLiveFile->ignoreNextUpdate(); writeToFile(tmp_file); diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp old mode 100644 new mode 100755 index e59cf70177..ef7542fe2e --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -1614,7 +1614,7 @@ void LLTextureCache::purgeAllTextures(bool purge_directories) gDirUtilp->deleteFilesInDir(mTexturesDirName, mask); // headers, fast cache if (purge_directories) { - LLFile::rmdir(mTexturesDirName); + LLFile::remove(mTexturesDirName); } } mHeaderIDMap.clear(); diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp old mode 100644 new mode 100755 index 35ac7919ac..7534d778a5 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -1140,15 +1140,12 @@ std::string getProfileStatsFilename() // same second), may produce (e.g.) sec==61, but avoids collisions and // preserves chronological filename sort order. std::string name; - std::error_code ec; do { // base + missing 2-digit seconds, append ".json" // post-increment sec in case we have to try again name = stringize(base, std::setw(2), std::setfill('0'), sec++, ".json"); - } while (std::filesystem::exists(fsyspath(name), ec)); - // Ignoring ec means we might potentially return a name that does already - // exist -- but if we can't check its existence, what more can we do? + } while (LLFile::exists(fsyspath(name))); return name; } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp old mode 100644 new mode 100755 index a77b9f6103..5cb05460bc --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1827,12 +1827,11 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ user_data_path_cache += gDirUtilp->getDirDelimiter(); // See if the plugin executable exists - llstat s; - if(LLFile::stat(launcher_name, &s)) + if (!LLFile::isfile(launcher_name)) { LL_WARNS_ONCE("Media") << "Couldn't find launcher at " << launcher_name << LL_ENDL; } - else if(LLFile::stat(plugin_name, &s)) + else if (!LLFile::isfile(plugin_name)) { #if !LL_LINUX LL_WARNS_ONCE("Media") << "Couldn't find plugin at " << plugin_name << LL_ENDL; diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp old mode 100644 new mode 100755 index 9537ad745b..cdc41baa88 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -1250,7 +1250,7 @@ void LLVOCache::removeCache(ELLPath location, bool started) std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname); LL_INFOS() << "Removing cache at " << cache_dir << LL_ENDL; gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files - LLFile::rmdir(cache_dir); + LLFile::remove(cache_dir); clearCacheInMemory(); mInitialized = false; diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp old mode 100644 new mode 100755 index d132cbfa36..e58a6577f1 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -943,8 +943,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() gDirUtilp->append(exe_path, "SLVoice"); #endif // See if the vivox executable exists - llstat s; - if (!LLFile::stat(exe_path, &s)) + if (LLFile::isfile(exe_path)) { // vivox executable exists. Build the command line and launch the daemon. LLProcess::Params params; diff --git a/indra/test/llmessageconfig_tut.cpp b/indra/test/llmessageconfig_tut.cpp old mode 100644 new mode 100755 index 93443467a2..b8942e99b3 --- a/indra/test/llmessageconfig_tut.cpp +++ b/indra/test/llmessageconfig_tut.cpp @@ -62,7 +62,7 @@ namespace tut int rmfile = LLFile::remove((mTestConfigDir + "/message.xml")); ensure_equals("rmfile value", rmfile, 0); // rm temp dir - int rmdir = LLFile::rmdir(mTestConfigDir); + int rmdir = LLFile::remove(mTestConfigDir); ensure_equals("rmdir value", rmdir, 0); } diff --git a/indra/test/message_tut.cpp b/indra/test/message_tut.cpp old mode 100644 new mode 100755 index 11cd710ef6..6fb2b92803 --- a/indra/test/message_tut.cpp +++ b/indra/test/message_tut.cpp @@ -113,7 +113,7 @@ namespace tut ensure_equals("rmfile value", rmfile, 0); // rm temp dir - int rmdir = LLFile::rmdir(mTestConfigDir); + int rmdir = LLFile::remove(mTestConfigDir); ensure_equals("rmdir value", rmdir, 0); } -- cgit v1.3 From 62e08e13d00d62a996a01fff20ad18c1886f3c8a Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Wed, 12 Nov 2025 19:20:50 +0100 Subject: Add LLFile unit tests Initial implementation of unit tests for LLFile functionality --- indra/test/CMakeLists.txt | 13 +-- indra/test/llfile_tut.cpp | 254 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+), 6 deletions(-) mode change 100644 => 100755 indra/test/CMakeLists.txt create mode 100755 indra/test/llfile_tut.cpp (limited to 'indra') diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt old mode 100644 new mode 100755 index 246fc5e6f8..69c9660575 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -16,6 +16,7 @@ set(test_SOURCE_FILES llbuffer_tut.cpp lldoubledispatch_tut.cpp llevents_tut.cpp + llfile_tut.cpp llhttpdate_tut.cpp llhttpnode_tut.cpp lliohttpserver_tut.cpp @@ -67,7 +68,7 @@ target_link_libraries(lltest if (WINDOWS) set_target_properties(lltest - PROPERTIES + PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT" LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\"" RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}" @@ -85,10 +86,10 @@ set(TEST_EXE $) SET_TEST_PATH(LD_LIBRARY_PATH) -LL_TEST_COMMAND(command +LL_TEST_COMMAND(command "${LD_LIBRARY_PATH}" "${TEST_EXE}" - "--output=${CMAKE_CURRENT_BINARY_DIR}/cpp_test_results.txt" + "--output=${CMAKE_CURRENT_BINARY_DIR}/cpp_test_results.txt" "--touch=${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt") ADD_CUSTOM_COMMAND( @@ -101,11 +102,11 @@ ADD_CUSTOM_COMMAND( set(test_results ${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt) -# This should cause the test executable to be built, but not +# This should cause the test executable to be built, but not # run if LL_TESTS is disabled. This will hopefully keep the -# tests up to date with any code changes changes even if +# tests up to date with any code changes changes even if # developers choose to disable LL_TESTS. -if (LL_TESTS) +if (LL_TESTS) add_custom_target(tests_ok ALL DEPENDS ${test_results}) if(DARWIN) # Support our "@executable_path/../Resources" load path for our test diff --git a/indra/test/llfile_tut.cpp b/indra/test/llfile_tut.cpp new file mode 100755 index 0000000000..9cd7fd335f --- /dev/null +++ b/indra/test/llfile_tut.cpp @@ -0,0 +1,254 @@ +/** + * @file llfile_tut.cpp + * @author Frederick Martian + * @date 2025-11 + * @brief LLFile test cases. + * + * $LicenseInfo:firstyear=2025&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 +#include "lltut.h" +#include "linden_common.h" +#include "llfile.h" + +namespace tut +{ + static void clear_entire_dir(std::filesystem::path &dir) + { + std::error_code ec; + std::filesystem::remove_all(dir, ec); + } + + static std::filesystem::path append_filename(const std::filesystem::path& dir, const std::string& element) + { + std::filesystem::path path = dir; + return path.append(element); + } + + static std::filesystem::path get_testdir(const std::filesystem::path& tempdir) + { + return append_filename(tempdir, std::string("test_dir")); + } + + struct llfile_test + { + std::filesystem::path tempdir = LLFile::tmpdir(); + std::filesystem::path testdir = get_testdir(tempdir); + }; + typedef test_group llfile_test_t; + typedef llfile_test_t::object llfile_test_object_t; + tut::llfile_test_t tut_llfile_test("llfile_test"); + + template<> template<> + void llfile_test_object_t::test<1>() + { + // Test creating directories and files and deleting them and checking if the + // relevant status functions work as expected + ensure("LLFile::tmpdir() empty", !tempdir.empty()); + ensure("LLFile::tmpdir() doesn't exist", LLFile::exists(tempdir.string())); + ensure("LLFile::tmpdir() is not a directory", LLFile::isdir(tempdir.string())); + ensure("LLFile::tmpdir() should not be a file", !LLFile::isfile(tempdir.string())); + + // Make sure there is nothing left from a previous test run + clear_entire_dir(testdir); + ensure("llfile_test should not exist anymore", !LLFile::exists(testdir.string())); + + int rc = LLFile::mkdir(testdir.string()); + ensure("LLFile::mkdir() failed", rc == 0); + ensure("llfile_test should be a directory", LLFile::isdir(testdir.string())); + rc = LLFile::mkdir(testdir.string()); + ensure("LLFile::mkdir() should not fail when the directory already exists", rc == 0); + + std::filesystem::path testfile1 = testdir; + testfile1.append("llfile_test.dat"); + ensure("llfile_test1.dat should not yet exist", !LLFile::exists(testfile1.string())); + + const char* testdata = "testdata"; + std::time_t current = time(nullptr); + S64 bytes = LLFile::write(testfile1.string(), testdata, 0, sizeof(testdata)); + ensure("LLFile::write() did not write correctly", bytes == sizeof(testdata)); + + rc = LLFile::remove(testfile1.string()); + ensure("LLFile::remove() for file test_file.dat", rc == 0); + ensure("llfile_test.dat should not exist anymore", !LLFile::exists(testfile1.string())); + ensure("llfile_test.dat should not be a file", !LLFile::isfile(testfile1.string())); + ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1.string())); + ensure("llfile_test.dat should not be a symlink", !LLFile::islink(testfile1.string())); + + rc = LLFile::remove(testdir.string()); + ensure("LLFile::remove() for directory llfile_test failed", rc == 0); + ensure("llfile_test should not exist anymore", !LLFile::exists(testdir.string())); + } + + template<> template<> + void llfile_test_object_t::test<2>() + { + // High level static file IO functions to read and write data files + LLFile::mkdir(testdir.string()); + ensure("llfile_test should exist", LLFile::isdir(testdir.string())); + + std::filesystem::path testfile1 = testdir; + testfile1.append("llfile_test.dat"); + + std::string testdata1("testdata"); + std::string testdata2("datateststuff"); + std::time_t current = time(nullptr); + S64 bytes = LLFile::write(testfile1.string(), testdata1.c_str(), 0, testdata1.length()); + ensure("LLFile::write() did not write correctly", bytes == testdata1.length()); + ensure("llfile_test.dat should exist", LLFile::exists(testfile1.string())); + ensure("llfile_test.dat should be a file", LLFile::isfile(testfile1.string())); + ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1.string())); + + bytes = LLFile::size(testfile1.string()); + ensure("LLFile::size() did not return the correct size", bytes == testdata1.length()); + + std::string data = LLFile::getContents(testfile1.string()); + ensure("LLFile::getContents() did not return the correct size data", data.length() == testdata1.length()); + ensure_memory_matches("LLFile::getContents() did not read correct data", testdata1.c_str(), (U32)testdata1.length(), data.c_str(), (U32)data.length()); + + std::time_t ctime = LLFile::getCreationTime(testfile1.string()); + ensure_approximately_equals_range("LLFile::getCreationTime() did not return correct time", (F32)(ctime - current), 0.f, 1); + + std::time_t mtime = LLFile::getModificationTime(testfile1.string()); + ensure_approximately_equals_range("LLFile::getModificationTime() did not return correct time", (F32)(mtime - current), 0.f, 1); + + char buffer[1024]; + bytes = LLFile::read(testfile1.string(), buffer, 0, testdata1.length()); + ensure("LLFile:read() did not return the correct size", bytes == testdata1.length()); + ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes); + + // What if we try to read more data than there is in the file? + bytes = LLFile::read(testfile1.string(), buffer, 0, bytes + 10); + ensure("LLFile:read() did not correctly stop on eof", bytes == testdata1.length()); + ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes); + + // Let's append more data + bytes = LLFile::write(testfile1.string(), testdata2.c_str(), -1, testdata2.length()); + ensure("LLFile::write() did not write correctly", bytes == testdata2.length()); + + bytes = LLFile::size(testfile1.string()); + ensure("LLFile::size() did not return the correct size", bytes == testdata1.length() + testdata2.length()); + bytes = LLFile::read(testfile1.string(), buffer, 0, bytes); + ensure("LLFile:read() did not read correct number of bytes", bytes == testdata1.length() + testdata2.length()); + ensure_memory_matches("LLFile:read() did not read correct testdata1", testdata1.c_str(), (U32)testdata1.length(), buffer, (U32)testdata1.length()); + ensure_memory_matches("LLFile:read() did not read correct testdata2", testdata2.c_str(), (U32)testdata2.length(), buffer + testdata1.length(), (U32)testdata2.length()); + } + + template<> template<> + void llfile_test_object_t::test<3>() + { + const size_t numints = 1024; + + // Testing the LLFile class implementation + std::filesystem::path testfile = testdir; + testfile.append("llfile_test.bin"); + + int data[numints]; + for (int &t : data) + { + t = rand(); + } + + std::error_code ec; + LLFile fileout(testfile.string(), LLFile::out, ec); + ensure("LLFile constructor did not open correctly", (bool)fileout); + ensure("error_code from LLFile constructor should not indicate an error", !ec); + if (fileout) + { + S64 length = fileout.size(ec); + ensure("freshly created file should be empty", length == 0); + ensure("error_code from LLFile::size() should not indicate an error", !ec); + S64 bytes = fileout.write(data, sizeof(data), ec); + ensure("LLFile::write() did not write correctly", bytes == sizeof(data)); + ensure("error_code from LLFile::write() should not indicate an error", !ec); + bytes = fileout.write(data, sizeof(data), ec); + ensure("LLFile::write() did not write correctly", bytes == sizeof(data)); + ensure("error_code from LLFile::write() should not indicate an error", !ec); + bytes = fileout.size(ec); + ensure("LLFile::size() returned wrong size", bytes == 2 * sizeof(data)); + ensure("error_code from LLFile::size() should not indicate an error", !ec); + fileout.close(); + } + + LLFile filein(testfile.string(), LLFile::in, ec); + ensure("LLFile constructor did not open correctly", (bool)filein); + ensure("error_code from LLFile constructor should not indicate an error", !ec); + if (filein) + { + S64 length = filein.size(ec); + ensure("LLFile::size() returned wrong size", length == 2 * sizeof(data)); + ensure("error_code from LLFile::size() should not indicate an error", !ec); + char* buffer = (char*)malloc(length); + S64 bytes = filein.read(buffer, length, ec); + ensure("LLFile::read() did not read correctly", bytes == length); + ensure("error_code from LLFile::read() should not indicate an error", !ec); + ensure_memory_matches("LLFile:read() did not read correct data1", data, (U32)sizeof(data), buffer, (U32)sizeof(data)); + ensure_memory_matches("LLFile:read() did not read correct data2", data, (U32)sizeof(data), buffer + sizeof(data), (U32)sizeof(data)); + S64 offset = filein.tell(ec); + ensure("LLFile::tell() returned a bad offset", offset == length); + ensure("error_code from LLFile::read() should not indicate an error", !ec); + offset = sizeof(data) / 2; + int rc = filein.seek(offset, ec); + ensure("LLFile::seek() indicated an error", rc == 0); + ensure("error_code from LLFile::seek() should not indicate an error", !ec); + bytes = filein.read(buffer, 2 * sizeof(data), ec); + ensure("LLFile::read() did not read correctly", bytes == sizeof(data) + offset); + ensure("error_code from LLFile::read() should not indicate an error", !ec); + ensure_memory_matches("LLFile:read() did not read correct data3", (char*)data + offset, (U32)offset, buffer, (U32)offset); + ensure_memory_matches("LLFile:read() did not read correct data4", (char*)data, (U32)sizeof(data), buffer + offset, (U32)sizeof(data)); + filein.close(); + + free(buffer); + } + } + + template<> template<> + void llfile_test_object_t::test<4>() + { + // Testing the LLFile class implementation with wrong paths and parameters + std::filesystem::path testfile = testdir; + testfile.append("llfile_test.bin"); + + std::error_code ec; + LLFile file(testfile.string(), LLFile::out | LLFile::noreplace, ec); + ensure("LLFile constructor should not have opened the already existing file", !file); + ensure("error_code from LLFile constructor should indicate an error", (bool)ec); + + LLFile::remove(testfile.string()); + file = LLFile(testfile.string(), LLFile::out | LLFile::app | LLFile::trunc, ec); + ensure("LLFile constructor should not have opened the file with conflicting flags", !file); + ensure("error_code from LLFile constructor should indicate an error", (bool)ec); + + file = LLFile(testfile.string(), LLFile::out | LLFile::app | LLFile::noreplace, ec); + ensure("LLFile constructor should not have opened the file with conflicting flags", !file); + ensure("error_code from LLFile constructor should indicate an error", (bool)ec); + + testfile = testdir; + testfile.append("llfile_test"); + testfile.append("llfile_test.bin"); + + file = LLFile(testfile.string(), LLFile::in, ec); + ensure("LLFile constructor should not have been able to open the file in the non-existing directory", !file); + ensure("error_code from LLFile constructor should indicate an error", (bool)ec); + } +} // namespace tut -- cgit v1.3 From 13a3cf92ca9e647dfc39e761ce0068aa021ca657 Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Fri, 28 Nov 2025 13:30:36 +0100 Subject: - Improve documentation of LLFile functions and methods - Improve error handling in LLFile::read(), LLFile::write() and LLFile::copy() - Improve LLFileSystem::getFileSize() to work with the extra possibility of LLFile::size() returning -1 on error - Replace LLAPRFile:readEx() and LLAPRFile::writeEx() with according LLFile functions --- indra/llcommon/llfile.cpp | 119 ++++++++-------- indra/llcommon/llfile.h | 266 ++++++++++++++++++++---------------- indra/llfilesystem/llfilesystem.cpp | 4 +- indra/newview/lltexturecache.cpp | 62 +++++---- 4 files changed, 243 insertions(+), 208 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 6ccf01dd78..cb3357419a 100755 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -29,7 +29,6 @@ #include "linden_common.h" #include "llfile.h" -#include "llstring.h" #include "llerror.h" #include "stringize.h" @@ -236,7 +235,7 @@ static int warnif(const std::string& desc, const std::string& filename, int rc, // For certain operations, a particular errno value might be // acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit - // EEXIST. Don't warn if caller explicitly says this errno is okay. + // EEXIST. Don't log a warning if caller explicitly says this errno is okay. if (errn != accept) { LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL; @@ -910,22 +909,29 @@ S64 LLFile::read(const std::string& filename, void* buf, S64 offset, S64 nbytes, return 0; } - std::ios_base::openmode omode = LLFile::in | LLFile::binary; - - LLFile file(filename, omode, ec); - if (file) + if (!buf || offset < 0) { - S64 bytes_read = 0; - if (offset > 0) - { - file.seek(offset, ec); - } - if (!ec) + set_ec_to_parameter_error(ec); + } + else + { + std::ios_base::openmode omode = LLFile::in | LLFile::binary; + + LLFile file(filename, omode, ec); + if (file) { - bytes_read = file.read(buf, nbytes, ec); + S64 bytes_read = 0; + if (offset > 0) + { + file.seek(offset, ec); + } if (!ec) { - return bytes_read; + bytes_read = file.read(buf, nbytes, ec); + if (!ec) + { + return bytes_read; + } } } } @@ -949,26 +955,33 @@ S64 LLFile::write(const std::string& filename, const void* buf, S64 offset, S64 return 0; } - std::ios_base::openmode omode = LLFile::out | LLFile::binary; - if (offset < 0) + if (!buf) { - omode |= LLFile::app; + set_ec_to_parameter_error(ec); } - - LLFile file(filename, omode, ec); - if (file) + else { - S64 bytes_written = 0; - if (offset > 0) + std::ios_base::openmode omode = LLFile::out | LLFile::binary; + if (offset < 0) { - file.seek(offset, ec); + omode |= LLFile::app; } - if (!ec) + + LLFile file(filename, omode, ec); + if (file) { - bytes_written = file.write(buf, nbytes, ec); + S64 bytes_written = 0; + if (offset > 0) + { + file.seek(offset, ec); + } if (!ec) { - return bytes_written; + bytes_written = file.write(buf, nbytes, ec); + if (!ec) + { + return bytes_written; + } } } } @@ -976,15 +989,21 @@ S64 LLFile::write(const std::string& filename, const void* buf, S64 offset, S64 } // static -bool LLFile::copy(const std::string& from, const std::string& to) +bool LLFile::copy(const std::string& source, const std::string& target) { std::error_code ec; - std::filesystem::path from_path = utf8StringToPath(from); - std::filesystem::path to_path = utf8StringToPath(to); - bool copied = std::filesystem::copy_file(from_path, to_path, ec); + return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec); +} + +// static +bool LLFile::copy(const std::string& source, const std::string& target, std::filesystem::copy_options options, std::error_code& ec) +{ + std::filesystem::path source_path = utf8StringToPath(source); + std::filesystem::path target_path = utf8StringToPath(target); + bool copied = std::filesystem::copy_file(source_path, target_path, options, ec); if (!copied) { - LL_WARNS("LLFile") << "copy failed" << LL_ENDL; + warnif(STRINGIZE("copy failed, to '" << target << "' from"), source, ec); } return copied; } @@ -1004,7 +1023,7 @@ int LLFile::stat(const std::string& filename, llstat* filestatus, const char *fn // static std::time_t LLFile::getCreationTime(const std::string& filename, int suppress_warning) { - // As if C++20 there is no functionality in std::filesystem to retrieve this information + // As of C++20 there is no functionality in std::filesystem to retrieve this information llstat filestat; int rc = stat(filename, &filestat, "getCreationTime", suppress_warning); if (rc == 0) @@ -1046,7 +1065,7 @@ S64 LLFile::size(const std::string& filename, int suppress_warning) std::error_code ec; std::filesystem::path file_path = utf8StringToPath(filename); std::intmax_t size = (std::intmax_t)std::filesystem::file_size(file_path, ec); - return warnif("size", filename, ec, suppress_warning) ? -1 : size; + return warnif("size", filename, ec, suppress_warning) ? 0 : size; } // static @@ -1057,13 +1076,13 @@ std::filesystem::file_status LLFile::getStatus(const std::string& filename, bool std::filesystem::file_status status; if (dontFollowSymLink) { - status = std::filesystem::status(file_path, ec); + status = std::filesystem::symlink_status(file_path, ec); } else { - status = std::filesystem::symlink_status(file_path, ec); + status = std::filesystem::status(file_path, ec); } - warnif("getattr", filename, ec, suppress_warning); + warnif("getStatus()", filename, ec, suppress_warning); return status; } @@ -1155,10 +1174,8 @@ void llifstream::open(const std::string& _Filename, ios_base::openmode _Mode) _Mode | ios_base::in); } - /************** output file stream ********************************/ - llofstream::llofstream() {} // explicit @@ -1174,30 +1191,4 @@ void llofstream::open(const std::string& _Filename, ios_base::openmode _Mode) _Mode | ios_base::out); } -/************** helper functions ********************************/ - -std::streamsize llifstream_size(llifstream& ifstr) -{ - if(!ifstr.is_open()) return 0; - std::streampos pos_old = ifstr.tellg(); - ifstr.seekg(0, std::ios_base::beg); - std::streampos pos_beg = ifstr.tellg(); - ifstr.seekg(0, std::ios_base::end); - std::streampos pos_end = ifstr.tellg(); - ifstr.seekg(pos_old, std::ios_base::beg); - return pos_end - pos_beg; -} - -std::streamsize llofstream_size(llofstream& ofstr) -{ - if(!ofstr.is_open()) return 0; - std::streampos pos_old = ofstr.tellp(); - ofstr.seekp(0, std::ios_base::beg); - std::streampos pos_beg = ofstr.tellp(); - ofstr.seekp(0, std::ios_base::end); - std::streampos pos_end = ofstr.tellp(); - ofstr.seekp(pos_old, std::ios_base::beg); - return pos_end - pos_beg; -} - #endif // LL_WINDOWS diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 2bfc4d1ffb..ae8e755fe7 100755 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -53,19 +53,25 @@ typedef FILE LLFILE; #include "llstring.h" // safe char* -> std::string conversion +/// This class provides a selection of functions to operate on files through names and +/// a class implementation to represent a file for reading and writing to it /// All the functions with a path string input take UTF8 path/filenames +/// +/// @nosubgrouping +/// class LL_COMMON_API LLFile { public: - //-------------------------------------------------------------------------------------- - // constants - //-------------------------------------------------------------------------------------- - // These can be passed to the omode parameter of LLFile::open() and its constructor - /* + // ================================================================================ + /// @name Constants + /// + ///@{ + /** These can be passed to the omode parameter of LLFile::open() and its constructor + This is similar to the openmode flags for std:fstream but not exactly the same std::fstream open() does not allow to open a file for writing without either forcing the file to be truncated on open or all write operations being always - appended to the end of the file. + appended to the end of the file or failing to open when the file does not exist. But to allow implementing the LLAPRFile::writeEx() functionality we need to be able to write at a random position to the file without truncating it on open. @@ -104,65 +110,71 @@ public: static const std::ios_base::openmode trunc = 1 << 6; // truncate on open static const std::ios_base::openmode noreplace = 1 << 7; // no replace if it exists - // Additional optional omode flags to open() and lmode to fopen() or mode flags to lock() - // to indicate which sort of lock if any to attempt to get + /// Additional optional flags to omode in open() and lmode in fopen() or lock() + /// to indicate which sort of lock if any to attempt to get + /// + /// NOTE: there is a fundamental difference between platforms. + /// On Windows this lock is mandatory as it is part of the API to open a file handle and other + /// processes can not avoid it. If a file was opened denying other processes read and/or write + /// access, trying to open the same file in another process with that access will fail. + /// On Mac and Linux it is only an advisory lock implemented through the flock() system call. + /// This means that any other application needs to also attempt to at least acquire a shared + /// lock on the file in order to notice that the file is actually already locked. It can + /// therefore not be used to prevent random other applications from accessing the file, but it + /// works for other viewer processes when they use either the LLFile::open() or LLFile::fopen() + /// functions with the appropriate lock flags to open a file. static const std::ios_base::openmode exclusive = 1 << 16; static const std::ios_base::openmode shared = 1 << 17; - // When used either in omode for LLFile::open() or in lmode for LLFile::fopen() there is a - // difference between platforms. - // On Windows this lock is mandatory as it is part of the API to open a file and other processes - // can not avoid it. If a file was opened denying other processes read and/or write access, - // trying to open the same file with that access will fail. - // On Mac and Linux it is only an advisory lock. This means that the other application - // needs to also attempt to at least acquire a shared lock on the file in order to notice that - // the file is actually already locked. It can therefore not be used to prevent random other - // applications from accessing the file, but it works for other viewer processes when they use - // either the LLFile::open() or LLFile::fopen() functions with the appropriate lock flags to - // open a file. - - // Additional mode flag to indicate to rather fail instead of blocking when trying - // to acquire a lock with LLFile::lock() + + /// Additional lmode flag to indicate to rather fail instead of blocking when trying + /// to acquire a lock with LLFile::lock() static const std::ios_base::openmode noblock = 1 << 18; + /// The mask value for the lock mask bits static const std::ios_base::openmode lock_mask = exclusive | shared; - // These can be passed to the dir parameter of LLFile::seek() + /// One of these can be passed to the dir parameter of LLFile::seek() static const std::ios_base::seekdir beg = std::ios_base::beg; static const std::ios_base::seekdir cur = std::ios_base::cur; static const std::ios_base::seekdir end = std::ios_base::end; + ///@} - //-------------------------------------------------------------------------------------- - // constructor/deconstructor - //-------------------------------------------------------------------------------------- + // ================================================================================ + /// @name constructor/deconstructor + /// + ///@{ + /// default constructor LLFile() : mHandle(InvalidHandle) {} - // no copy + /// no copy constructor LLFile(const LLFile&) = delete; - // move construction + /// move constructor LLFile(LLFile&& other) noexcept { mHandle = other.mHandle; other.mHandle = InvalidHandle; } - // open a file + /// constructor opening the file LLFile(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666) : mHandle(InvalidHandle) { open(filename, omode, ec, perm); } - // Make it a RAII class + /// destructor always attempts to close the file ~LLFile() { close(); } + ///@} - //-------------------------------------------------------------------------------------- - // operators - //-------------------------------------------------------------------------------------- - // copy assignment deleted + // ================================================================================ + /// @name operators + /// + ///@{ + /// copy assignment deleted LLFile& operator=(const LLFile&) = delete; - // move assignment + /// move assignment LLFile& operator=(LLFile&& other) noexcept { close(); @@ -173,13 +185,17 @@ public: // detect whether the wrapped file descriptor/handle is open or not explicit operator bool() const { return (mHandle != InvalidHandle); } bool operator!() { return (mHandle == InvalidHandle); } + ///@} - //-------------------------------------------------------------------------------------- - // class member methods - //-------------------------------------------------------------------------------------- - - /// All of these functions take as one of their parameters a std::error_code object which can be used to + /// ================================================================================ + /// @name class member methods + /// + /// These methods provide read and write support as well as additional functionality to query the size of + /// the file, change the position of the the current file pointer or query it. + /// + /// Most of these functions take as one of their parameters a std::error_code object which can be used to /// determine in more detail what error occurred if required + ///@{ /// Open a file with the specific open mode flags int open(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666); @@ -189,7 +205,7 @@ public: S64 size(std::error_code& ec); ///< @returns the number of bytes in the file or -1 on failure - /// Return the current file pointer into the file + /// Query the position of the current file pointer in the file S64 tell(std::error_code& ec); ///< @returns the absolute offset of the file pointer in bytes relative to the start of the file or -1 on failure @@ -201,76 +217,78 @@ public: int seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec); ///< @returns 0 on success, -1 on failure - /// Read a number of bytes into the buffer starting at the current file pointer + /// Read the specified number of bytes into the buffer starting at the current file pointer S64 read(void* buffer, S64 nbytes, std::error_code& ec); ///< If the file ends before the requested amount of bytes could be read, the function succeeds and - /// returns the bytes up to the end of the file. The return value indicates the number of actually - /// read bytes and can be therefore smaller than the requested amount. - /// @returns the number of bytes read from the file or -1 on failure + /// returns the bytes up to the end of the file. The return value indicates the number of actually + /// read bytes and can be therefore smaller than the requested amount. + /// @returns the number of bytes read from the file or -1 on failure - /// Write a number of bytes to the file starting at the current file pointer + /// Write the specified number of bytes to the file starting at the current file pointer S64 write(const void* buffer, S64 nbytes, std::error_code& ec); - /// @returns the number of bytes written to the file or -1 on failure + ///< @returns the number of bytes written to the file or -1 on failure - /// Write into the file starting at the current file pointer using printf style format + /// Write into the file starting at the current file pointer using printf style format and + /// additional optional parameters as specified in the fmt string S64 printf(const char* fmt, ...); - /// @returns the number of bytes written to the file or -1 on failure + ///< @returns the number of bytes written to the file or -1 on failure /// Attempt to acquire or release a lock on the file - int lock(int mode, std::error_code& ec); - ///< mode can be one of LLFile::exclusive or LLFile::shared to acquire the according lock - /// or 0 to give up an earlier acquired lock. Adding LLFile::noblock together with one of - /// the lock requests will cause the function to fail if the lock can not be acquired, - /// otherwise the function will block until the lock can be acquired. - /// @returns 0 on success, -1 on failure + int lock(int lmode, std::error_code& ec); + ///< lmode can be one of LLFile::exclusive or LLFile::shared to acquire the according lock + /// or 0 to give up an earlier acquired lock. Adding LLFile::noblock together with one of + /// the lock requests will cause the function to fail if the lock can not be acquired, + /// otherwise the function will block until the lock can be acquired. + /// @returns 0 on success, -1 on failure /// close the file explicitely int close(std::error_code& ec); ///< @returns 0 on success, -1 on failure - /// Convinience function to close the file without parameters + /// Convenience function to close the file without additional parameters int close(); ///< @returns 0 on success, -1 on failure + ///@} - //---------------------------------------------------------------------------------------- - // static member functions - // - // All filename parameters are UTF8 file paths - // - //---------------------------------------------------------------------------------------- + /// ================================================================================ + /// @name static member functions + /// + /// These functions are static and operate with UTF8 filenames as one of their parameters. + /// + ///@{ /// open a file with the specified access mode static LLFILE* fopen(const std::string& filename, const char* accessmode, int lmode = 0); ///< 'accessmode' follows the rules of the Posix fopen() mode parameter - /// "r" open the file for reading only and positions the stream at the beginning - /// "r+" open the file for reading and writing and positions the stream at the beginning - /// "w" open the file for reading and writing and truncate it to zero length - /// "w+" open or create the file for reading and writing and truncate to zero length if it existed - /// "a" open the file for reading and writing and and before every write position the stream at the end of the file - /// "a+" open or create the file for reading and writing and before every write position the stream at the end of the file + /// "r" open the file for reading only and positions the stream at the beginning + /// "r+" open the file for reading and writing and positions the stream at the beginning + /// "w" open the file for reading and writing and truncate it to zero length + /// "w+" open or create the file for reading and writing and truncate to zero length if it existed + /// "a" open the file for reading and writing and before every write position the stream at the end of the file + /// "a+" open or create the file for reading and writing and before every write position the stream at the end of the file /// - /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac - /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not - /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither - /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT. - /// This means that it is always a good idea to append "b" specifically for binary file access to - /// avoid corruption of the binary consistency of the data stream when reading or writing - /// Other characters in 'accessmode', while possible on some platforms (Windows), will usually - /// cause an error as fopen will verify this parameter + /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac + /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not + /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither + /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT. + /// This means that it is always a good idea to append "b" specifically for binary file access to + /// avoid corruption of the binary consistency of the data stream when reading or writing + /// Other characters in 'accessmode', while possible on some platforms (Windows), will usually + /// cause an error on other platforms as fopen will verify this parameter /// - /// lmode is optional and allows to lock the file for other processes either as a shared lock or an - /// exclusive lock. If the requested lock conflicts with an already existing lock, the open fails. - /// Pass either LLFIle::exclusive or LLFile::shared to this parameter if you want to prevent other - /// processes from reading (exclusive lock) or writing (shared lock) to the file. It will always use - /// LLFile::noblock, meaning the open will immediately fail if it conflicts with an existing lock on the - /// file. + /// lmode is optional and allows to lock the file for other processes either as a shared lock or an + /// exclusive lock. If the requested lock conflicts with an already existing lock, the open fails. + /// Pass either LLFIle::exclusive or LLFile::shared to this parameter if you want to prevent other + /// processes from reading (exclusive lock) or writing (shared lock) to the file. It will always use + /// LLFile::noblock, meaning the open will immediately fail if it conflicts with an existing lock on the + /// file. /// - /// @returns a valid LLFILE* pointer on success that can be passed to the fread() and fwrite() functions - /// and some other f functions in the Standard C library that accept a FILE* as parameter - /// or NULL on failure + /// @returns a valid LLFILE* pointer on success that can be passed to the fread() and fwrite() functions + /// and some other f functions in the Standard C library that accept a FILE* as parameter + /// or NULL on failure /// Close a file handle opened with fopen() above static int close(LLFILE * file); - /// @returns 0 on success and -1 on failure. + ///< @returns 0 on success and -1 on failure. /// create a directory static int mkdir(const std::string& filename); @@ -288,27 +306,42 @@ public: ///< it will silently overwrite newname if it exists without returning an error /// Posix guarantees that if newname already exists, then there will be no moment /// in which for other processes newname does not exist. There is no such guarantee - /// under Windows at this time. It may do it in the same way but the used Windows API - /// does not make such guarantees. + /// under Windows at this time. It may do it in the same way but the used Windows + /// APIs do not make such guarantees. /// @returns 0 on success and -1 on failure. - /// copy the contents of the file from 'from' to 'to' filename - static bool copy(const std::string& from, const std::string& to); - ///< @returns true on success and false on failure. + /// copy the contents of the file from 'source' to 'target' + static bool copy(const std::string& source, const std::string& target); + ///< Copies the contents of the file 'source' to the file 'target', overwriting 'target' if it already + /// existed. + /// This is a convenience function that implements the previous behavior of silently overwriting an + /// already existing target file. Consider using the function below if you desire a different + /// behavior when the target file already exists + /// @returns true on success and false on failure. + + /// copy the contents of the file from 'from' to 'to' + static bool copy(const std::string& source, const std::string& target, std::filesystem::copy_options options, std::error_code& ec); + ///< Copies the contents of the file 'source' to the file 'target'. The options parameter allows to + /// specify what should happen if the "target" file already exists: + /// std::filesystem::copy_options::none - return an error in ec and fail + /// std::filesystem::copy_options::skip_existing - skip the operation and do not overwrite file + /// std::filesystem::copy_options::overwrite_existing - overwrite the file + /// std::filesystem::copy_options::update_existing - overwrite the file only if it is older than the file being copied + /// @returns true on success and false on failure. /// retrieve the content of a file into a string static std::string getContents(const std::string& filename); static std::string getContents(const std::string& filename, std::error_code& ec); - ///< @returns the content of the file or an empty string on failure + ///< @returns the entire content of the file as std::string or an empty string on failure /// read nBytes from the file into the buffer, starting at offset in the file - static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes); - static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec); + static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes); + static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec); ///< @returns bytes read on success, or -1 on failure /// write nBytes from the buffer into the file, starting at offset in the file - static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes); - static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec); + static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes); + static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec); ///< A negative offset will append the data to the end of the file /// @returns bytes written on success, or -1 on failure @@ -321,7 +354,16 @@ public: /// get the creation data and time of a file static std::time_t getCreationTime(const std::string& filename, int suppress_warning = 0); - ///< @returns the creation time of the file or 0 on error + ///< Different systems have different support for this. Under Windows this is supposedly + /// the actual time the file was created, on the Mac this is the actual birth date of + /// the file which is in fact the creation time. The according ctime entry in the stat + /// structure under Linux (and any other *nix really) is however contrary to what one + /// might expect based on the c in ctime not the creation time but the time the last + /// change to the inode entry was made. Changing access rights to a file will update + /// this value too. In order to have a true creation time under Linux, we would have + /// to use the statx() call which is available since kernel 4.19, but that will require + /// considerable changes to the implementation of above stat() function. + /// @returns the creation time (last status change under Linux) of the file or 0 on error /// get the last modification data and time of a file static std::time_t getModificationTime(const std::string& filename, int suppress_warning = 0); @@ -332,7 +374,7 @@ public: ///< dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it /// we pass by default ENOENT in the optional 'suppress_warning' parameter to not spam the log with /// warnings when the file or directory does not exist - /// @returns a std::filesystem::file_status value that can be passed to the according std::filesystem::exists() + /// @returns a std::filesystem::file_status value that can be passed to the appropriate std::filesystem::exists() /// and other APIs accepting a file_status. /// get the size of a file in bytes @@ -342,19 +384,19 @@ public: /// @returns the file size on success or -1 on failure. /// check if filename is an existing file or directory - static bool exists(const std::string& filename); + static bool exists(const std::string& filename); ///< @returns true if the path is for an existing file or directory /// check if filename is an existing directory - static bool isdir(const std::string& filename); + static bool isdir(const std::string& filename); ///< @returns true if the path is for an existing directory /// check if filename is an existing file - static bool isfile(const std::string& filename); + static bool isfile(const std::string& filename); ///< @returns true if the path is for an existing file /// check if filename is a symlink - static bool islink(const std::string& filename); + static bool islink(const std::string& filename); ///< @returns true if the path is pointing at a symlink /// return a path to the temporary directory on the system @@ -363,6 +405,7 @@ public: /// converts a string containing a path in utf8 encoding into an explicit filesystem path static std::filesystem::path utf8StringToPath(const std::string& pathname); ///< @returns the path as a std::filesystem::path + ///@} private: #if LL_WINDOWS @@ -373,9 +416,9 @@ private: const llfile_handle_t InvalidHandle = -1; #endif - //---------------------------------------------------------------------------------------- - // static member functions - //---------------------------------------------------------------------------------------- + /// ================================================================================ + /// @name private static member functions + /// #if LL_WINDOWS /// convert a string containing a path in utf8 encoding into a Windows format std::wstring static std::wstring utf8StringToWstring(const std::string& pathname); @@ -393,7 +436,7 @@ private: /// /// @returns the path as a std::wstring path #endif - llfile_handle_t mHandle; + llfile_handle_t mHandle; // The file handle/descriptor std::ios_base::openmode mOpen; // Used to emulate std::ios_base::app under Windows }; @@ -485,17 +528,6 @@ class LL_COMMON_API llofstream : public std::ofstream ios_base::openmode _Mode = ios_base::out|ios_base::trunc); }; - -/** - * @brief filesize helpers. - * - * The file size helpers are not considered particularly efficient, - * and should only be used for config files and the like -- not in a - * loop. - */ -std::streamsize LL_COMMON_API llifstream_size(llifstream& fstr); -std::streamsize LL_COMMON_API llofstream_size(llofstream& fstr); - #else // ! LL_WINDOWS // on non-windows, llifstream and llofstream are just mapped directly to the std:: equivalents diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 4238dd64e9..087daf78be 100755 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -114,8 +114,8 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) { const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type); - - return (S32)LLFile::size(filename); + S64 fileSize = LLFile::size(filename); + return (fileSize > 0) ? (S32)fileSize : 0; } bool LLFileSystem::read(U8* buffer, S32 bytes) diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index ef7542fe2e..1b63739f79 100755 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -210,7 +210,8 @@ bool LLTextureCacheLocalFileWorker::doRead() } mReadData = (U8*)ll_aligned_malloc_16(mDataSize); - S32 bytes_read = LLAPRFile::readEx(mFileName, mReadData, mOffset, mDataSize, mCache->getLocalAPRFilePool()); +// S32 bytes_read = LLAPRFile::readEx(mFileName, mReadData, mOffset, mDataSize, mCache->getLocalAPRFilePool()); + S32 bytes_read = (S32)LLFile::read(mFileName, mReadData, mOffset, mDataSize); if (bytes_read != mDataSize) { @@ -346,11 +347,15 @@ bool LLTextureCacheRemoteWorker::doRead() if (mReadData) { - S32 bytes_read = LLAPRFile::readEx( local_filename, - mReadData, - mOffset, - mDataSize, - mCache->getLocalAPRFilePool()); +// S32 bytes_read = LLAPRFile::readEx(local_filename, +// mReadData, +// mOffset, +// mDataSize, +// mCache->getLocalAPRFilePool()); + S32 bytes_read = (S32)LLFile::read(local_filename, + mReadData, + mOffset, + mDataSize); if (bytes_read != mDataSize) { @@ -410,8 +415,9 @@ bool LLTextureCacheRemoteWorker::doRead() mReadData = (U8*)ll_aligned_malloc_16(size); if (mReadData) { - S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName, - mReadData, offset, size, mCache->getLocalAPRFilePool()); +// S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName, +// mReadData, offset, size, mCache->getLocalAPRFilePool()); + S32 bytes_read = (S32)LLFile::read(mCache->mHeaderDataFileName, mReadData, offset, size); if (bytes_read != size) { LL_WARNS() << "LLTextureCacheWorker: " << mID @@ -487,10 +493,13 @@ bool LLTextureCacheRemoteWorker::doRead() mReadData = data; // Read the data at last - S32 bytes_read = LLAPRFile::readEx(filename, - mReadData + data_offset, - file_offset, file_size, - mCache->getLocalAPRFilePool()); +// S32 bytes_read = LLAPRFile::readEx(filename, +// mReadData + data_offset, +// file_offset, file_size, +// mCache->getLocalAPRFilePool()); + S32 bytes_read = (S32)LLFile::read(filename, + mReadData + data_offset, + file_offset, file_size); if (bytes_read != file_size) { LL_WARNS() << "LLTextureCacheWorker: " << mID @@ -636,13 +645,15 @@ bool LLTextureCacheRemoteWorker::doWrite() U8* padBuffer = (U8*)ll_aligned_malloc_16(TEXTURE_CACHE_ENTRY_SIZE); memset(padBuffer, 0, TEXTURE_CACHE_ENTRY_SIZE); // Init with zeros memcpy(padBuffer, mWriteData, mDataSize); // Copy the write buffer - bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, padBuffer, offset, size, mCache->getLocalAPRFilePool()); +// bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, padBuffer, offset, size, mCache->getLocalAPRFilePool()); + bytes_written = (S32)LLFile::write(mCache->mHeaderDataFileName, padBuffer, offset, size); ll_aligned_free_16(padBuffer); } else { // Write the header record (== first TEXTURE_CACHE_ENTRY_SIZE bytes of the raw file) in the header file - bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, mCache->getLocalAPRFilePool()); +// bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, , mCache->getLocalAPRFilePool()); + bytes_written = (S32)LLFile::write(mCache->mHeaderDataFileName, mWriteData, offset, size); } if (bytes_written <= 0) @@ -678,15 +689,15 @@ bool LLTextureCacheRemoteWorker::doWrite() else { S32 file_size = mDataSize - TEXTURE_CACHE_ENTRY_SIZE; - { // build the cache file name from the UUID std::string filename = mCache->getTextureFileName(mID); // LL_INFOS() << "Writing Body: " << filename << " Bytes: " << file_offset+file_size << LL_ENDL; - S32 bytes_written = LLAPRFile::writeEx(filename, +/* S32 bytes_written = LLAPRFile::writeEx(filename, mWriteData + TEXTURE_CACHE_ENTRY_SIZE, 0, file_size, - mCache->getLocalAPRFilePool()); + mCache->getLocalAPRFilePool()); */ + S32 bytes_written = (S32)LLFile::write(filename, mWriteData + TEXTURE_CACHE_ENTRY_SIZE, 0, file_size); if (bytes_written <= 0) { LL_WARNS() << "LLTextureCacheWorker: " << mID @@ -943,7 +954,7 @@ const char* fast_cache_filename = "FastCache.cache"; void LLTextureCache::setDirNames(ELLPath location) { - std::string delem = gDirUtilp->getDirDelimiter(); +// std::string delem = gDirUtilp->getDirDelimiter(); mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, entries_filename); mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, cache_filename); @@ -1072,8 +1083,9 @@ void LLTextureCache::readEntriesHeader() llassert_always(mHeaderAPRFile == NULL); if (LLFile::isfile(mHeaderEntriesFileName)) { - LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), - mHeaderAPRFilePoolp); +// LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), +// mHeaderAPRFilePoolp); + LLFile::read(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); } else //create an empty entries header. { @@ -1089,7 +1101,7 @@ void LLTextureCache::setEntriesHeader() // For simplicity we use predefined size of header, so if version string // doesn't fit, either getEngineInfo() returned malformed string or // sHeaderEncoderStringSize need to be increased. - // Also take into accout that c_str() returns additional null character + // Also take into account that c_str() returns additional null character LL_ERRS() << "Version string doesn't fit in header" << LL_ENDL; } @@ -1104,8 +1116,9 @@ void LLTextureCache::writeEntriesHeader() llassert_always(mHeaderAPRFile == NULL); if (!mReadOnly) { - LLAPRFile::writeEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), - mHeaderAPRFilePoolp); +// LLAPRFile::writeEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), +// mHeaderAPRFilePoolp); + LLFile::write(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); } } @@ -1209,8 +1222,7 @@ void LLTextureCache::writeEntryToHeaderImmediately(S32& idx, Entry& entry, bool idx = -1 ;//mark the idx invalid. return ; } - - mHeaderAPRFile->seek(APR_SET, offset); + aprfile->seek(APR_SET, offset); } else { -- cgit v1.3 From 15f9886ee779b2d014f4a386eac34700b3014bec Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Sat, 29 Nov 2025 15:46:48 +0100 Subject: The returned vector should only contain filenames, not the entire paths --- indra/llfilesystem/lldir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp index 4c51cc12ab..d36df99c14 100755 --- a/indra/llfilesystem/lldir.cpp +++ b/indra/llfilesystem/lldir.cpp @@ -106,7 +106,7 @@ std::vector LLDir::getFilesInDir(const std::string &dirname) { if (std::filesystem::is_regular_file(dir_itr->status())) { - v.push_back(dir_itr->path().string()); + v.push_back(dir_itr->path().filename().string()); } } } -- cgit v1.3 From fe61aa3c8531d647c4ae2e183e73132fcbad90aa Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Sat, 29 Nov 2025 15:55:29 +0100 Subject: Fix several typos and comments --- indra/llcommon/llfile.cpp | 6 ++-- indra/llcommon/llfile.h | 55 ++++++++++++++++++------------------ indra/newview/llfloateruipreview.cpp | 34 +++++++++++----------- 3 files changed, 48 insertions(+), 47 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index cb3357419a..0751bde07c 100755 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -332,13 +332,13 @@ inline DWORD decode_open_create_flags(std::ios_base::openmode omode) { if (!(omode & LLFile::out)) { - return TRUNCATE_EXISTING; // open and truncatte if it exists, otherwise fail + return TRUNCATE_EXISTING; // open and truncate if it exists, otherwise fail } return CREATE_ALWAYS; // open and truncate if it exists, otherwise create it } if (!(omode & LLFile::out)) { - return OPEN_EXISTING; // open if exists, otherwise fail + return OPEN_EXISTING; // open if it exists, otherwise fail } // LLFile::app or (LLFile::out and (!LLFile::trunc or !LLFile::noreplace)) return OPEN_ALWAYS; // open if it exists, otherwise create it @@ -738,7 +738,7 @@ int LLFile::lock(int mode, std::error_code& ec) { flags |= LOCKFILE_EXCLUSIVE_LOCK; } - // We lock the maximum range, since flock only supports locking the entire file too + // We lock the maximum range, since flock only supports locking the entire file too if (LockFileEx(mHandle, flags, 0, MAXDWORD, MAXDWORD, &overlapped)) { return clear_error(ec); diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index ae8e755fe7..f48b0f5cad 100755 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -77,30 +77,30 @@ public: any other combinations than listed here are not allowed and will cause an error - bin in out trunc app norep File exists File does not exist - ------------------------------------------------------------------------------- - - + - - - - Open at begin Failure to open - + + - - - - " " - - - + - - - " Create new - + - + - - - " " - - + + - - - " " - + + + - - - " " - ------------------------------------------------------------------------------- - - - + + - - Destroy contents Create new - + - + + - - " " - - + + + - - " " - + + + + - - " " - ------------------------------------------------------------------------------- - - - + x - + Failure to open Create new - + - + x - + " " - - + + x - + " " - + + + x - + " " - ------------------------------------------------------------------------------- - - - + - + - Write to end Create new - + - + - + - " " - - + + - + - " " - + + + - + - " " - ------------------------------------------------------------------------------- + bin in out trunc app noreplace File exists File doesn't exist + ---------------------------------------------------------------------------------- + - + - - - - Open at begin Failure to open + + + - - - - " " + - - + - - - " Create new + + - + - - - " " + - + + - - - " " + + + + - - - " " + ---------------------------------------------------------------------------------- + - - + + - - Destroy contents Create new + + - + + - - " " + - + + + - - " " + + + + + - - " " + ---------------------------------------------------------------------------------- + - - + x - + Failure to open Create new + + - + x - + " " + - + + x - + " " + + + + x - + " " + ---------------------------------------------------------------------------------- + - - + - + - Write to end Create new + + - + - + - " " + - + + - + - " " + + + + - + - " " + ---------------------------------------------------------------------------------- */ static const std::ios_base::openmode app = 1 << 1; // append to end static const std::ios_base::openmode ate = 1 << 2; // initialize to end @@ -241,7 +241,7 @@ public: /// otherwise the function will block until the lock can be acquired. /// @returns 0 on success, -1 on failure - /// close the file explicitely + /// close the file explicitly int close(std::error_code& ec); ///< @returns 0 on success, -1 on failure @@ -297,8 +297,9 @@ public: /// remove a file or directory static int remove(const std::string& filename, int suppress_warning = 0); - ///< pass ENOENT in the optional 'suppress_warning' parameter if you don't want - /// a warning in the log when the directory does not exist + ///< pass an errno value (e.g., ENOENT) in the optional 'suppress_warning' parameter if you want to + /// suppress a warning in the log when the failure matches that errno (e.g., suppress warning if + /// the file or directory does not exist) /// @returns 0 on success and -1 on failure. /// rename a file diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index db98a70573..0bf0946c42 100755 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -484,47 +484,47 @@ bool LLFloaterUIPreview::postBuild() bool found_en_us = false; std::string language_directory; std::string xui_dir = get_xui_dir(); // directory containing localizations -- don't forget trailing delim - mLanguageSelection->removeall(); // clear out anything temporarily in list from XML + mLanguageSelection->removeall(); // clear out anything temporarily in list from XML LLDirIterator iter(xui_dir, "*"); - while(found) // for every directory + while (found) // for every directory { - if((found = iter.next(language_directory))) // get next directory + if ((found = iter.next(language_directory))) // get next directory { std::string full_path = gDirUtilp->add(xui_dir, language_directory); - if(LLFile::isfile(full_path.c_str())) // if it's not a directory, skip it + if (!LLFile::isdir(full_path.c_str())) // if it's not a directory, skip it { continue; } - if(strncmp("template",language_directory.c_str(),8) && -1 == language_directory.find(".")) // if it's not the template directory or a hidden directory + if (strncmp("template",language_directory.c_str(), 8) && -1 == language_directory.find(".")) // if it's not the template directory or a hidden directory { - if(!strncmp("en",language_directory.c_str(),5)) // remember if we've seen en, so we can make it default + if (!strncmp("en",language_directory.c_str(), 5)) // remember if we've seen en, so we can make it default { found_en_us = true; } else { - mLanguageSelection->add(std::string(language_directory)); // add it to the language selection dropdown menu + mLanguageSelection->add(std::string(language_directory)); // add it to the language selection dropdown menu mLanguageSelection_2->add(std::string(language_directory)); } } } } - if(found_en_us) + if (found_en_us) { - mLanguageSelection->add(std::string("en"),ADD_TOP); // make en first item if we found it - mLanguageSelection_2->add(std::string("en"),ADD_TOP); + mLanguageSelection->add(std::string("en"), ADD_TOP); // make en first item if we found it + mLanguageSelection_2->add(std::string("en"), ADD_TOP); } else { std::string warning = std::string("No EN localization found; check your XUI directories!"); popupAndPrintWarning(warning); } - mLanguageSelection->selectFirstItem(); // select the first item + mLanguageSelection->selectFirstItem(); // select the first item mLanguageSelection_2->selectFirstItem(); - refreshList(); // refresh the list of available floaters + refreshList(); // refresh the list of available floaters return true; } @@ -892,7 +892,7 @@ void LLFloaterUIPreview::displayFloater(bool click, S32 ID) // Add localization to title so user knows whether it's localized or defaulted to en std::string full_path = getLocalizedDirectory() + path; std::string floater_lang = "EN"; - if (LLFile::isfile(full_path.c_str())) // if the file does not exist + if (LLFile::isfile(full_path.c_str())) // use localized language if the file exists { floater_lang = getLocStr(ID); } @@ -965,8 +965,8 @@ void LLFloaterUIPreview::onClickEditFloater() } file_path = getLocalizedDirectory() + file_name; - // does it exist? (some localized versions may not have it there are no diffs, and then we try to open an nonexistent file - if(!LLFile::isfile(file_path.c_str())) // if the file does not exist + // Does it exist? (Some localized versions may not have it when there are no diffs, and then we try to open a nonexistent file) + if (!LLFile::isfile(file_path.c_str())) // if the file does not exist { popupAndPrintWarning("No file for this floater exists in the selected localization. Opening the EN version instead."); file_path = get_xui_dir() + mDelim + "en" + mDelim + file_name; // open the en version instead, by default @@ -1115,14 +1115,14 @@ void LLFloaterUIPreview::onClickToggleDiffHighlighting() std::string path_in_textfield = mDiffPathTextBox->getText(); // get file path bool error = false; - if(std::string("") == path_in_textfield) // check for blank file + if(std::string("") == path_in_textfield) // check for blank file { std::string warning = "Unable to highlight differences because no file was provided; fill in the relevant text field"; popupAndPrintWarning(warning); error = true; } - if (!LLFile::isfile(path_in_textfield.c_str()) && !error) // check if the file exists (empty check is reduntant but useful for the informative error message) + if (!LLFile::isfile(path_in_textfield.c_str()) && !error) // check if the file exists (empty check is redundant but useful for the informative error message) { std::string warning = std::string("Unable to highlight differences because an invalid path to a difference file was provided:\"") + path_in_textfield + "\""; popupAndPrintWarning(warning); -- cgit v1.3 From e0c5fc80c78be6ce16d835a249c9da56c991b226 Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Sun, 30 Nov 2025 13:30:09 +0100 Subject: Remove commented out old function calls --- indra/newview/lltexturecache.cpp | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'indra') diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 1b63739f79..ad222f229f 100755 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -210,9 +210,7 @@ bool LLTextureCacheLocalFileWorker::doRead() } mReadData = (U8*)ll_aligned_malloc_16(mDataSize); -// S32 bytes_read = LLAPRFile::readEx(mFileName, mReadData, mOffset, mDataSize, mCache->getLocalAPRFilePool()); S32 bytes_read = (S32)LLFile::read(mFileName, mReadData, mOffset, mDataSize); - if (bytes_read != mDataSize) { // LL_WARNS() << "Error reading file from local cache: " << mFileName @@ -347,16 +345,10 @@ bool LLTextureCacheRemoteWorker::doRead() if (mReadData) { -// S32 bytes_read = LLAPRFile::readEx(local_filename, -// mReadData, -// mOffset, -// mDataSize, -// mCache->getLocalAPRFilePool()); S32 bytes_read = (S32)LLFile::read(local_filename, mReadData, mOffset, mDataSize); - if (bytes_read != mDataSize) { LL_WARNS() << "Error reading file from local cache: " << local_filename @@ -415,8 +407,6 @@ bool LLTextureCacheRemoteWorker::doRead() mReadData = (U8*)ll_aligned_malloc_16(size); if (mReadData) { -// S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName, -// mReadData, offset, size, mCache->getLocalAPRFilePool()); S32 bytes_read = (S32)LLFile::read(mCache->mHeaderDataFileName, mReadData, offset, size); if (bytes_read != size) { @@ -493,10 +483,6 @@ bool LLTextureCacheRemoteWorker::doRead() mReadData = data; // Read the data at last -// S32 bytes_read = LLAPRFile::readEx(filename, -// mReadData + data_offset, -// file_offset, file_size, -// mCache->getLocalAPRFilePool()); S32 bytes_read = (S32)LLFile::read(filename, mReadData + data_offset, file_offset, file_size); @@ -645,14 +631,12 @@ bool LLTextureCacheRemoteWorker::doWrite() U8* padBuffer = (U8*)ll_aligned_malloc_16(TEXTURE_CACHE_ENTRY_SIZE); memset(padBuffer, 0, TEXTURE_CACHE_ENTRY_SIZE); // Init with zeros memcpy(padBuffer, mWriteData, mDataSize); // Copy the write buffer -// bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, padBuffer, offset, size, mCache->getLocalAPRFilePool()); bytes_written = (S32)LLFile::write(mCache->mHeaderDataFileName, padBuffer, offset, size); ll_aligned_free_16(padBuffer); } else { // Write the header record (== first TEXTURE_CACHE_ENTRY_SIZE bytes of the raw file) in the header file -// bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, , mCache->getLocalAPRFilePool()); bytes_written = (S32)LLFile::write(mCache->mHeaderDataFileName, mWriteData, offset, size); } @@ -693,10 +677,6 @@ bool LLTextureCacheRemoteWorker::doWrite() // build the cache file name from the UUID std::string filename = mCache->getTextureFileName(mID); // LL_INFOS() << "Writing Body: " << filename << " Bytes: " << file_offset+file_size << LL_ENDL; -/* S32 bytes_written = LLAPRFile::writeEx(filename, - mWriteData + TEXTURE_CACHE_ENTRY_SIZE, - 0, file_size, - mCache->getLocalAPRFilePool()); */ S32 bytes_written = (S32)LLFile::write(filename, mWriteData + TEXTURE_CACHE_ENTRY_SIZE, 0, file_size); if (bytes_written <= 0) { @@ -1083,8 +1063,6 @@ void LLTextureCache::readEntriesHeader() llassert_always(mHeaderAPRFile == NULL); if (LLFile::isfile(mHeaderEntriesFileName)) { -// LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), -// mHeaderAPRFilePoolp); LLFile::read(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); } else //create an empty entries header. @@ -1116,8 +1094,6 @@ void LLTextureCache::writeEntriesHeader() llassert_always(mHeaderAPRFile == NULL); if (!mReadOnly) { -// LLAPRFile::writeEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), -// mHeaderAPRFilePoolp); LLFile::write(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); } } -- cgit v1.3 From 2438854ea33d0a2f8d9fb13d2e3ef6f7d9bda235 Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Thu, 4 Dec 2025 00:10:42 +0100 Subject: Incorporate some of the comment improvements suggested by Copilot and make LLFile::size() return -1 on error and adjust the callers to account for that. --- indra/llaudio/llaudiodecodemgr.cpp | 2 +- indra/llcommon/llfile.cpp | 58 ++++++++++++++++++---------------- indra/llcommon/llfile.h | 10 +++--- indra/llfilesystem/llfilesystem.cpp | 19 +++++++---- indra/llfilesystem/llfilesystem.h | 4 +-- indra/llmessage/llassetstorage.cpp | 2 +- indra/newview/lltexturecache.cpp | 5 +-- indra/newview/llviewerassetstorage.cpp | 2 +- indra/test/llfile_tut.cpp | 1 - 9 files changed, 53 insertions(+), 50 deletions(-) (limited to 'indra') diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index d8a6fffea6..232b429130 100755 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -199,7 +199,7 @@ bool LLVorbisDecodeState::initDecode() LL_DEBUGS("AudioEngine") << "Initing decode from vfile: " << mUUID << LL_ENDL; mInFilep = new LLFileSystem(mUUID, LLAssetType::AT_SOUND); - if (!mInFilep || !mInFilep->getSize()) + if (!mInFilep || mInFilep->getSize() <= 0) { LL_WARNS("AudioEngine") << "unable to open vorbis source vfile for reading" << LL_ENDL; delete mInFilep; diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 0751bde07c..3e469111fa 100755 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -226,7 +226,7 @@ static void find_locking_process(const std::string& filename) } #endif // LL_WINDOWS hack to identify processes holding file open -static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0) +static int warnif(const std::string& desc, const std::string& filename, int rc, int suppress_warning = 0) { if (rc < 0) { @@ -236,7 +236,7 @@ static int warnif(const std::string& desc, const std::string& filename, int rc, // For certain operations, a particular errno value might be // acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit // EEXIST. Don't log a warning if caller explicitly says this errno is okay. - if (errn != accept) + if (errn != suppress_warning) { LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL; } @@ -252,12 +252,12 @@ static int warnif(const std::string& desc, const std::string& filename, int rc, return rc; } -static int warnif(const std::string& desc, const std::string& filename, std::error_code& ec, int accept = 0) +static int warnif(const std::string& desc, const std::string& filename, const std::error_code& ec, int suppress_warning = 0) { if (ec) { - // get Posix errno from the std::error_code so we can compare it to the accept parameter to see - // when a caller wants us to not generate a warning for a particular error code + // get Posix errno from the std::error_code so we can compare it to the suppress_warning parameter + // to see when a caller wants us to not generate a warning for a particular error code #if LL_WINDOWS int errn = get_errno_from_oserror(ec.value()); #else @@ -265,7 +265,7 @@ static int warnif(const std::string& desc, const std::string& filename, std::err #endif // For certain operations, a particular errno value might be acceptable // Don't warn if caller explicitly says this errno is okay. - if (errn != accept) + if (errn != suppress_warning) { LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << ec.message() << LL_ENDL; } @@ -511,12 +511,13 @@ int LLFile::open(const std::string& filename, std::ios_base::openmode omode, std std::wstring file_path = utf8StringToWstring(filename); mHandle = CreateFileW(file_path.c_str(), access, share, nullptr, create, attributes, nullptr); + // The dwShareMode = share parameter takes care of locking the file for other processes if indicated, + // no need to do anything else for file locking here #else int oflags = decode_open_mode(omode); - int lmode = LLFile::noblock | (omode & LLFile::lock_mask); + int lmode = omode & LLFile::lock_mask; mHandle = ::open(filename.c_str(), oflags, perm); - if (mHandle != InvalidHandle && omode & LLFile::lock_mask && - lock(omode | LLFile::noblock, ec) != 0) + if (mHandle != InvalidHandle && lmode && lock(lmode | LLFile::noblock, ec) != 0) { close(); return -1; @@ -611,8 +612,7 @@ S64 LLFile::read(void* buffer, S64 nbytes, std::error_code& ec) if (nbytes == 0) { // Nothing to do - clear_error(ec); - return 0; + return clear_error(ec); } else if (!buffer || nbytes < 0) { @@ -652,8 +652,7 @@ S64 LLFile::write(const void* buffer, S64 nbytes, std::error_code& ec) if (nbytes == 0) { // Nothing to do here - clear_error(ec); - return 0; + return clear_error(ec); } else if (!buffer || nbytes < 0) { @@ -699,7 +698,7 @@ S64 LLFile::printf(const char* fmt, ...) va_start(args1, fmt); va_list args2; va_copy(args2, args1); - int length = vsnprintf(NULL, 0, fmt, args1); + int length = vsnprintf(nullptr, 0, fmt, args1); va_end(args1); if (length < 0) { @@ -806,7 +805,7 @@ LLFILE* LLFile::fopen(const std::string& filename, const char* mode, int lmode) // Rather fail on a sharing conflict than block if (flock(fileno(file), decode_lock_mode(lmode | LLFile::noblock))) { - LLFile::close(file); + ::fclose(file); file = nullptr; } } @@ -820,13 +819,7 @@ int LLFile::close(LLFILE* file) int ret_value = 0; if (file) { - // Read the current errno and restore it if it was not 0 - int errn = errno; ret_value = ::fclose(file); - if (errn) - { - errno = errn; - } } return ret_value; } @@ -874,22 +867,22 @@ int LLFile::mkdir(const std::string& dirname) } // static -int LLFile::remove(const std::string& filename, int suppress_error) +int LLFile::remove(const std::string& filename, int suppress_warning) { std::error_code ec; std::filesystem::path file_path = utf8StringToPath(filename); std::filesystem::remove(file_path, ec); - return warnif("remove", filename, ec, suppress_error); + return warnif("remove", filename, ec, suppress_warning); } // static -int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error) +int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_warning) { std::error_code ec; std::filesystem::path file_path = utf8StringToPath(filename); std::filesystem::path new_path = utf8StringToPath(newname); std::filesystem::rename(file_path, new_path, ec); - return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, ec, suppress_error); + return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, ec, suppress_warning); } // static @@ -1064,8 +1057,12 @@ S64 LLFile::size(const std::string& filename, int suppress_warning) { std::error_code ec; std::filesystem::path file_path = utf8StringToPath(filename); - std::intmax_t size = (std::intmax_t)std::filesystem::file_size(file_path, ec); - return warnif("size", filename, ec, suppress_warning) ? 0 : size; + std::intmax_t size = static_cast(std::filesystem::file_size(file_path, ec)); + if (ec) + { + return warnif("size", filename, ec, suppress_warning); + } + return size; } // static @@ -1152,7 +1149,12 @@ std::wstring LLFile::utf8StringToWstring(const std::string& pathname) utf16string.assign(L"\\\\?\\").append(utf16path); /* remove trailing spaces and dots (yes, Windows really does that) */ - return utf16string.substr(0, utf16string.find_last_not_of(L" \t.")); + size_t last_valid = utf16string.find_last_not_of(L" \t."); + if (last_valid == std::wstring::npos) + { + return std::wstring(); + } + return utf16string.substr(0, last_valid + 1); } return utf16string; } diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index f48b0f5cad..87c8b80321 100755 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -191,7 +191,7 @@ public: /// @name class member methods /// /// These methods provide read and write support as well as additional functionality to query the size of - /// the file, change the position of the the current file pointer or query it. + /// the file, change the position of the current file pointer or query it. /// /// Most of these functions take as one of their parameters a std::error_code object which can be used to /// determine in more detail what error occurred if required @@ -370,11 +370,11 @@ public: static std::time_t getModificationTime(const std::string& filename, int suppress_warning = 0); ///< @returns the modification time of the file or 0 on error - /// get the file or directory attributes for filename + /// get the std::filesystem::file_status for filename static std::filesystem::file_status getStatus(const std::string& filename, bool dontFollowSymLink = false, int suppress_warning = ENOENT); - ///< dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it - /// we pass by default ENOENT in the optional 'suppress_warning' parameter to not spam the log with - /// warnings when the file or directory does not exist + ///< dontFollowSymLinks set to true returns the std::filesystem::file_status of the symlink if it + /// is one, rather than resolving it. We pass by default ENOENT in the optional 'suppress_warning' + /// parameter to not spam the log with warnings when the file or directory does not exist /// @returns a std::filesystem::file_status value that can be passed to the appropriate std::filesystem::exists() /// and other APIs accepting a file_status. diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 087daf78be..df8b8de880 100755 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -78,15 +78,21 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type); // not only test for existence but for the file to be not empty - return LLFile::size(filename) > 0; + S64 size = LLFile::size(filename); + if (size < 0) + { + LL_WARNS() << "Failed to get size for file '" << filename << "': " << strerror(errno) << LL_ENDL; + return false; + } + return size > 0; } // static -bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error /*= 0*/) +bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_warning /*= 0*/) { const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type); - LLFile::remove(filename.c_str(), suppress_error); + LLFile::remove(filename.c_str(), suppress_warning); return true; } @@ -111,11 +117,10 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp } // static -S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) +S64 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) { const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type); - S64 fileSize = LLFile::size(filename); - return (fileSize > 0) ? (S32)fileSize : 0; + return LLFile::size(filename); } bool LLFileSystem::read(U8* buffer, S32 bytes) @@ -256,7 +261,7 @@ S32 LLFileSystem::tell() const S32 LLFileSystem::getSize() const { - return LLFileSystem::getFileSize(mFileID, mFileType); + return (S32)LLFileSystem::getFileSize(mFileID, mFileType); } S32 LLFileSystem::getMaxSize() const diff --git a/indra/llfilesystem/llfilesystem.h b/indra/llfilesystem/llfilesystem.h index 10649b6920..7188683e7f 100644 --- a/indra/llfilesystem/llfilesystem.h +++ b/indra/llfilesystem/llfilesystem.h @@ -61,10 +61,10 @@ class LLFileSystem void updateFileAccessTime(const std::string& file_path); 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 removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_warning = 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); + static S64 getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type); public: static const S32 READ; diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index 10fd56a68e..34eeacb273 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -467,7 +467,7 @@ bool LLAssetStorage::findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetTyp else { LL_WARNS("AssetStorage") << "Asset vfile " << uuid << ":" << type - << " found in static cache with bad size " << file.getSize() << ", ignoring" << LL_ENDL; + << " found in static cache with bad size " << size << ", ignoring" << LL_ENDL; } } return false; diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index ad222f229f..a6d81816ce 100755 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -181,7 +181,6 @@ bool LLTextureCacheLocalFileWorker::doRead() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; S32 local_size = (S32)LLFile::size(mFileName); - if (local_size > 0 && mFileName.size() > 4) { mDataSize = local_size; // Only a complete file is valid @@ -444,7 +443,7 @@ bool LLTextureCacheRemoteWorker::doRead() std::string filename = mCache->getTextureFileName(mID); S32 filesize = (S32)LLFile::size(filename); - if (filesize && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset) + if (filesize > 0 && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset) { S32 max_datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize - mOffset; mDataSize = llmin(max_datasize, mDataSize); @@ -934,8 +933,6 @@ const char* fast_cache_filename = "FastCache.cache"; void LLTextureCache::setDirNames(ELLPath location) { -// std::string delem = gDirUtilp->getDirDelimiter(); - mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, entries_filename); mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, cache_filename); mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname); diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 141f370ecb..e76d340eda 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -182,7 +182,7 @@ void LLViewerAssetStorage::storeAssetData( else { // LLAssetStorage metric: Successful Request - S32 size = LLFileSystem::getFileSize(asset_id, asset_type); + S32 size = (S32)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 ); diff --git a/indra/test/llfile_tut.cpp b/indra/test/llfile_tut.cpp index 9cd7fd335f..c3676cd8b3 100755 --- a/indra/test/llfile_tut.cpp +++ b/indra/test/llfile_tut.cpp @@ -84,7 +84,6 @@ namespace tut ensure("llfile_test1.dat should not yet exist", !LLFile::exists(testfile1.string())); const char* testdata = "testdata"; - std::time_t current = time(nullptr); S64 bytes = LLFile::write(testfile1.string(), testdata, 0, sizeof(testdata)); ensure("LLFile::write() did not write correctly", bytes == sizeof(testdata)); -- cgit v1.3 From 8edaa00c73e9dc44a4bafb939898e7b91a85a2d6 Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Thu, 4 Dec 2025 00:12:47 +0100 Subject: Cleanup unused LLAPRFile functions --- indra/llcommon/llapr.cpp | 102 ----------------------------------------------- indra/llcommon/llapr.h | 10 ----- 2 files changed, 112 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index cffd6b7bc0..22bed48542 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -526,108 +526,6 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset) } } -//static -S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool) -{ - LL_PROFILE_ZONE_SCOPED; - //***************************************** - LLAPRFilePoolScope scope(pool); - apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), APR_READ|APR_BINARY); - //***************************************** - if (!file_handle) - { - return 0; - } - - llassert(offset >= 0); - - if (offset > 0) - offset = LLAPRFile::seek(file_handle, APR_SET, offset); - - apr_size_t bytes_read; - if (offset < 0) - { - bytes_read = 0; - } - else - { - bytes_read = nbytes ; - apr_status_t s = apr_file_read(file_handle, buf, &bytes_read); - if (s != APR_SUCCESS) - { - LL_WARNS("APR") << " Attempting to read filename: " << filename << LL_ENDL; - ll_apr_warn_status(s); - bytes_read = 0; - } - else - { - llassert_always(bytes_read <= 0x7fffffff); - } - } - - //***************************************** - close(file_handle) ; - //***************************************** - return (S32)bytes_read; -} - -//static -S32 LLAPRFile::writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool) -{ - LL_PROFILE_ZONE_SCOPED; - apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY; - if (offset < 0) - { - flags |= APR_APPEND; - offset = 0; - } - - //***************************************** - LLAPRFilePoolScope scope(pool); - apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), flags); - //***************************************** - if (!file_handle) - { - return 0; - } - - if (offset > 0) - { - offset = LLAPRFile::seek(file_handle, APR_SET, offset); - } - - apr_size_t bytes_written; - if (offset < 0) - { - bytes_written = 0; - } - else - { - bytes_written = nbytes ; - apr_status_t s = apr_file_write(file_handle, buf, &bytes_written); - if (s != APR_SUCCESS) - { - LL_WARNS("APR") << "Attempting to write filename: " << filename << LL_ENDL; - if (APR_STATUS_IS_ENOSPC(s)) - { - LLApp::notifyOutOfDiskSpace(); - } - ll_apr_warn_status(s); - bytes_written = 0; - } - else - { - llassert_always(bytes_written <= 0x7fffffff); - } - } - - //***************************************** - LLAPRFile::close(file_handle); - //***************************************** - - return (S32)bytes_written; -} - // //end of static components of LLAPRFile //******************************************************************************************************************************* diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index 5477cfcacd..72458c6302 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -35,12 +35,6 @@ #include "llwin32headers.h" #include "apr_thread_proc.h" -#include "apr_getopt.h" -#include "apr_signal.h" - -#include "llstring.h" - -#include "mutex.h" struct apr_dso_handle_t; /** @@ -184,10 +178,6 @@ private: static apr_file_t* open(const std::string& filename, apr_pool_t* apr_pool, apr_int32_t flags); static apr_status_t close(apr_file_t* file) ; static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset); -public: - // Returns bytes read/written, 0 if read/write fails: - static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); - static S32 writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); // offset<0 means append //******************************************************************************************************************************* }; -- cgit v1.3 From c2898f7ca159d739da5174080f388b9c13c8a083 Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Thu, 4 Dec 2025 00:16:15 +0100 Subject: Improve comment and remove superfluous error_code check --- indra/llfilesystem/lldir.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp index d36df99c14..eb3c2d9909 100755 --- a/indra/llfilesystem/lldir.cpp +++ b/indra/llfilesystem/lldir.cpp @@ -30,8 +30,6 @@ #include #include #include -#else -#include #endif #include "lldir.h" @@ -43,7 +41,6 @@ #include "lldiriterator.h" #include "stringize.h" #include "llstring.h" -#include #include #include @@ -93,11 +90,11 @@ LLDir::~LLDir() std::vector LLDir::getFilesInDir(const std::string &dirname) { - //Returns a vector of fullpath filenames. + // Returns a vector of filenames in the directory. std::filesystem::path p = LLFile::utf8StringToPath(dirname); std::vector v; std::error_code ec; - if (std::filesystem::is_directory(p, ec) && !ec) + if (std::filesystem::is_directory(p, ec)) { std::filesystem::directory_iterator end_iter; for (std::filesystem::directory_iterator dir_itr(p); -- cgit v1.3 From 0fed0bfe33f3db4cb12295e6dfa404b1313afc78 Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Fri, 5 Dec 2025 16:16:26 +0100 Subject: Remove chatty warning message when checking for existence of a file --- indra/llfilesystem/llfilesystem.cpp | 5 ----- 1 file changed, 5 deletions(-) (limited to 'indra') diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index df8b8de880..0c220fe7cf 100755 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -79,11 +79,6 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil // not only test for existence but for the file to be not empty S64 size = LLFile::size(filename); - if (size < 0) - { - LL_WARNS() << "Failed to get size for file '" << filename << "': " << strerror(errno) << LL_ENDL; - return false; - } return size > 0; } -- cgit v1.3 From 23a3cdddeebcb1cd06691f81464bd059f5649650 Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Fri, 5 Dec 2025 16:32:59 +0100 Subject: Add header include for apr_signal.h that was removed from llapr.h as it was not neededanywhere else --- indra/llcommon/llapp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index c532620daa..5829d2be49 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -37,7 +37,7 @@ #endif #include "llcommon.h" -#include "llapr.h" + #include "llerrorcontrol.h" #include "llframetimer.h" #include "lllivefile.h" @@ -53,6 +53,8 @@ // // Signal handling #ifndef LL_WINDOWS +#include "apr_signal.h" + # include # include // for fork() void setup_signals(); -- cgit v1.3 From 2c8f8fd6b4cae17bf8f74e422327b052d212a7ab Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Sun, 7 Dec 2025 13:08:16 +0100 Subject: Fix typo and check for valid opened file --- indra/llrender/llshadermgr.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 49df9cd88c..0522307661 100755 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1014,7 +1014,6 @@ void LLShaderMgr::initShaderCache(bool enabled, const LLUUID& old_cache_version, mShaderCacheDir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "shader_cache"); LLFile::mkdir(mShaderCacheDir); - { std::string meta_out_path = gDirUtilp->add(mShaderCacheDir, "shaderdata.llsd"); if (gDirUtilp->fileExists(meta_out_path)) @@ -1078,7 +1077,7 @@ void LLShaderMgr::persistShaderCacheMetadata() // Settings and shader cache get saved at different time, thus making // RenderShaderCacheVersion unreliable when running multiple viewer // instances, or for cases where viewer crashes before saving settings. - // Dupplicate version to the cache itself. + // Duplicate version to the cache itself. out["version"] = mShaderCacheVersion; out["shaders"] = LLSD::emptyMap(); LLSD &shaders = out["shaders"]; @@ -1133,7 +1132,7 @@ bool LLShaderMgr::loadCachedProgramBinary(LLGLSLShader* shader) in_data.resize(shader_info.mBinaryLength); std::error_code ec; LLFile filep = LLFile(in_path, LLFile::in | LLFile::binary, ec); - if (filep) + if (!ec && (bool)filep) { size_t result = filep.read(in_data.data(), in_data.size(), ec); filep.close(); -- cgit v1.3 From 71721b8c1c7e095a3f8cbb907d9438fd846a07c7 Mon Sep 17 00:00:00 2001 From: fmartian Date: Tue, 9 Dec 2025 13:09:37 +0100 Subject: Update indra/llcommon/llfile.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- indra/llcommon/llfile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 87c8b80321..b007fcaf02 100755 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -347,7 +347,7 @@ public: /// @returns bytes written on success, or -1 on failure /// return the file stat structure for filename - static int stat(const std::string& filename, llstat* file_status, const char *fname = nullptr, int suppress_warning = ENOENT); + static int stat(const std::string& filename, llstat* file_status, const char *operation = nullptr, int suppress_warning = ENOENT); ///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the /// optional 'suppress_warning' parameter to avoid spamming the log with warnings when the API /// is used to detect if a file exists -- cgit v1.3 From 9aff5ff403ab8bba071c7a5c75b664399fbfe482 Mon Sep 17 00:00:00 2001 From: fmartian Date: Tue, 9 Dec 2025 13:09:48 +0100 Subject: Update indra/test/llfile_tut.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- indra/test/llfile_tut.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/test/llfile_tut.cpp b/indra/test/llfile_tut.cpp index c3676cd8b3..9f2a9b2988 100755 --- a/indra/test/llfile_tut.cpp +++ b/indra/test/llfile_tut.cpp @@ -6,7 +6,7 @@ * * $LicenseInfo:firstyear=2025&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2025, 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 -- cgit v1.3 From 4153d676839aff0055937d346fcee995f62083f7 Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Tue, 9 Dec 2025 22:11:14 +0100 Subject: Make function documentation more clear about the negative offset value. Improve error checking inside function and add comment about not updating the seek pointer when an offset of 0 is specified --- indra/llcommon/llfile.cpp | 12 ++++++------ indra/llcommon/llfile.h | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 3e469111fa..a1d41cdf73 100755 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -911,16 +911,16 @@ S64 LLFile::read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::ios_base::openmode omode = LLFile::in | LLFile::binary; LLFile file(filename, omode, ec); - if (file) + if (!ec && (bool)file) { - S64 bytes_read = 0; if (offset > 0) { file.seek(offset, ec); } + // else (offset == 0) file was just opened and should already be at 0. if (!ec) { - bytes_read = file.read(buf, nbytes, ec); + S64 bytes_read = file.read(buf, nbytes, ec); if (!ec) { return bytes_read; @@ -961,16 +961,16 @@ S64 LLFile::write(const std::string& filename, const void* buf, S64 offset, S64 } LLFile file(filename, omode, ec); - if (file) + if (!ec && (bool)file) { - S64 bytes_written = 0; if (offset > 0) { file.seek(offset, ec); } + // else (offset == 0) we are not appending, file was just opened and should already be at 0. if (!ec) { - bytes_written = file.write(buf, nbytes, ec); + S64 bytes_written = file.write(buf, nbytes, ec); if (!ec) { return bytes_written; diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index b007fcaf02..2c74c97d0b 100755 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -343,7 +343,8 @@ public: /// write nBytes from the buffer into the file, starting at offset in the file static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes); static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec); - ///< A negative offset will append the data to the end of the file + ///< If a negative offset is provided, the file is opened in append mode and the + /// write will be appended to the end of the file. /// @returns bytes written on success, or -1 on failure /// return the file stat structure for filename -- cgit v1.3 From 88b6765dd929cb13ee256160d67665571d04ca3b Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Tue, 9 Dec 2025 22:45:29 +0100 Subject: Generate error if the asset upload is bigger than INT_MAX as recommended by Copilot --- indra/newview/llviewerassetupload.cpp | 39 +++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'indra') diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 69c63a6ac8..2773cd73c3 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -45,11 +45,7 @@ #include "llappviewer.h" #include "llviewerstats.h" #include "llfilesystem.h" -#include "llgesturemgr.h" -#include "llpreviewnotecard.h" -#include "llpreviewgesture.h" #include "llcoproceduremanager.h" -#include "llthread.h" #include "llkeyframemotion.h" #include "lldatapacker.h" #include "llvoavatarself.h" @@ -405,6 +401,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() std::string errorMessage; std::string errorLabel; + std::error_code ec; bool error = false; @@ -475,30 +472,38 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() error = true; // read from getFileName() - LLAPRFile infile; - infile.open(getFileName(),LL_APR_RB); - if (!infile.getFileHandle()) + LLFile infile(getFileName(), LLFile::in | LLFile::binary, ec); + if (ec || !infile) { LL_WARNS() << "Couldn't open file for reading: " << getFileName() << LL_ENDL; errorMessage = llformat("Failed to open animation file %s\n", getFileName().c_str()); } else { - S32 size = (S32)LLFile::size(getFileName()); + S64 size = infile.size(ec); + if (ec || size <= 0) + { + LLError::LLUserWarningMsg::showMissingFiles(); + LL_ERRS() << "Invalid file" << LL_ENDL; + } + else if (size > INT_MAX) + { + LL_ERRS() << "File is to big, size: " << size << LL_ENDL; + } U8* buffer = new(std::nothrow) U8[size]; if (!buffer) { LLError::LLUserWarningMsg::showOutOfMemory(); LL_ERRS() << "Bad memory allocation for buffer, size: " << size << LL_ENDL; } - S32 size_read = infile.read(buffer,size); - if (size_read != size) + S64 size_read = infile.read(buffer, size, ec); + if (ec || size_read != size) { errorMessage = llformat("Failed to read animation file %s: wanted %d bytes, got %d\n", getFileName().c_str(), size, size_read); } else { - LLDataPackerBinaryBuffer dp(buffer, size); + LLDataPackerBinaryBuffer dp(buffer, (S32)size); LLKeyframeMotion *motionp = new LLKeyframeMotion(getAssetId()); motionp->setCharacter(gAgentAvatarp); if (motionp->deserialize(dp, getAssetId(), false)) @@ -544,18 +549,17 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() setAssetType(assetType); // copy this file into the cache for upload - S32 file_size; - LLAPRFile infile; - infile.open(filename, LL_APR_RB, NULL, &file_size); - if (infile.getFileHandle()) + LLFile infile(filename, LLFile::in | LLFile::binary, ec); + if (!ec && infile.size(ec) > 0) { LLFileSystem file(getAssetId(), assetType, LLFileSystem::APPEND); + S64 read_bytes; const S32 buf_size = 65536; U8 copy_buf[buf_size]; - while ((file_size = infile.read(copy_buf, buf_size))) + while (((read_bytes = infile.read(copy_buf, buf_size, ec))) > 0) { - file.write(copy_buf, file_size); + file.write(copy_buf, (S32)read_bytes); } } else @@ -569,7 +573,6 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() } return LLSD(); - } //========================================================================= -- cgit v1.3 From c894a74d1858eb555c8340517c1997c298d4e999 Mon Sep 17 00:00:00 2001 From: fmartian Date: Wed, 10 Dec 2025 18:11:21 +0100 Subject: Fix typo found by Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- indra/newview/llviewerassetupload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 2773cd73c3..5ac7d6b1fe 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -488,7 +488,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() } else if (size > INT_MAX) { - LL_ERRS() << "File is to big, size: " << size << LL_ENDL; + LL_ERRS() << "File is too big, size: " << size << LL_ENDL; } U8* buffer = new(std::nothrow) U8[size]; if (!buffer) -- cgit v1.3 From f48be9ff9be34793ab3686d935297bd9f88921cd Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 10 Dec 2025 20:37:49 +0200 Subject: p#512 Block ability to accidentaly purge Inventory items outside of trash Purge menu is trash specifc and shouldn't touch other items --- indra/newview/llinventorypanel.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index cde87ede9b..8d57a4e7bb 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -1849,6 +1849,7 @@ void LLInventoryPanel::purgeSelectedItems() { if (!mFolderRoot.get()) return; + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); const std::set inventory_selected = mFolderRoot.get()->getSelectionList(); if (inventory_selected.empty()) return; LLSD args; @@ -1858,12 +1859,17 @@ void LLInventoryPanel::purgeSelectedItems() it != end_it; ++it) { + // Selection allows items outside trash folder, only count the ones inside. LLUUID item_id = static_cast((*it)->getViewModelItem())->getUUID(); - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(item_id, cats, items, LLInventoryModel::INCLUDE_TRASH); - count += items.size() + cats.size(); - selected_items.push_back(item_id); + LLInventoryObject* obj = gInventory.getObject(item_id); + if (obj->getParentUUID() == trash_id) + { + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(item_id, cats, items, LLInventoryModel::INCLUDE_TRASH); + count += items.size() + cats.size(); + selected_items.push_back(item_id); + } } args["COUNT"] = static_cast(count); LLNotificationsUtil::add("PurgeSelectedItems", args, LLSD(), boost::bind(callbackPurgeSelectedItems, _1, _2, selected_items)); -- cgit v1.3 From c0043a9951b90a4b67c23dae7fbb5399b18b045b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 10 Dec 2025 21:22:15 +0200 Subject: #5138 Added test case for multiple columns in a combo box --- .../skins/default/xui/en/floater_test_combobox.xml | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/floater_test_combobox.xml b/indra/newview/skins/default/xui/en/floater_test_combobox.xml index 4211d624ac..0d75685223 100644 --- a/indra/newview/skins/default/xui/en/floater_test_combobox.xml +++ b/indra/newview/skins/default/xui/en/floater_test_combobox.xml @@ -155,4 +155,36 @@ name="item3" value="tx" /> + + Multiple columns: + + + + + + + + + + + + + + + + + -- cgit v1.3 From dbbce566e7d66c907dde7bd6c4212b0954b9a5e1 Mon Sep 17 00:00:00 2001 From: Zi Ree <81702435+zi-ree@users.noreply.github.com> Date: Wed, 10 Dec 2025 20:37:24 +0100 Subject: #5138 fix multi column comboboxes not honoring width parameter --- indra/llui/llscrolllistctrl.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra') diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index b459c67dad..558ce6a7fd 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -3185,6 +3185,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS if (cell_p.width.isProvided()) { new_column.width.pixel_width = cell_p.width; + new_column.width.pixel_width.choose(); } addColumn(new_column); columnp = mColumns[column]; -- cgit v1.3 From c92b0b74cbd963cd79d1cb7754256b801f1479b1 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Thu, 11 Dec 2025 01:42:52 +0200 Subject: Revert #4899 "Add more functionality to LLFile and cleanup LLAPRFile" Interferes with linux work, will be moved to a different branch and applied separately. --- indra/llaudio/llaudiodecodemgr.cpp | 2 +- indra/llcommon/llapp.cpp | 4 +- indra/llcommon/llapr.cpp | 220 ++++++ indra/llcommon/llapr.h | 19 + indra/llcommon/llerror.cpp | 10 +- indra/llcommon/llfile.cpp | 1136 ++++++++----------------------- indra/llcommon/llfile.h | 488 ++++--------- indra/llcrashlogger/llcrashlock.cpp | 7 +- indra/llfilesystem/lldir.cpp | 56 +- indra/llfilesystem/lldir_win32.cpp | 13 +- indra/llfilesystem/llfilesystem.cpp | 29 +- indra/llfilesystem/llfilesystem.h | 4 +- indra/llfilesystem/tests/lldir_test.cpp | 4 +- indra/llmessage/llassetstorage.cpp | 2 +- indra/llrender/llshadermgr.cpp | 22 +- indra/llxml/tests/llcontrol_test.cpp | 2 +- indra/newview/llappviewer.cpp | 28 +- indra/newview/llfloaterpreference.cpp | 12 +- indra/newview/llfloateruipreview.cpp | 37 +- indra/newview/llpreviewnotecard.cpp | 3 +- indra/newview/llpreviewscript.cpp | 3 +- indra/newview/lltexturecache.cpp | 89 ++- indra/newview/llviewerassetstorage.cpp | 2 +- indra/newview/llviewerassetupload.cpp | 39 +- indra/newview/llviewerdisplay.cpp | 5 +- indra/newview/llviewermedia.cpp | 5 +- indra/newview/llvocache.cpp | 6 +- indra/newview/llvoicevivox.cpp | 3 +- indra/test/CMakeLists.txt | 13 +- indra/test/llfile_tut.cpp | 253 ------- indra/test/llmessageconfig_tut.cpp | 2 +- indra/test/message_tut.cpp | 2 +- 32 files changed, 910 insertions(+), 1610 deletions(-) mode change 100755 => 100644 indra/llcommon/llerror.cpp mode change 100755 => 100644 indra/llcommon/llfile.cpp mode change 100755 => 100644 indra/llcommon/llfile.h mode change 100755 => 100644 indra/llcrashlogger/llcrashlock.cpp mode change 100755 => 100644 indra/llfilesystem/lldir.cpp mode change 100755 => 100644 indra/llfilesystem/lldir_win32.cpp mode change 100755 => 100644 indra/llfilesystem/llfilesystem.cpp mode change 100755 => 100644 indra/llfilesystem/tests/lldir_test.cpp mode change 100755 => 100644 indra/llrender/llshadermgr.cpp mode change 100755 => 100644 indra/llxml/tests/llcontrol_test.cpp mode change 100755 => 100644 indra/newview/llappviewer.cpp mode change 100755 => 100644 indra/newview/llfloaterpreference.cpp mode change 100755 => 100644 indra/newview/llfloateruipreview.cpp mode change 100755 => 100644 indra/newview/llpreviewnotecard.cpp mode change 100755 => 100644 indra/newview/llpreviewscript.cpp mode change 100755 => 100644 indra/newview/lltexturecache.cpp mode change 100755 => 100644 indra/newview/llviewerdisplay.cpp mode change 100755 => 100644 indra/newview/llviewermedia.cpp mode change 100755 => 100644 indra/newview/llvocache.cpp mode change 100755 => 100644 indra/newview/llvoicevivox.cpp mode change 100755 => 100644 indra/test/CMakeLists.txt delete mode 100755 indra/test/llfile_tut.cpp mode change 100755 => 100644 indra/test/llmessageconfig_tut.cpp mode change 100755 => 100644 indra/test/message_tut.cpp (limited to 'indra') diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index 232b429130..d8a6fffea6 100755 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -199,7 +199,7 @@ bool LLVorbisDecodeState::initDecode() LL_DEBUGS("AudioEngine") << "Initing decode from vfile: " << mUUID << LL_ENDL; mInFilep = new LLFileSystem(mUUID, LLAssetType::AT_SOUND); - if (!mInFilep || mInFilep->getSize() <= 0) + if (!mInFilep || !mInFilep->getSize()) { LL_WARNS("AudioEngine") << "unable to open vorbis source vfile for reading" << LL_ENDL; delete mInFilep; diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 5829d2be49..c532620daa 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -37,7 +37,7 @@ #endif #include "llcommon.h" - +#include "llapr.h" #include "llerrorcontrol.h" #include "llframetimer.h" #include "lllivefile.h" @@ -53,8 +53,6 @@ // // Signal handling #ifndef LL_WINDOWS -#include "apr_signal.h" - # include # include // for fork() void setup_signals(); diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index 22bed48542..eeff2694a7 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -526,6 +526,226 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset) } } +//static +S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool) +{ + LL_PROFILE_ZONE_SCOPED; + //***************************************** + LLAPRFilePoolScope scope(pool); + apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), APR_READ|APR_BINARY); + //***************************************** + if (!file_handle) + { + return 0; + } + + llassert(offset >= 0); + + if (offset > 0) + offset = LLAPRFile::seek(file_handle, APR_SET, offset); + + apr_size_t bytes_read; + if (offset < 0) + { + bytes_read = 0; + } + else + { + bytes_read = nbytes ; + apr_status_t s = apr_file_read(file_handle, buf, &bytes_read); + if (s != APR_SUCCESS) + { + LL_WARNS("APR") << " Attempting to read filename: " << filename << LL_ENDL; + ll_apr_warn_status(s); + bytes_read = 0; + } + else + { + llassert_always(bytes_read <= 0x7fffffff); + } + } + + //***************************************** + close(file_handle) ; + //***************************************** + return (S32)bytes_read; +} + +//static +S32 LLAPRFile::writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool) +{ + LL_PROFILE_ZONE_SCOPED; + apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY; + if (offset < 0) + { + flags |= APR_APPEND; + offset = 0; + } + + //***************************************** + LLAPRFilePoolScope scope(pool); + apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), flags); + //***************************************** + if (!file_handle) + { + return 0; + } + + if (offset > 0) + { + offset = LLAPRFile::seek(file_handle, APR_SET, offset); + } + + apr_size_t bytes_written; + if (offset < 0) + { + bytes_written = 0; + } + else + { + bytes_written = nbytes ; + apr_status_t s = apr_file_write(file_handle, buf, &bytes_written); + if (s != APR_SUCCESS) + { + LL_WARNS("APR") << "Attempting to write filename: " << filename << LL_ENDL; + if (APR_STATUS_IS_ENOSPC(s)) + { + LLApp::notifyOutOfDiskSpace(); + } + ll_apr_warn_status(s); + bytes_written = 0; + } + else + { + llassert_always(bytes_written <= 0x7fffffff); + } + } + + //***************************************** + LLAPRFile::close(file_handle); + //***************************************** + + return (S32)bytes_written; +} + +//static +bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool) +{ + apr_status_t s; + + LLAPRFilePoolScope scope(pool); + s = apr_file_remove(filename.c_str(), scope.getVolatileAPRPool()); + + if (s != APR_SUCCESS) + { + ll_apr_warn_status(s); + LL_WARNS("APR") << " Attempting to remove filename: " << filename << LL_ENDL; + return false; + } + return true; +} + +//static +bool LLAPRFile::rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool) +{ + apr_status_t s; + + LLAPRFilePoolScope scope(pool); + s = apr_file_rename(filename.c_str(), newname.c_str(), scope.getVolatileAPRPool()); + + if (s != APR_SUCCESS) + { + ll_apr_warn_status(s); + LL_WARNS("APR") << " Attempting to rename filename: " << filename << LL_ENDL; + return false; + } + return true; +} + +//static +bool LLAPRFile::isExist(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags) +{ + apr_file_t* apr_file; + apr_status_t s; + + LLAPRFilePoolScope scope(pool); + s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, scope.getVolatileAPRPool()); + + if (s != APR_SUCCESS || !apr_file) + { + return false; + } + else + { + apr_file_close(apr_file) ; + return true; + } +} + +//static +S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool) +{ + apr_file_t* apr_file; + apr_finfo_t info; + apr_status_t s; + + LLAPRFilePoolScope scope(pool); + s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, scope.getVolatileAPRPool()); + + if (s != APR_SUCCESS || !apr_file) + { + return 0; + } + else + { + apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file); + + apr_file_close(apr_file) ; + + if (s == APR_SUCCESS) + { + return (S32)info.size; + } + else + { + return 0; + } + } +} + +//static +bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool) +{ + apr_status_t s; + + LLAPRFilePoolScope scope(pool); + s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, scope.getVolatileAPRPool()); + + if (s != APR_SUCCESS) + { + ll_apr_warn_status(s); + LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL; + return false; + } + return true; +} + +//static +bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool) +{ + apr_status_t s; + + LLAPRFilePoolScope scope(pool); + s = apr_file_remove(dirname.c_str(), scope.getVolatileAPRPool()); + + if (s != APR_SUCCESS) + { + ll_apr_warn_status(s); + LL_WARNS("APR") << " Attempting to remove directory: " << dirname << LL_ENDL; + return false; + } + return true; +} // //end of static components of LLAPRFile //******************************************************************************************************************************* diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index 72458c6302..11e474b5dd 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -35,6 +35,12 @@ #include "llwin32headers.h" #include "apr_thread_proc.h" +#include "apr_getopt.h" +#include "apr_signal.h" + +#include "llstring.h" + +#include "mutex.h" struct apr_dso_handle_t; /** @@ -178,7 +184,20 @@ private: static apr_file_t* open(const std::string& filename, apr_pool_t* apr_pool, apr_int32_t flags); static apr_status_t close(apr_file_t* file) ; static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset); +public: + // returns false if failure: + static bool remove(const std::string& filename, LLVolatileAPRPool* pool = NULL); + static bool rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool = NULL); + static bool isExist(const std::string& filename, LLVolatileAPRPool* pool = NULL, apr_int32_t flags = APR_READ); + static S32 size(const std::string& filename, LLVolatileAPRPool* pool = NULL); + static bool makeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL); + static bool removeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL); + + // Returns bytes read/written, 0 if read/write fails: + static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); + static S32 writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); // offset<0 means append //******************************************************************************************************************************* }; + #endif // LL_LLAPR_H diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp old mode 100755 new mode 100644 index bfa8bca224..b14464382b --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -435,9 +435,13 @@ namespace std::string file = user_dir + "/logcontrol-dev.xml"; - if (!LLFile::isfile(file)) - { - file = app_dir + "/logcontrol.xml"; + llstat stat_info; + if (LLFile::stat(file, &stat_info)) { + // NB: stat returns non-zero if it can't read the file, for example + // if it doesn't exist. LLFile has no better abstraction for + // testing for file existence. + + file = app_dir + "/logcontrol.xml"; } return * new LogControlFile(file); // NB: This instance is never freed diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp old mode 100755 new mode 100644 index a1d41cdf73..a539e4fe28 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -29,17 +29,22 @@ #include "linden_common.h" #include "llfile.h" +#include "llstring.h" #include "llerror.h" #include "stringize.h" #if LL_WINDOWS -#include +#include "llwin32headers.h" +#include #else #include -#include #endif -// Some of the methods below use OS-level functions that mess with errno. Wrap +using namespace std; + +static std::string empty; + +// Many of the methods below use OS-level functions that mess with errno. Wrap // variants of strerror() to report errors. #if LL_WINDOWS @@ -74,7 +79,6 @@ static errentry const errtable[] { ERROR_CURRENT_DIRECTORY, EACCES }, // 16 { ERROR_NOT_SAME_DEVICE, EXDEV }, // 17 { ERROR_NO_MORE_FILES, ENOENT }, // 18 - { ERROR_SHARING_VIOLATION, EACCES }, // 32 { ERROR_LOCK_VIOLATION, EACCES }, // 33 { ERROR_BAD_NETPATH, ENOENT }, // 53 { ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65 @@ -105,25 +109,22 @@ static errentry const errtable[] { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816 }; -static int get_errno_from_oserror(int oserr) +static int set_errno_from_oserror(unsigned long oserr) { if (!oserr) return 0; // Check the table for the Windows OS error code - for (const struct errentry& entry : errtable) + for (const struct errentry &entry : errtable) { if (oserr == entry.oserr) { - return entry.errcode; + _set_errno(entry.errcode); + return -1; } } - return EINVAL; -} -static int set_errno_from_oserror(unsigned long oserr) -{ - _set_errno(get_errno_from_oserror(oserr)); + _set_errno(EINVAL); return -1; } @@ -135,8 +136,69 @@ std::string strerr(int errn) return buffer; } -#else +inline bool is_slash(wchar_t const c) +{ + return c == L'\\' || c == L'/'; +} +static std::wstring utf8path_to_wstring(const std::string& utf8path) +{ + if (utf8path.size() >= MAX_PATH) + { + // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names + std::wstring utf16path = L"\\\\?\\" + ll_convert(utf8path); + // We need to make sure that the path does not contain forward slashes as above + // prefix does bypass the path normalization that replaces slashes with backslashes + // before passing the path to kernel mode APIs + std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\'); + return utf16path; + } + return ll_convert(utf8path); +} + +static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false) +{ + unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS; + if (dontFollowSymLink) + { + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + } + HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, OPEN_EXISTING, flags, nullptr); + if (file_handle != INVALID_HANDLE_VALUE) + { + FILE_ATTRIBUTE_TAG_INFO attribute_info; + if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info))) + { + // A volume path alone (only drive letter) is not recognized as directory while it technically is + bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || + (iswalpha(utf16path[0]) && utf16path[1] == ':' && + (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3]))); + unsigned short st_mode = is_directory ? S_IFDIR : + (attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG); + st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE; + // we do not try to guess executable flag + + // propagate user bits to group/other fields: + st_mode |= (st_mode & 0700) >> 3; + st_mode |= (st_mode & 0700) >> 6; + + CloseHandle(file_handle); + return st_mode; + } + } + // Retrieve last error and set errno before calling CloseHandle() + set_errno_from_oserror(GetLastError()); + + if (file_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(file_handle); + } + return 0; +} + +#else // On Posix we want to call strerror_r(), but alarmingly, there are two // different variants. The one that returns int always populates the passed // buffer (except in case of error), whereas the other one always returns a @@ -183,50 +245,9 @@ std::string strerr(int errn) return message_from(errn, buffer, sizeof(buffer), strerror_r(errn, buffer, sizeof(buffer))); } - #endif // ! LL_WINDOWS -#if LL_WINDOWS && 0 // turn on to debug file-locking problems -#define PROCESS_LOCKING_CHECK 1 -static void find_locking_process(const std::string& filename) -{ - // Only do any of this stuff (before LL_ENDL) if it will be logged. - LL_DEBUGS("LLFile") << ""; - // wrong way - std::string TEMP = LLFile::tmpdir(); - if (TEMP.empty()) - { - LL_CONT << "No $TEMP, not running 'handle'"; - } - else - { - std::string tf(TEMP); - tf += "\\handle.tmp"; - // http://technet.microsoft.com/en-us/sysinternals/bb896655 - std::string cmd(STRINGIZE("handle \"" << filename - // "openfiles /query /v | fgrep -i \"" << filename - << "\" > \"" << tf << '"')); - LL_CONT << cmd; - if (system(cmd.c_str()) != 0) - { - LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655"; - } - else - { - std::ifstream inf(tf); - std::string line; - while (std::getline(inf, line)) - { - LL_CONT << '\n' << line; - } - } - LLFile::remove(tf); - } - LL_CONT << LL_ENDL; -} -#endif // LL_WINDOWS hack to identify processes holding file open - -static int warnif(const std::string& desc, const std::string& filename, int rc, int suppress_warning = 0) +static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0) { if (rc < 0) { @@ -235,930 +256,319 @@ static int warnif(const std::string& desc, const std::string& filename, int rc, // For certain operations, a particular errno value might be // acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit - // EEXIST. Don't log a warning if caller explicitly says this errno is okay. - if (errn != suppress_warning) + // EEXIST. Don't warn if caller explicitly says this errno is okay. + if (errn != accept) { - LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL; + LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename + << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL; } -#if PROCESS_LOCKING_CHECK +#if 0 && LL_WINDOWS // turn on to debug file-locking problems // If the problem is "Permission denied," maybe it's because another // process has the file open. Try to find out. - if (errn == EACCES) // *not* EPERM + if (errn == EACCES) // *not* EPERM { - find_locking_process(filename); + // Only do any of this stuff (before LL_ENDL) if it will be logged. + LL_DEBUGS("LLFile") << empty; + // would be nice to use LLDir for this, but dependency goes the + // wrong way + const char* TEMP = LLFile::tmpdir(); + if (! (TEMP && *TEMP)) + { + LL_CONT << "No $TEMP, not running 'handle'"; + } + else + { + std::string tf(TEMP); + tf += "\\handle.tmp"; + // http://technet.microsoft.com/en-us/sysinternals/bb896655 + std::string cmd(STRINGIZE("handle \"" << filename + // "openfiles /query /v | fgrep -i \"" << filename + << "\" > \"" << tf << '"')); + LL_CONT << cmd; + if (system(cmd.c_str()) != 0) + { + LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655"; + } + else + { + std::ifstream inf(tf); + std::string line; + while (std::getline(inf, line)) + { + LL_CONT << '\n' << line; + } + } + LLFile::remove(tf); + } + LL_CONT << LL_ENDL; } -#endif +#endif // LL_WINDOWS hack to identify processes holding file open } return rc; } -static int warnif(const std::string& desc, const std::string& filename, const std::error_code& ec, int suppress_warning = 0) +// static +int LLFile::mkdir(const std::string& dirname, int perms) { - if (ec) - { - // get Posix errno from the std::error_code so we can compare it to the suppress_warning parameter - // to see when a caller wants us to not generate a warning for a particular error code + // We often use mkdir() to ensure the existence of a directory that might + // already exist. There is no known case in which we want to call out as + // an error the requested directory already existing. #if LL_WINDOWS - int errn = get_errno_from_oserror(ec.value()); -#else - int errn = ec.value(); -#endif - // For certain operations, a particular errno value might be acceptable - // Don't warn if caller explicitly says this errno is okay. - if (errn != suppress_warning) - { - LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << ec.message() << LL_ENDL; - } -#if PROCESS_LOCKING_CHECK - // Try to detect locked files by other processes - if (ec.value() == ERROR_SHARING_VIOLATION || ec.value() == ERROR_LOCK_VIOLATION) + // permissions are ignored on Windows + int rc = 0; + std::wstring utf16dirname = utf8path_to_wstring(dirname); + if (!CreateDirectoryW(utf16dirname.c_str(), nullptr)) + { + // Only treat other errors than an already existing file as a real error + unsigned long oserr = GetLastError(); + if (oserr != ERROR_ALREADY_EXISTS) { - find_locking_process(filename); + rc = set_errno_from_oserror(oserr); } -#endif - return -1; - } - return 0; -} - -#if LL_WINDOWS - -inline int set_ec_from_system_error(std::error_code& ec, DWORD error) -{ - ec.assign(error, std::system_category()); - return -1; -} - -static int set_ec_from_system_error(std::error_code& ec) -{ - return set_ec_from_system_error(ec, GetLastError()); -} - -inline int set_ec_to_parameter_error(std::error_code& ec) -{ - return set_ec_from_system_error(ec, ERROR_INVALID_PARAMETER); -} - -inline int set_ec_to_outofmemory_error(std::error_code& ec) -{ - return set_ec_from_system_error(ec, ERROR_NOT_ENOUGH_MEMORY); -} - -inline DWORD decode_access_mode(std::ios_base::openmode omode) -{ - switch (omode & (LLFile::in | LLFile::out)) - { - case LLFile::in: - return GENERIC_READ; - case LLFile::out: - return GENERIC_WRITE; - case LLFile::in | LLFile::out: - return GENERIC_READ | GENERIC_WRITE; - } - if (omode & LLFile::app) - { - return GENERIC_WRITE; - } - return 0; -} - -inline DWORD decode_open_create_flags(std::ios_base::openmode omode) -{ - if (omode & LLFile::noreplace) - { - return CREATE_NEW; // create if it does not exist, otherwise fail - } - if (omode & LLFile::trunc) - { - if (!(omode & LLFile::out)) - { - return TRUNCATE_EXISTING; // open and truncate if it exists, otherwise fail - } - return CREATE_ALWAYS; // open and truncate if it exists, otherwise create it - } - if (!(omode & LLFile::out)) - { - return OPEN_EXISTING; // open if it exists, otherwise fail - } - // LLFile::app or (LLFile::out and (!LLFile::trunc or !LLFile::noreplace)) - return OPEN_ALWAYS; // open if it exists, otherwise create it -} - -inline DWORD decode_share_mode(int omode) -{ - if (omode & LLFile::exclusive) - { - return 0; // allow no other access - } - if (omode & LLFile::shared) - { - return FILE_SHARE_READ; // allow read access } - return FILE_SHARE_READ | FILE_SHARE_WRITE; // allow read and write access to others -} - -inline DWORD decode_attributes(std::ios_base::openmode omode, int perm) -{ - return (perm & S_IWRITE) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_READONLY; -} - -// Under Windows the values for the std::ios_base::seekdir constants match the according FILE_BEGIN -// and other constants but we do a programmatic translation for now to be sure -static DWORD seek_mode_from_dir(std::ios_base::seekdir seekdir) -{ - switch (seekdir) - { - case LLFile::beg: - return FILE_BEGIN; - case LLFile::cur: - return FILE_CURRENT; - case LLFile::end: - return FILE_END; - } - return FILE_BEGIN; -} - #else - -inline int set_ec_from_system_error(std::error_code& ec, int error) -{ - ec.assign(error, std::system_category()); - return -1; -} - -static int set_ec_from_system_error(std::error_code& ec) -{ - return set_ec_from_system_error(ec, errno); -} - -inline int set_ec_to_parameter_error(std::error_code& ec) -{ - return set_ec_from_system_error(ec, EINVAL); -} - -inline int set_ec_to_outofmemory_error(std::error_code& ec) -{ - return set_ec_from_system_error(ec, ENOMEM); -} - -inline int decode_access_mode(std::ios_base::openmode omode) -{ - switch (omode & (LLFile::in | LLFile::out)) + int rc = ::mkdir(dirname.c_str(), (mode_t)perms); + if (rc < 0 && errno == EEXIST) { - case LLFile::out: - return O_WRONLY; - case LLFile::in | LLFile::out: - return O_RDWR; - } - return O_RDONLY; -} - -inline int decode_open_mode(std::ios_base::openmode omode) -{ - int flags = O_CREAT | decode_access_mode(omode); - if (omode & LLFile::app) - { - flags |= O_APPEND; - } - if (omode & LLFile::trunc) - { - flags |= O_TRUNC; - } - if (omode & LLFile::binary) - { - // Not a thing under *nix - } - if (omode & LLFile::noreplace) - { - flags |= O_EXCL; - } - return flags; -} - -inline int decode_lock_mode(std::ios_base::openmode omode) -{ - int lmode = omode & LLFile::noblock ? LOCK_NB : 0; - if (omode & LLFile::lock_mask) - { - if (omode & LLFile::exclusive) - { - return lmode | LOCK_EX; - } - return lmode | LOCK_SH; - } - return lmode | LOCK_UN; -} - -// Under Linux and Mac the values for the std::ios_base::seekdir constants match the according SEEK_SET -// and other constants but we do a programmatic translation for now to be sure -inline int seek_mode_from_dir(std::ios_base::seekdir seekdir) -{ - switch (seekdir) - { - case LLFile::beg: - return SEEK_SET; - case LLFile::cur: - return SEEK_CUR; - case LLFile::end: - return SEEK_END; + // this is not the error you want, move along + return 0; } - return SEEK_SET; -} - #endif - -inline int clear_error(std::error_code& ec) -{ - ec.clear(); - return 0; -} - -inline bool are_open_mode_flags_invalid(std::ios_base::openmode omode) -{ - // at least one of input or output needs to be specified - if (!(omode & (LLFile::in | LLFile::out))) - { - return true; - } - // output must be possible for any of the extra options - if (!(omode & LLFile::out) && (omode & (LLFile::trunc | LLFile::app | LLFile::noreplace))) - { - return true; - } - // invalid combination, mutually exclusive - if ((omode & LLFile::app) && (omode & (LLFile::trunc | LLFile::noreplace))) - { - return true; - } - return false; + // anything else might be a problem + return warnif("mkdir", dirname, rc); } -//---------------------------------------------------------------------------------------- -// class member functions -//---------------------------------------------------------------------------------------- -int LLFile::open(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm) +// static +int LLFile::rmdir(const std::string& dirname, int suppress_error) { - close(ec); - if (are_open_mode_flags_invalid(omode)) - { - return set_ec_to_parameter_error(ec); - } #if LL_WINDOWS - DWORD access = decode_access_mode(omode), - share = decode_share_mode(omode), - create = decode_open_create_flags(omode), - attributes = decode_attributes(omode, perm); - - std::wstring file_path = utf8StringToWstring(filename); - mHandle = CreateFileW(file_path.c_str(), access, share, nullptr, create, attributes, nullptr); - // The dwShareMode = share parameter takes care of locking the file for other processes if indicated, - // no need to do anything else for file locking here + std::wstring utf16dirname = utf8path_to_wstring(dirname); + int rc = _wrmdir(utf16dirname.c_str()); #else - int oflags = decode_open_mode(omode); - int lmode = omode & LLFile::lock_mask; - mHandle = ::open(filename.c_str(), oflags, perm); - if (mHandle != InvalidHandle && lmode && lock(lmode | LLFile::noblock, ec) != 0) - { - close(); - return -1; - } + int rc = ::rmdir(dirname.c_str()); #endif - if (mHandle == InvalidHandle) - { - return set_ec_from_system_error(ec); - } - - if (omode & LLFile::ate && seek(0, LLFile::end, ec) != 0) - { - close(); - return -1; - } - mOpen = omode; - return clear_error(ec); + return warnif("rmdir", dirname, rc, suppress_error); } -S64 LLFile::size(std::error_code& ec) +// static +LLFILE* LLFile::fopen(const std::string& filename, const char* mode) { #if LL_WINDOWS - LARGE_INTEGER value = { 0 }; - if (GetFileSizeEx(mHandle, &value)) - { - clear_error(ec); - return value.QuadPart; - } + std::wstring utf16filename = utf8path_to_wstring(filename); + std::wstring utf16mode = ll_convert(std::string(mode)); + return _wfopen(utf16filename.c_str(), utf16mode.c_str()); #else - struct stat statval; - if (fstat(mHandle, &statval) == 0) - { - clear_error(ec); - return statval.st_size; - } + return ::fopen(filename.c_str(),mode); #endif - return set_ec_from_system_error(ec); } -S64 LLFile::tell(std::error_code& ec) +// static +int LLFile::close(LLFILE * file) { -#if LL_WINDOWS - LARGE_INTEGER value = { 0 }; - if (SetFilePointerEx(mHandle, value, &value, FILE_CURRENT)) - { - clear_error(ec); - return value.QuadPart; - } -#else - off_t offset = lseek(mHandle, 0, SEEK_CUR); - if (offset != -1) + int ret_value = 0; + if (file) { - clear_error(ec); - return offset; + ret_value = fclose(file); } -#endif - return set_ec_from_system_error(ec); -} - -int LLFile::seek(S64 pos, std::error_code& ec) -{ - return seek(pos, LLFile::beg, ec); + return ret_value; } -int LLFile::seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec) +// static +std::string LLFile::getContents(const std::string& filename) { - S64 newOffset = 0; -#if LL_WINDOWS - DWORD seekdir = seek_mode_from_dir(dir); - LARGE_INTEGER value; - value.QuadPart = offset; - if (SetFilePointerEx(mHandle, value, (PLARGE_INTEGER)&newOffset, seekdir)) -#else - newOffset = lseek(mHandle, offset, seek_mode_from_dir(dir)); - if (newOffset != -1) -#endif + LLFILE* fp = LLFile::fopen(filename, "rb"); + if (fp) { - return clear_error(ec); - } - return set_ec_from_system_error(ec); -} + fseek(fp, 0, SEEK_END); + U32 length = ftell(fp); + fseek(fp, 0, SEEK_SET); -#if LL_WINDOWS -inline DWORD next_buffer_size(S64 nbytes) -{ - return nbytes > 0x80000000 ? 0x80000000 : (DWORD)nbytes; -} -#endif + std::vector buffer(length); + size_t nread = fread(buffer.data(), 1, length, fp); + fclose(fp); -S64 LLFile::read(void* buffer, S64 nbytes, std::error_code& ec) -{ - if (nbytes == 0) - { - // Nothing to do - return clear_error(ec); + return std::string(buffer.data(), nread); } - else if (!buffer || nbytes < 0) - { - return set_ec_to_parameter_error(ec); - } -#if LL_WINDOWS - S64 totalBytes = 0; - char *ptr = (char*)buffer; - DWORD bytesRead, bytesToRead = next_buffer_size(nbytes); - // Read in chunks to support >4GB which the S64 nbytes value makes possible - while (ReadFile(mHandle, ptr, bytesToRead, &bytesRead, nullptr)) - { - totalBytes += bytesRead; - if (nbytes <= totalBytes || // requested amount read - bytesRead < bytesToRead) // ReadFile encountered eof - { - clear_error(ec); - return totalBytes; - } - ptr += bytesRead; - bytesToRead = next_buffer_size(nbytes - totalBytes); - } -#else - ssize_t bytesRead = ::read(mHandle, buffer, nbytes); - if (bytesRead != -1) - { - clear_error(ec); - return bytesRead; - } -#endif - return set_ec_from_system_error(ec); + return LLStringUtil::null; } -S64 LLFile::write(const void* buffer, S64 nbytes, std::error_code& ec) +// static +int LLFile::remove(const std::string& filename, int suppress_error) { - if (nbytes == 0) - { - // Nothing to do here - return clear_error(ec); - } - else if (!buffer || nbytes < 0) - { - return set_ec_to_parameter_error(ec); - } #if LL_WINDOWS - // If this was opened in append mode, we emulate it on Windows - if (mOpen & LLFile::app && seek(0, LLFile::end, ec) != 0) + // Posix remove() works on both files and directories although on Windows + // remove() and its wide char variant _wremove() only removes files just + // as its siblings unlink() and _wunlink(). + // If we really only want to support files we should instead use + // unlink() in the non-Windows part below too + int rc = -1; + std::wstring utf16filename = utf8path_to_wstring(filename); + unsigned short st_mode = get_fileattr(utf16filename); + if (S_ISDIR(st_mode)) { - return -1; + rc = _wrmdir(utf16filename.c_str()); } - - S64 totalBytes = 0; - char* ptr = (char*)buffer; - DWORD bytesWritten, bytesToWrite = next_buffer_size(nbytes); - - // Write in chunks to support >4GB which the S64 nbytes value makes possible - while (WriteFile(mHandle, ptr, bytesToWrite, &bytesWritten, nullptr)) + else if (S_ISREG(st_mode)) { - totalBytes += bytesWritten; - if (nbytes <= totalBytes) - { - clear_error(ec); - return totalBytes; - } - ptr += bytesWritten; - bytesToWrite = next_buffer_size(nbytes - totalBytes); + rc = _wunlink(utf16filename.c_str()); } -#else - ssize_t bytesWritten = ::write(mHandle, buffer, nbytes); - if (bytesWritten != -1) + else if (st_mode) { - clear_error(ec); - return bytesWritten; - } -#endif - return set_ec_from_system_error(ec); -} - -S64 LLFile::printf(const char* fmt, ...) -{ - va_list args1; - va_start(args1, fmt); - va_list args2; - va_copy(args2, args1); - int length = vsnprintf(nullptr, 0, fmt, args1); - va_end(args1); - if (length < 0) - { - va_end(args2); - return -1; - } - void* buffer = malloc(length + 1); - if (!buffer) - { - va_end(args2); - return -1; - } - length = vsnprintf((char*)buffer, length + 1, fmt, args2); - va_end(args2); - std::error_code ec; - S64 written = write(buffer, length, ec); - free(buffer); - return written; -} - -int LLFile::lock(int mode, std::error_code& ec) -{ -#if LL_WINDOWS - if (!(mode & LLFile::lock_mask)) - { - if (UnlockFile(mHandle, 0, 0, MAXDWORD, MAXDWORD)) - { - return clear_error(ec); - } + // it is something else than a file or directory + // this should not really happen as long as we do not allow for symlink + // detection in the optional parameter to get_fileattr() + rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER); } else { - OVERLAPPED overlapped = { 0 }; - DWORD flags = (mode & LLFile::noblock) ? LOCKFILE_FAIL_IMMEDIATELY : 0; - if (mode & LLFile::exclusive) - { - flags |= LOCKFILE_EXCLUSIVE_LOCK; - } - // We lock the maximum range, since flock only supports locking the entire file too - if (LockFileEx(mHandle, flags, 0, MAXDWORD, MAXDWORD, &overlapped)) - { - return clear_error(ec); - } - } -#else - if (flock(mHandle, decode_lock_mode(mode)) == 0) - { - return clear_error(ec); + // get_fileattr() failed and already set errno, preserve it for correct error reporting } -#endif - return set_ec_from_system_error(ec); -} - -int LLFile::close(std::error_code& ec) -{ - if (mHandle != InvalidHandle) - { - llfile_handle_t handle = InvalidHandle; - std::swap(handle, mHandle); -#if LL_WINDOWS - if (!CloseHandle(handle)) #else - if (::close(handle)) + int rc = ::remove(filename.c_str()); #endif - { - return set_ec_from_system_error(ec); - } - } - return clear_error(ec); -} - -int LLFile::close() -{ - std::error_code ec; - return close(ec); + return warnif("remove", filename, rc, suppress_error); } -//---------------------------------------------------------------------------------------- -// static member functions -//---------------------------------------------------------------------------------------- - // static -LLFILE* LLFile::fopen(const std::string& filename, const char* mode, int lmode) +int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error) { - LLFILE* file; #if LL_WINDOWS - int shflag = _SH_DENYNO; - switch (lmode) + // Posix rename() will gladly overwrite a file at newname if it exists, the Windows + // rename(), respectively _wrename(), will bark on that. Instead call directly the Windows + // API MoveFileEx() and use its flags to specify that overwrite is allowed. + std::wstring utf16filename = utf8path_to_wstring(filename); + std::wstring utf16newname = utf8path_to_wstring(newname); + int rc = 0; + if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) { - case LLFile::exclusive: - shflag = _SH_DENYRW; - break; - case LLFile::shared: - shflag = _SH_DENYWR; - break; + rc = set_errno_from_oserror(GetLastError()); } - std::wstring file_path = utf8StringToWstring(filename); - std::wstring utf16mode = ll_convert(std::string(mode)); - file = _wfsopen(file_path.c_str(), utf16mode.c_str(), shflag); #else - file = ::fopen(filename.c_str(), mode); - if (file && (lmode & (LLFile::lock_mask))) - { - // Rather fail on a sharing conflict than block - if (flock(fileno(file), decode_lock_mode(lmode | LLFile::noblock))) - { - ::fclose(file); - file = nullptr; - } - } + int rc = ::rename(filename.c_str(),newname.c_str()); #endif - return file; + return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error); } -// static -int LLFile::close(LLFILE* file) -{ - int ret_value = 0; - if (file) - { - ret_value = ::fclose(file); - } - return ret_value; -} - -// static -std::string LLFile::getContents(const std::string& filename) -{ - std::error_code ec; - return getContents(filename, ec); -} +// Make this a define rather than using magic numbers multiple times in the code +#define LLFILE_COPY_BUFFER_SIZE 16384 // static -std::string LLFile::getContents(const std::string& filename, std::error_code& ec) +bool LLFile::copy(const std::string& from, const std::string& to) { - std::string buffer; - LLFile file(filename, LLFile::in | LLFile::binary, ec); - if (file) + bool copied = false; + LLFILE* in = LLFile::fopen(from, "rb"); + if (in) { - S64 length = file.size(ec); - if (!ec && length > 0) + LLFILE* out = LLFile::fopen(to, "wb"); + if (out) { - buffer = std::string(length, 0); - file.read(&buffer[0], length, ec); - if (ec) + char buf[LLFILE_COPY_BUFFER_SIZE]; + size_t readbytes; + bool write_ok = true; + while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in))) { - buffer.clear(); - } - } - } - return buffer; -} - -// static -int LLFile::mkdir(const std::string& dirname) -{ - std::error_code ec; - std::filesystem::path file_path = utf8StringToPath(dirname); - // We often use mkdir() to ensure the existence of a directory that might - // already exist. There is no known case in which we want to call out as - // an error the requested directory already existing. - std::filesystem::create_directory(file_path, ec); - // The return value is only true if the directory was actually created. - // But if it already existed, ec still indicates success. - return warnif("mkdir", dirname, ec); -} - -// static -int LLFile::remove(const std::string& filename, int suppress_warning) -{ - std::error_code ec; - std::filesystem::path file_path = utf8StringToPath(filename); - std::filesystem::remove(file_path, ec); - return warnif("remove", filename, ec, suppress_warning); -} - -// static -int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_warning) -{ - std::error_code ec; - std::filesystem::path file_path = utf8StringToPath(filename); - std::filesystem::path new_path = utf8StringToPath(newname); - std::filesystem::rename(file_path, new_path, ec); - return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, ec, suppress_warning); -} - -// static -S64 LLFile::read(const std::string& filename, void* buf, S64 offset, S64 nbytes) -{ - std::error_code ec; - return read(filename, buf, offset, nbytes, ec); -} - -// static -S64 LLFile::read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec) -{ - // if number of bytes is 0 or less there is nothing to do here - if (nbytes <= 0) - { - clear_error(ec); - return 0; - } - - if (!buf || offset < 0) - { - set_ec_to_parameter_error(ec); - } - else - { - std::ios_base::openmode omode = LLFile::in | LLFile::binary; - - LLFile file(filename, omode, ec); - if (!ec && (bool)file) - { - if (offset > 0) - { - file.seek(offset, ec); - } - // else (offset == 0) file was just opened and should already be at 0. - if (!ec) - { - S64 bytes_read = file.read(buf, nbytes, ec); - if (!ec) + if (fwrite(buf, 1, readbytes, out) != readbytes) { - return bytes_read; + LL_WARNS("LLFile") << "Short write" << LL_ENDL; + write_ok = false; } } - } - } - return warnif("read from file failed", filename, ec); -} - -// static -S64 LLFile::write(const std::string& filename, const void* buf, S64 offset, S64 nbytes) -{ - std::error_code ec; - return write(filename, buf, offset, nbytes, ec); -} - -// static -S64 LLFile::write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec) -{ - // if number of bytes is 0 or less there is nothing to do here - if (nbytes <= 0) - { - clear_error(ec); - return 0; - } - - if (!buf) - { - set_ec_to_parameter_error(ec); - } - else - { - std::ios_base::openmode omode = LLFile::out | LLFile::binary; - if (offset < 0) - { - omode |= LLFile::app; - } - - LLFile file(filename, omode, ec); - if (!ec && (bool)file) - { - if (offset > 0) + if ( write_ok ) { - file.seek(offset, ec); - } - // else (offset == 0) we are not appending, file was just opened and should already be at 0. - if (!ec) - { - S64 bytes_written = file.write(buf, nbytes, ec); - if (!ec) - { - return bytes_written; - } + copied = true; } + fclose(out); } - } - return warnif("write to file failed", filename, ec); -} - -// static -bool LLFile::copy(const std::string& source, const std::string& target) -{ - std::error_code ec; - return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec); -} - -// static -bool LLFile::copy(const std::string& source, const std::string& target, std::filesystem::copy_options options, std::error_code& ec) -{ - std::filesystem::path source_path = utf8StringToPath(source); - std::filesystem::path target_path = utf8StringToPath(target); - bool copied = std::filesystem::copy_file(source_path, target_path, options, ec); - if (!copied) - { - warnif(STRINGIZE("copy failed, to '" << target << "' from"), source, ec); + fclose(in); } return copied; } // static -int LLFile::stat(const std::string& filename, llstat* filestatus, const char *fname, int suppress_warning) +int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error) { #if LL_WINDOWS - std::wstring file_path = utf8StringToWstring(filename); - int rc = _wstat64(file_path.c_str(), filestatus); + std::wstring utf16filename = utf8path_to_wstring(filename); + int rc = _wstat64(utf16filename.c_str(), filestatus); #else int rc = ::stat(filename.c_str(), filestatus); #endif - return warnif(fname ? fname : "stat", filename, rc, suppress_warning); + return warnif("stat", filename, rc, suppress_error); } // static -std::time_t LLFile::getCreationTime(const std::string& filename, int suppress_warning) +unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error) { - // As of C++20 there is no functionality in std::filesystem to retrieve this information - llstat filestat; - int rc = stat(filename, &filestat, "getCreationTime", suppress_warning); - if (rc == 0) +#if LL_WINDOWS + // _wstat64() is a bit heavyweight on Windows, use a more lightweight API + // to just get the attributes + int rc = -1; + std::wstring utf16filename = utf8path_to_wstring(filename); + unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink); + if (st_mode) { -#if LL_DARWIN - return filestat.st_birthtime; -#else - // Linux stat() doesn't have a creation/birth time (st_ctime really is the last status - // change or inode attributes change) unless we would use statx() instead. But that is - // a major effort, which would require Linux specific changes to LLFile::stat() above - // and possibly adaptions for other platforms that we leave for a later exercise if it - // is ever desired. - return filestat.st_ctime; -#endif + return st_mode; } - return 0; -} - -// static -std::time_t LLFile::getModificationTime(const std::string& filename, int suppress_warning) -{ - // tried to use std::filesystem::last_write_time() but the whole std::chrono infrastructure is as of - // C++20 still not fully implemented on all platforms. Specifically MacOS C++20 seems lacking here, - // and Windows requires a roundabout through std::chrono::utc_clock to then get a - // std::chrono::system_clock that can return a more useful time_t. - // So we take the easy way out in a similar way as with getCreationTime(). - llstat filestat; - int rc = stat(filename, &filestat, "getModificationTime", suppress_warning); +#else + llstat filestatus; + int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus); if (rc == 0) { - return filestat.st_mtime; + return filestatus.st_mode; } +#endif + warnif("getattr", filename, rc, suppress_error); return 0; } -// static -S64 LLFile::size(const std::string& filename, int suppress_warning) -{ - std::error_code ec; - std::filesystem::path file_path = utf8StringToPath(filename); - std::intmax_t size = static_cast(std::filesystem::file_size(file_path, ec)); - if (ec) - { - return warnif("size", filename, ec, suppress_warning); - } - return size; -} - -// static -std::filesystem::file_status LLFile::getStatus(const std::string& filename, bool dontFollowSymLink, int suppress_warning) -{ - std::error_code ec; - std::filesystem::path file_path = utf8StringToPath(filename); - std::filesystem::file_status status; - if (dontFollowSymLink) - { - status = std::filesystem::symlink_status(file_path, ec); - } - else - { - status = std::filesystem::status(file_path, ec); - } - warnif("getStatus()", filename, ec, suppress_warning); - return status; -} - -// static -bool LLFile::exists(const std::string& filename) -{ - std::filesystem::file_status status = getStatus(filename); - return std::filesystem::exists(status); -} - // static bool LLFile::isdir(const std::string& filename) { - std::filesystem::file_status status = getStatus(filename); - return std::filesystem::is_directory(status); + return S_ISDIR(getattr(filename)); } // static bool LLFile::isfile(const std::string& filename) { - std::filesystem::file_status status = getStatus(filename); - return std::filesystem::is_regular_file(status); + return S_ISREG(getattr(filename)); } // static bool LLFile::islink(const std::string& filename) { - std::filesystem::file_status status = getStatus(filename, true); - return std::filesystem::is_symlink(status); -} - -// static -const std::string& LLFile::tmpdir() -{ - static std::string temppath; - if (temppath.empty()) - { - temppath = std::filesystem::temp_directory_path().string(); - } - return temppath; + return S_ISLNK(getattr(filename, true)); } // static -std::filesystem::path LLFile::utf8StringToPath(const std::string& pathname) +const char *LLFile::tmpdir() { -#if LL_WINDOWS - return ll_convert(pathname); -#else - return pathname; -#endif -} + static std::string utf8path; -#if LL_WINDOWS - -// static -std::wstring LLFile::utf8StringToWstring(const std::string& pathname) -{ - std::wstring utf16string(ll_convert(pathname)); - if (utf16string.size() >= MAX_PATH) + if (utf8path.empty()) { - // By going through std::filesystem::path we get a lot of path sanitation done for us that - // is needed when passing a path with a kernel object space prefix to Windows API functions - // since this prefix disables the kernel32 path normalization - std::filesystem::path utf16path(utf16string); + char sep; +#if LL_WINDOWS + sep = '\\'; - // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names - utf16string.assign(L"\\\\?\\").append(utf16path); + std::vector utf16path(MAX_PATH + 1); + GetTempPathW(static_cast(utf16path.size()), &utf16path[0]); + utf8path = ll_convert_wide_to_string(&utf16path[0]); +#else + sep = '/'; - /* remove trailing spaces and dots (yes, Windows really does that) */ - size_t last_valid = utf16string.find_last_not_of(L" \t."); - if (last_valid == std::wstring::npos) + utf8path = LLStringUtil::getenv("TMPDIR", "/tmp/"); +#endif + if (utf8path[utf8path.size() - 1] != sep) { - return std::wstring(); + utf8path += sep; } - return utf16string.substr(0, last_valid + 1); } - return utf16string; + return utf8path.c_str(); } +#if LL_WINDOWS + /************** input file stream ********************************/ llifstream::llifstream() {} @@ -1176,8 +586,10 @@ void llifstream::open(const std::string& _Filename, ios_base::openmode _Mode) _Mode | ios_base::in); } + /************** output file stream ********************************/ + llofstream::llofstream() {} // explicit @@ -1193,4 +605,30 @@ void llofstream::open(const std::string& _Filename, ios_base::openmode _Mode) _Mode | ios_base::out); } +/************** helper functions ********************************/ + +std::streamsize llifstream_size(llifstream& ifstr) +{ + if(!ifstr.is_open()) return 0; + std::streampos pos_old = ifstr.tellg(); + ifstr.seekg(0, ios_base::beg); + std::streampos pos_beg = ifstr.tellg(); + ifstr.seekg(0, ios_base::end); + std::streampos pos_end = ifstr.tellg(); + ifstr.seekg(pos_old, ios_base::beg); + return pos_end - pos_beg; +} + +std::streamsize llofstream_size(llofstream& ofstr) +{ + if(!ofstr.is_open()) return 0; + std::streampos pos_old = ofstr.tellp(); + ofstr.seekp(0, ios_base::beg); + std::streampos pos_beg = ofstr.tellp(); + ofstr.seekp(0, ios_base::end); + std::streampos pos_end = ofstr.tellp(); + ofstr.seekp(pos_old, ios_base::beg); + return pos_end - pos_beg; +} + #endif // LL_WINDOWS diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h old mode 100755 new mode 100644 index 2c74c97d0b..04a2946ac4 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -35,359 +35,118 @@ * Attempts to mostly mirror the POSIX style IO functions. */ +typedef FILE LLFILE; + #include -#include #include #if LL_WINDOWS -#include // The Windows version of stat function and stat data structure are called _stat64 // We use _stat64 here to support 64-bit st_size and time_t values -typedef struct _stat64 llstat; +typedef struct _stat64 llstat; #else +typedef struct stat llstat; #include -typedef struct stat llstat; #endif -typedef FILE LLFILE; +#ifndef S_ISREG +# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) +#endif + +#ifndef S_ISDIR +# define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) +#endif + +// Windows C runtime library does not define this and does not support symlink detection in the +// stat functions but we do in our getattr() function +#ifndef S_IFLNK +#define S_IFLNK 0xA000 /* symlink */ +#endif + +#ifndef S_ISLNK +#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK) +#endif #include "llstring.h" // safe char* -> std::string conversion -/// This class provides a selection of functions to operate on files through names and -/// a class implementation to represent a file for reading and writing to it +/// LLFile is a class of static functions operating on paths /// All the functions with a path string input take UTF8 path/filenames -/// -/// @nosubgrouping -/// class LL_COMMON_API LLFile { public: - // ================================================================================ - /// @name Constants - /// - ///@{ - /** These can be passed to the omode parameter of LLFile::open() and its constructor - - This is similar to the openmode flags for std:fstream but not exactly the same - std::fstream open() does not allow to open a file for writing without either - forcing the file to be truncated on open or all write operations being always - appended to the end of the file or failing to open when the file does not exist. - But to allow implementing the LLAPRFile::writeEx() functionality we need to be - able to write at a random position to the file without truncating it on open. - - any other combinations than listed here are not allowed and will cause an error - - bin in out trunc app noreplace File exists File doesn't exist - ---------------------------------------------------------------------------------- - - + - - - - Open at begin Failure to open - + + - - - - " " - - - + - - - " Create new - + - + - - - " " - - + + - - - " " - + + + - - - " " - ---------------------------------------------------------------------------------- - - - + + - - Destroy contents Create new - + - + + - - " " - - + + + - - " " - + + + + - - " " - ---------------------------------------------------------------------------------- - - - + x - + Failure to open Create new - + - + x - + " " - - + + x - + " " - + + + x - + " " - ---------------------------------------------------------------------------------- - - - + - + - Write to end Create new - + - + - + - " " - - + + - + - " " - + + + - + - " " - ---------------------------------------------------------------------------------- - */ - static const std::ios_base::openmode app = 1 << 1; // append to end - static const std::ios_base::openmode ate = 1 << 2; // initialize to end - static const std::ios_base::openmode binary = 1 << 3; // binary mode - static const std::ios_base::openmode in = 1 << 4; // for reading - static const std::ios_base::openmode out = 1 << 5; // for writing - static const std::ios_base::openmode trunc = 1 << 6; // truncate on open - static const std::ios_base::openmode noreplace = 1 << 7; // no replace if it exists - - /// Additional optional flags to omode in open() and lmode in fopen() or lock() - /// to indicate which sort of lock if any to attempt to get - /// - /// NOTE: there is a fundamental difference between platforms. - /// On Windows this lock is mandatory as it is part of the API to open a file handle and other - /// processes can not avoid it. If a file was opened denying other processes read and/or write - /// access, trying to open the same file in another process with that access will fail. - /// On Mac and Linux it is only an advisory lock implemented through the flock() system call. - /// This means that any other application needs to also attempt to at least acquire a shared - /// lock on the file in order to notice that the file is actually already locked. It can - /// therefore not be used to prevent random other applications from accessing the file, but it - /// works for other viewer processes when they use either the LLFile::open() or LLFile::fopen() - /// functions with the appropriate lock flags to open a file. - static const std::ios_base::openmode exclusive = 1 << 16; - static const std::ios_base::openmode shared = 1 << 17; - - /// Additional lmode flag to indicate to rather fail instead of blocking when trying - /// to acquire a lock with LLFile::lock() - static const std::ios_base::openmode noblock = 1 << 18; - - /// The mask value for the lock mask bits - static const std::ios_base::openmode lock_mask = exclusive | shared; - - /// One of these can be passed to the dir parameter of LLFile::seek() - static const std::ios_base::seekdir beg = std::ios_base::beg; - static const std::ios_base::seekdir cur = std::ios_base::cur; - static const std::ios_base::seekdir end = std::ios_base::end; - ///@} - - // ================================================================================ - /// @name constructor/deconstructor - /// - ///@{ - /// default constructor - LLFile() : mHandle(InvalidHandle) {} - - /// no copy constructor - LLFile(const LLFile&) = delete; - - /// move constructor - LLFile(LLFile&& other) noexcept - { - mHandle = other.mHandle; - other.mHandle = InvalidHandle; - } - - /// constructor opening the file - LLFile(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666) : - mHandle(InvalidHandle) - { - open(filename, omode, ec, perm); - } - - /// destructor always attempts to close the file - ~LLFile() { close(); } - ///@} - - // ================================================================================ - /// @name operators - /// - ///@{ - /// copy assignment deleted - LLFile& operator=(const LLFile&) = delete; - - /// move assignment - LLFile& operator=(LLFile&& other) noexcept - { - close(); - std::swap(mHandle, other.mHandle); - return *this; - } - - // detect whether the wrapped file descriptor/handle is open or not - explicit operator bool() const { return (mHandle != InvalidHandle); } - bool operator!() { return (mHandle == InvalidHandle); } - ///@} - - /// ================================================================================ - /// @name class member methods - /// - /// These methods provide read and write support as well as additional functionality to query the size of - /// the file, change the position of the current file pointer or query it. - /// - /// Most of these functions take as one of their parameters a std::error_code object which can be used to - /// determine in more detail what error occurred if required - ///@{ - - /// Open a file with the specific open mode flags - int open(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666); - ///< @returns 0 on success, -1 on failure - - /// Determine the size of the opened file - S64 size(std::error_code& ec); - ///< @returns the number of bytes in the file or -1 on failure - - /// Query the position of the current file pointer in the file - S64 tell(std::error_code& ec); - ///< @returns the absolute offset of the file pointer in bytes relative to the start of the file or -1 on failure - - /// Move the file pointer to the specified absolute position relative to the start of the file - int seek(S64 pos, std::error_code& ec); - ///< @returns 0 on success, -1 on failure - - /// Move the file pointer to the specified position relative to dir - int seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec); - ///< @returns 0 on success, -1 on failure - - /// Read the specified number of bytes into the buffer starting at the current file pointer - S64 read(void* buffer, S64 nbytes, std::error_code& ec); - ///< If the file ends before the requested amount of bytes could be read, the function succeeds and - /// returns the bytes up to the end of the file. The return value indicates the number of actually - /// read bytes and can be therefore smaller than the requested amount. - /// @returns the number of bytes read from the file or -1 on failure - - /// Write the specified number of bytes to the file starting at the current file pointer - S64 write(const void* buffer, S64 nbytes, std::error_code& ec); - ///< @returns the number of bytes written to the file or -1 on failure - - /// Write into the file starting at the current file pointer using printf style format and - /// additional optional parameters as specified in the fmt string - S64 printf(const char* fmt, ...); - ///< @returns the number of bytes written to the file or -1 on failure - - /// Attempt to acquire or release a lock on the file - int lock(int lmode, std::error_code& ec); - ///< lmode can be one of LLFile::exclusive or LLFile::shared to acquire the according lock - /// or 0 to give up an earlier acquired lock. Adding LLFile::noblock together with one of - /// the lock requests will cause the function to fail if the lock can not be acquired, - /// otherwise the function will block until the lock can be acquired. - /// @returns 0 on success, -1 on failure - - /// close the file explicitly - int close(std::error_code& ec); - ///< @returns 0 on success, -1 on failure - - /// Convenience function to close the file without additional parameters - int close(); - ///< @returns 0 on success, -1 on failure - ///@} - - /// ================================================================================ - /// @name static member functions - /// - /// These functions are static and operate with UTF8 filenames as one of their parameters. - /// - ///@{ /// open a file with the specified access mode - static LLFILE* fopen(const std::string& filename, const char* accessmode, int lmode = 0); + static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */ ///< 'accessmode' follows the rules of the Posix fopen() mode parameter - /// "r" open the file for reading only and positions the stream at the beginning - /// "r+" open the file for reading and writing and positions the stream at the beginning - /// "w" open the file for reading and writing and truncate it to zero length - /// "w+" open or create the file for reading and writing and truncate to zero length if it existed - /// "a" open the file for reading and writing and before every write position the stream at the end of the file - /// "a+" open or create the file for reading and writing and before every write position the stream at the end of the file - /// - /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac - /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not - /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither - /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT. - /// This means that it is always a good idea to append "b" specifically for binary file access to - /// avoid corruption of the binary consistency of the data stream when reading or writing - /// Other characters in 'accessmode', while possible on some platforms (Windows), will usually - /// cause an error on other platforms as fopen will verify this parameter + /// "r" open the file for reading only and positions the stream at the beginning + /// "r+" open the file for reading and writing and positions the stream at the beginning + /// "w" open the file for reading and writing and truncate it to zero length + /// "w+" open or create the file for reading and writing and truncate to zero length if it existed + /// "a" open the file for reading and writing and position the stream at the end of the file + /// "a+" open or create the file for reading and writing and position the stream at the end of the file /// - /// lmode is optional and allows to lock the file for other processes either as a shared lock or an - /// exclusive lock. If the requested lock conflicts with an already existing lock, the open fails. - /// Pass either LLFIle::exclusive or LLFile::shared to this parameter if you want to prevent other - /// processes from reading (exclusive lock) or writing (shared lock) to the file. It will always use - /// LLFile::noblock, meaning the open will immediately fail if it conflicts with an existing lock on the - /// file. - /// - /// @returns a valid LLFILE* pointer on success that can be passed to the fread() and fwrite() functions - /// and some other f functions in the Standard C library that accept a FILE* as parameter - /// or NULL on failure + /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac + /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not + /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither + /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT. + /// This means that it is always a good idea to append "b" specifically for binary file access to + /// avoid corruption of the binary consistency of the data stream when reading or writing + /// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter + /// @returns a valid LLFILE* pointer on success or NULL on failure - /// Close a file handle opened with fopen() above static int close(LLFILE * file); - ///< @returns 0 on success and -1 on failure. + + /// retrieve the content of a file into a string + static std::string getContents(const std::string& filename); + ///< @returns the content of the file or an empty string on failure /// create a directory - static int mkdir(const std::string& filename); - ///< mkdir() considers "directory already exists" to be not an error. + static int mkdir(const std::string& filename, int perms = 0700); + ///< perms is a permissions mask like 0777 or 0700. In most cases it will be + /// overridden by the user's umask. It is ignored on Windows. + /// mkdir() considers "directory already exists" to be not an error. + /// @returns 0 on success and -1 on failure. + + //// remove a directory + static int rmdir(const std::string& filename, int suppress_error = 0); + ///< pass ENOENT in the optional 'suppress_error' parameter + /// if you don't want a warning in the log when the directory does not exist /// @returns 0 on success and -1 on failure. /// remove a file or directory - static int remove(const std::string& filename, int suppress_warning = 0); - ///< pass an errno value (e.g., ENOENT) in the optional 'suppress_warning' parameter if you want to - /// suppress a warning in the log when the failure matches that errno (e.g., suppress warning if - /// the file or directory does not exist) + static int remove(const std::string& filename, int suppress_error = 0); + ///< pass ENOENT in the optional 'suppress_error' parameter + /// if you don't want a warning in the log when the directory does not exist /// @returns 0 on success and -1 on failure. /// rename a file - static int rename(const std::string& filename, const std::string& newname, int suppress_warning = 0); + static int rename(const std::string& filename, const std::string& newname, int suppress_error = 0); ///< it will silently overwrite newname if it exists without returning an error /// Posix guarantees that if newname already exists, then there will be no moment /// in which for other processes newname does not exist. There is no such guarantee - /// under Windows at this time. It may do it in the same way but the used Windows - /// APIs do not make such guarantees. + /// under Windows at this time. It may do it in the same way but the used Windows API + /// does not make such guarantees. /// @returns 0 on success and -1 on failure. - /// copy the contents of the file from 'source' to 'target' - static bool copy(const std::string& source, const std::string& target); - ///< Copies the contents of the file 'source' to the file 'target', overwriting 'target' if it already - /// existed. - /// This is a convenience function that implements the previous behavior of silently overwriting an - /// already existing target file. Consider using the function below if you desire a different - /// behavior when the target file already exists - /// @returns true on success and false on failure. - - /// copy the contents of the file from 'from' to 'to' - static bool copy(const std::string& source, const std::string& target, std::filesystem::copy_options options, std::error_code& ec); - ///< Copies the contents of the file 'source' to the file 'target'. The options parameter allows to - /// specify what should happen if the "target" file already exists: - /// std::filesystem::copy_options::none - return an error in ec and fail - /// std::filesystem::copy_options::skip_existing - skip the operation and do not overwrite file - /// std::filesystem::copy_options::overwrite_existing - overwrite the file - /// std::filesystem::copy_options::update_existing - overwrite the file only if it is older than the file being copied - /// @returns true on success and false on failure. - - /// retrieve the content of a file into a string - static std::string getContents(const std::string& filename); - static std::string getContents(const std::string& filename, std::error_code& ec); - ///< @returns the entire content of the file as std::string or an empty string on failure - - /// read nBytes from the file into the buffer, starting at offset in the file - static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes); - static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec); - ///< @returns bytes read on success, or -1 on failure - /// write nBytes from the buffer into the file, starting at offset in the file - static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes); - static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec); - ///< If a negative offset is provided, the file is opened in append mode and the - /// write will be appended to the end of the file. - /// @returns bytes written on success, or -1 on failure + /// copy the contents of file from 'from' to 'to' filename + static bool copy(const std::string& from, const std::string& to); + ///< @returns true on success and false on failure. /// return the file stat structure for filename - static int stat(const std::string& filename, llstat* file_status, const char *operation = nullptr, int suppress_warning = ENOENT); + static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT); ///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the - /// optional 'suppress_warning' parameter to avoid spamming the log with warnings when the API + /// optional 'suppress_error' parameter to avoid spamming the log with warnings when the API /// is used to detect if a file exists /// @returns 0 on success and -1 on failure. - /// get the creation data and time of a file - static std::time_t getCreationTime(const std::string& filename, int suppress_warning = 0); - ///< Different systems have different support for this. Under Windows this is supposedly - /// the actual time the file was created, on the Mac this is the actual birth date of - /// the file which is in fact the creation time. The according ctime entry in the stat - /// structure under Linux (and any other *nix really) is however contrary to what one - /// might expect based on the c in ctime not the creation time but the time the last - /// change to the inode entry was made. Changing access rights to a file will update - /// this value too. In order to have a true creation time under Linux, we would have - /// to use the statx() call which is available since kernel 4.19, but that will require - /// considerable changes to the implementation of above stat() function. - /// @returns the creation time (last status change under Linux) of the file or 0 on error - - /// get the last modification data and time of a file - static std::time_t getModificationTime(const std::string& filename, int suppress_warning = 0); - ///< @returns the modification time of the file or 0 on error - - /// get the std::filesystem::file_status for filename - static std::filesystem::file_status getStatus(const std::string& filename, bool dontFollowSymLink = false, int suppress_warning = ENOENT); - ///< dontFollowSymLinks set to true returns the std::filesystem::file_status of the symlink if it - /// is one, rather than resolving it. We pass by default ENOENT in the optional 'suppress_warning' - /// parameter to not spam the log with warnings when the file or directory does not exist - /// @returns a std::filesystem::file_status value that can be passed to the appropriate std::filesystem::exists() - /// and other APIs accepting a file_status. - - /// get the size of a file in bytes - static S64 size(const std::string& filename, int suppress_warning = ENOENT); - ///< we pass by default ENOENT in the optional 'suppress_warning' parameter to not spam - /// the log with warnings when the file does not exist - /// @returns the file size on success or -1 on failure. - - /// check if filename is an existing file or directory - static bool exists(const std::string& filename); - ///< @returns true if the path is for an existing file or directory + /// get the file or directory attributes for filename + static unsigned short getattr(const std::string& filename, bool dontFollowSymLink = false, int suppress_error = ENOENT); + ///< a more lightweight function on Windows to stat, that just returns the file attribute flags + /// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it + /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with + /// warnings when the file or directory does not exist + /// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise + /// together with the three access bits which under Windows only the write bit is relevant. /// check if filename is an existing directory static bool isdir(const std::string& filename); @@ -402,44 +161,70 @@ public: ///< @returns true if the path is pointing at a symlink /// return a path to the temporary directory on the system - static const std::string& tmpdir(); + static const char * tmpdir(); +}; - /// converts a string containing a path in utf8 encoding into an explicit filesystem path - static std::filesystem::path utf8StringToPath(const std::string& pathname); - ///< @returns the path as a std::filesystem::path - ///@} +/// RAII class +class LLUniqueFile +{ +public: + // empty + LLUniqueFile(): mFileHandle(nullptr) {} + // wrap (e.g.) result of LLFile::fopen() + LLUniqueFile(LLFILE* f): mFileHandle(f) {} + // no copy + LLUniqueFile(const LLUniqueFile&) = delete; + // move construction + LLUniqueFile(LLUniqueFile&& other) noexcept + { + mFileHandle = other.mFileHandle; + other.mFileHandle = nullptr; + } + // The point of LLUniqueFile is to close on destruction. + ~LLUniqueFile() + { + close(); + } -private: -#if LL_WINDOWS - typedef HANDLE llfile_handle_t; - const llfile_handle_t InvalidHandle = INVALID_HANDLE_VALUE; -#else - typedef int llfile_handle_t; - const llfile_handle_t InvalidHandle = -1; -#endif + // simple assignment + LLUniqueFile& operator=(LLFILE* f) + { + close(); + mFileHandle = f; + return *this; + } + // copy assignment deleted + LLUniqueFile& operator=(const LLUniqueFile&) = delete; + // move assignment + LLUniqueFile& operator=(LLUniqueFile&& other) noexcept + { + close(); + std::swap(mFileHandle, other.mFileHandle); + return *this; + } - /// ================================================================================ - /// @name private static member functions - /// -#if LL_WINDOWS - /// convert a string containing a path in utf8 encoding into a Windows format std::wstring - static std::wstring utf8StringToWstring(const std::string& pathname); - ///< this will prepend the path with the Windows kernel object space prefix when the path is - /// equal or longer than MAX_PATH characters and do some sanitation on the path. - /// This allows the underlaying Windows APIs to process long path names. Do not pass such a path - /// to std::filesystem functions. These functions are not guaranteed to handle such paths properly. - /// It's only useful to pass the resulting string buffer to Microsoft Windows widechar APIs or - /// the Microsoft C runtime widechar file functions. - /// - /// Example: - /// - /// std::wstring file_path = utf8StringToWstring(filename); - /// HANDLE CreateFileW(file_path.c_str(), ......); - /// - /// @returns the path as a std::wstring path -#endif - llfile_handle_t mHandle; // The file handle/descriptor - std::ios_base::openmode mOpen; // Used to emulate std::ios_base::app under Windows + // explicit close operation + void close() + { + if (mFileHandle) + { + // in case close() throws, set mFileHandle null FIRST + LLFILE* h{nullptr}; + std::swap(h, mFileHandle); + LLFile::close(h); + } + } + + // detect whether the wrapped LLFILE is open or not + explicit operator bool() const { return bool(mFileHandle); } + bool operator!() { return ! mFileHandle; } + + // LLUniqueFile should be usable for any operation that accepts LLFILE* + // (or FILE* for that matter) + operator LLFILE*() const { return mFileHandle; } + +private: + LLFILE* mFileHandle; }; #if LL_WINDOWS @@ -530,6 +315,17 @@ class LL_COMMON_API llofstream : public std::ofstream ios_base::openmode _Mode = ios_base::out|ios_base::trunc); }; + +/** + * @brief filesize helpers. + * + * The file size helpers are not considered particularly efficient, + * and should only be used for config files and the like -- not in a + * loop. + */ +std::streamsize LL_COMMON_API llifstream_size(llifstream& fstr); +std::streamsize LL_COMMON_API llofstream_size(llofstream& fstr); + #else // ! LL_WINDOWS // on non-windows, llifstream and llofstream are just mapped directly to the std:: equivalents diff --git a/indra/llcrashlogger/llcrashlock.cpp b/indra/llcrashlogger/llcrashlock.cpp old mode 100755 new mode 100644 index 731b53b7e9..bc34f6798f --- a/indra/llcrashlogger/llcrashlock.cpp +++ b/indra/llcrashlogger/llcrashlock.cpp @@ -188,7 +188,12 @@ LLSD LLCrashLock::getProcessList() //static bool LLCrashLock::fileExists(std::string filename) { - return LLFile::exists(filename); +#ifdef LL_WINDOWS // or BOOST_WINDOWS_API + boost::filesystem::path file_path(ll_convert(filename)); +#else + boost::filesystem::path file_path(filename); +#endif + return boost::filesystem::exists(file_path); } void LLCrashLock::cleanupProcess(std::string proc_dir) diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp old mode 100755 new mode 100644 index eb3c2d9909..190539cea5 --- a/indra/llfilesystem/lldir.cpp +++ b/indra/llfilesystem/lldir.cpp @@ -30,6 +30,8 @@ #include #include #include +#else +#include #endif #include "lldir.h" @@ -41,6 +43,7 @@ #include "lldiriterator.h" #include "stringize.h" #include "llstring.h" +#include #include #include @@ -90,20 +93,30 @@ LLDir::~LLDir() std::vector LLDir::getFilesInDir(const std::string &dirname) { - // Returns a vector of filenames in the directory. - std::filesystem::path p = LLFile::utf8StringToPath(dirname); + //Returns a vector of fullpath filenames. + +#ifdef LL_WINDOWS // or BOOST_WINDOWS_API + boost::filesystem::path p(ll_convert(dirname)); +#else + boost::filesystem::path p(dirname); +#endif + std::vector v; - std::error_code ec; - if (std::filesystem::is_directory(p, ec)) + + boost::system::error_code ec; + if (exists(p, ec) && !ec.failed()) { - std::filesystem::directory_iterator end_iter; - for (std::filesystem::directory_iterator dir_itr(p); - dir_itr != end_iter; - ++dir_itr) + if (is_directory(p, ec) && !ec.failed()) { - if (std::filesystem::is_regular_file(dir_itr->status())) + boost::filesystem::directory_iterator end_iter; + for (boost::filesystem::directory_iterator dir_itr(p); + dir_itr != end_iter; + ++dir_itr) { - v.push_back(dir_itr->path().filename().string()); + if (boost::filesystem::is_regular_file(dir_itr->status())) + { + v.push_back(dir_itr->path().filename().string()); + } } } } @@ -173,23 +186,28 @@ U32 LLDir::deleteDirAndContents(const std::string& dir_name) //Removes the directory and its contents. Returns number of files deleted. U32 num_deleted = 0; - std::filesystem::path dir_path = LLFile::utf8StringToPath(dir_name); try { - if (std::filesystem::is_directory(dir_path)) +#ifdef LL_WINDOWS // or BOOST_WINDOWS_API + boost::filesystem::path dir_path(ll_convert(dir_name)); +#else + boost::filesystem::path dir_path(dir_name); +#endif + + if (boost::filesystem::exists(dir_path)) { - if (!std::filesystem::is_empty(dir_path)) + if (!boost::filesystem::is_empty(dir_path)) { // Directory has content - num_deleted = (U32)std::filesystem::remove_all(dir_path); + num_deleted = (U32)boost::filesystem::remove_all(dir_path); } else { // Directory is empty - std::filesystem::remove(dir_path); + boost::filesystem::remove(dir_path); } } } - catch (std::filesystem::filesystem_error &er) + catch (boost::filesystem::filesystem_error &er) { LL_WARNS() << "Failed to delete " << dir_name << " with error " << er.code().message() << LL_ENDL; } @@ -1087,15 +1105,15 @@ void dir_exists_or_crash(const std::string &dir_name) #if LL_WINDOWS // *FIX: lame - it doesn't do the same thing on windows. not so // important since we don't deploy simulator to windows boxes. - LLFile::mkdir(dir_name); + LLFile::mkdir(dir_name, 0700); #else - llstat dir_stat; + struct stat dir_stat; if(0 != LLFile::stat(dir_name, &dir_stat)) { S32 stat_rv = errno; if(ENOENT == stat_rv) { - if(0 != LLFile::mkdir(dir_name)) + if(0 != LLFile::mkdir(dir_name, 0700)) // octal { LL_ERRS() << "Unable to create directory: " << dir_name << LL_ENDL; } diff --git a/indra/llfilesystem/lldir_win32.cpp b/indra/llfilesystem/lldir_win32.cpp old mode 100755 new mode 100644 index 2b478e5dce..58c080c982 --- a/indra/llfilesystem/lldir_win32.cpp +++ b/indra/llfilesystem/lldir_win32.cpp @@ -376,7 +376,18 @@ std::string LLDir_Win32::getCurPath() bool LLDir_Win32::fileExists(const std::string &filename) const { - return LLFile::exists(filename); + llstat stat_data; + // Check the age of the file + // Now, we see if the files we've gathered are recent... + int res = LLFile::stat(filename, &stat_data); + if (!res) + { + return true; + } + else + { + return false; + } } diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp old mode 100755 new mode 100644 index 0c220fe7cf..541266af4f --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -77,17 +77,21 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil LL_PROFILE_ZONE_SCOPED; const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type); - // not only test for existence but for the file to be not empty - S64 size = LLFile::size(filename); - return size > 0; + 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_warning /*= 0*/) +bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error /*= 0*/) { const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type); - LLFile::remove(filename.c_str(), suppress_warning); + LLFile::remove(filename.c_str(), suppress_error); return true; } @@ -112,10 +116,19 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp } // static -S64 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) +S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) { const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type); - return LLFile::size(filename); + + S32 file_size = 0; + llifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(0, std::ios::end); + file_size = (S32)file.tellg(); + } + + return file_size; } bool LLFileSystem::read(U8* buffer, S32 bytes) @@ -256,7 +269,7 @@ S32 LLFileSystem::tell() const S32 LLFileSystem::getSize() const { - return (S32)LLFileSystem::getFileSize(mFileID, mFileType); + return LLFileSystem::getFileSize(mFileID, mFileType); } S32 LLFileSystem::getMaxSize() const diff --git a/indra/llfilesystem/llfilesystem.h b/indra/llfilesystem/llfilesystem.h index 7188683e7f..10649b6920 100644 --- a/indra/llfilesystem/llfilesystem.h +++ b/indra/llfilesystem/llfilesystem.h @@ -61,10 +61,10 @@ class LLFileSystem void updateFileAccessTime(const std::string& file_path); 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_warning = 0); + 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 S64 getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type); + static S32 getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type); public: static const S32 READ; diff --git a/indra/llfilesystem/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp old mode 100755 new mode 100644 index 17e4ffecf1..13db6fad80 --- a/indra/llfilesystem/tests/lldir_test.cpp +++ b/indra/llfilesystem/tests/lldir_test.cpp @@ -558,8 +558,8 @@ namespace tut LLFile::remove(dir1files[i]); LLFile::remove(dir2files[i]); } - LLFile::remove(dir1); - LLFile::remove(dir2); + LLFile::rmdir(dir1); + LLFile::rmdir(dir2); } template<> template<> diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index 34eeacb273..10fd56a68e 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -467,7 +467,7 @@ bool LLAssetStorage::findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetTyp else { LL_WARNS("AssetStorage") << "Asset vfile " << uuid << ":" << type - << " found in static cache with bad size " << size << ", ignoring" << LL_ENDL; + << " found in static cache with bad size " << file.getSize() << ", ignoring" << LL_ENDL; } } return false; diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp old mode 100755 new mode 100644 index 0522307661..2c35a6acae --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1014,6 +1014,7 @@ void LLShaderMgr::initShaderCache(bool enabled, const LLUUID& old_cache_version, mShaderCacheDir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "shader_cache"); LLFile::mkdir(mShaderCacheDir); + { std::string meta_out_path = gDirUtilp->add(mShaderCacheDir, "shaderdata.llsd"); if (gDirUtilp->fileExists(meta_out_path)) @@ -1058,7 +1059,7 @@ void LLShaderMgr::clearShaderCache() LL_INFOS("ShaderMgr") << "Removing shader cache at " << shader_cache << LL_ENDL; const std::string mask = "*"; gDirUtilp->deleteFilesInDir(shader_cache, mask); - LLFile::remove(shader_cache); + LLFile::rmdir(shader_cache); mShaderBinaryCache.clear(); } @@ -1077,7 +1078,7 @@ void LLShaderMgr::persistShaderCacheMetadata() // Settings and shader cache get saved at different time, thus making // RenderShaderCacheVersion unreliable when running multiple viewer // instances, or for cases where viewer crashes before saving settings. - // Duplicate version to the cache itself. + // Dupplicate version to the cache itself. out["version"] = mShaderCacheVersion; out["shaders"] = LLSD::emptyMap(); LLSD &shaders = out["shaders"]; @@ -1130,11 +1131,11 @@ bool LLShaderMgr::loadCachedProgramBinary(LLGLSLShader* shader) { std::vector in_data; in_data.resize(shader_info.mBinaryLength); - std::error_code ec; - LLFile filep = LLFile(in_path, LLFile::in | LLFile::binary, ec); - if (!ec && (bool)filep) + + LLUniqueFile filep = LLFile::fopen(in_path, "rb"); + if (filep) { - size_t result = filep.read(in_data.data(), in_data.size(), ec); + size_t result = fread(in_data.data(), sizeof(U8), in_data.size(), filep); filep.close(); if (result == in_data.size()) @@ -1179,12 +1180,11 @@ bool LLShaderMgr::saveCachedProgramBinary(LLGLSLShader* shader) if (error == GL_NO_ERROR) { std::string out_path = gDirUtilp->add(mShaderCacheDir, shader->mShaderHash.asString() + ".shaderbin"); - std::error_code ec; - LLFile filep = LLFile(out_path, LLFile::out | LLFile::binary, ec); - if (filep) + LLUniqueFile outfile = LLFile::fopen(out_path, "wb"); + if (outfile) { - filep.write(program_binary.data(), program_binary.size(), ec); - filep.close(); + fwrite(program_binary.data(), sizeof(U8), program_binary.size(), outfile); + outfile.close(); binary_info.mLastUsedTime = (F32)LLTimer::getTotalSeconds(); diff --git a/indra/llxml/tests/llcontrol_test.cpp b/indra/llxml/tests/llcontrol_test.cpp old mode 100755 new mode 100644 index e15117fc29..4192e029c5 --- a/indra/llxml/tests/llcontrol_test.cpp +++ b/indra/llxml/tests/llcontrol_test.cpp @@ -69,7 +69,7 @@ namespace tut LLFile::remove(filename); } LLFile::remove(mTestConfigFile); - LLFile::remove(mTestConfigDir); + LLFile::rmdir(mTestConfigDir); } void writeSettingsFile(const LLSD& config) { diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp old mode 100755 new mode 100644 index 962a91bb73..569fd30b21 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2268,7 +2268,7 @@ void errorCallback(LLError::ELevel level, const std::string &error_string) LLAppViewer::instance()->writeDebugInfo(); std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); - if (!LLFile::isfile(error_marker_file)) + if (!LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB)) { // If marker doesn't exist, create a marker with llerror code for next launch // otherwise don't override existing file @@ -3031,11 +3031,13 @@ void LLAppViewer::initStrings() } else { - if (!LLFile::exists(strings_path_full)) + llstat st; + int rc = LLFile::stat(strings_path_full, &st); + if (rc != 0) { - crash_reason = "The file '" + strings_path_full + "' doesn't seem to exist"; + crash_reason = "The file '" + strings_path_full + "' failed to get status. Error code: " + std::to_string(rc); } - else if (LLFile::isdir(strings_path_full)) + else if (S_ISDIR(st.st_mode)) { crash_reason = "The filename '" + strings_path_full + "' is a directory name"; } @@ -3898,7 +3900,7 @@ void LLAppViewer::processMarkerFiles() bool marker_is_same_version = true; // first, look for the marker created at startup and deleted on a clean exit mMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,MARKER_FILE_NAME); - if (LLFile::isfile(mMarkerFileName)) + if (LLAPRFile::isExist(mMarkerFileName, NULL, LL_APR_RB)) { // File exists... // first, read it to see if it was created by the same version (we need this later) @@ -3990,7 +3992,7 @@ void LLAppViewer::processMarkerFiles() // check for any last exec event report based on whether or not it happened during logout // (the logout marker is created when logout begins) std::string logout_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME); - if(LLFile::isfile(logout_marker_file)) + if(LLAPRFile::isExist(logout_marker_file, NULL, LL_APR_RB)) { if (markerIsSameVersion(logout_marker_file)) { @@ -4002,11 +4004,11 @@ void LLAppViewer::processMarkerFiles() { LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "' found, but versions did not match" << LL_ENDL; } - LLFile::remove(logout_marker_file); + LLAPRFile::remove(logout_marker_file); } // and last refine based on whether or not a marker created during a non-llerr crash is found std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); - if(LLFile::isfile(error_marker_file)) + if(LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB)) { S32 marker_code = getMarkerErrorCode(error_marker_file); if (marker_code >= 0) @@ -4031,7 +4033,7 @@ void LLAppViewer::processMarkerFiles() { LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' marker found, but versions did not match" << LL_ENDL; } - LLFile::remove(error_marker_file); + LLAPRFile::remove(error_marker_file); } #if LL_DARWIN @@ -4058,7 +4060,7 @@ void LLAppViewer::removeMarkerFiles() if (mMarkerFile.getFileHandle()) { mMarkerFile.close() ; - LLFile::remove( mMarkerFileName ); + LLAPRFile::remove( mMarkerFileName ); LL_DEBUGS("MarkerFile") << "removed exec marker '"<getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); - return LLFile::isfile(error_marker_file); + return LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB); } void LLAppViewer::outOfMemorySoftQuit() diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp old mode 100755 new mode 100644 index 66066a45b2..c5c1e01538 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -1500,7 +1500,7 @@ bool LLFloaterPreference::moveTranscriptsAndLog() //Couldn't move the log and created a new directory so remove the new directory if(madeDirectory) { - LLFile::remove(chatLogPath); + LLFile::rmdir(chatLogPath); } return false; } @@ -1526,7 +1526,7 @@ bool LLFloaterPreference::moveTranscriptsAndLog() if(madeDirectory) { - LLFile::remove(chatLogPath); + LLFile::rmdir(chatLogPath); } return false; @@ -2031,15 +2031,17 @@ void LLFloaterPreference::changed() { if (LLConversationLog::instance().getIsLoggingEnabled()) { - getChild("clear_log")->setEnabled(LLConversationLog::instance().getConversations().size() > 0); + getChild("clear_log")->setEnabled(LLConversationLog::instance().getConversations().size() > 0); } else { // onClearLog clears list, then notifies changed() and only then clears file, // so check presence of conversations before checking file, file will cleared later. + llstat st; bool has_logs = LLConversationLog::instance().getConversations().size() > 0 - && LLFile::isfile(LLConversationLog::instance().getFileName()) - && LLFile::size(LLConversationLog::instance().getFileName()) > 0; + && LLFile::stat(LLConversationLog::instance().getFileName(), &st) == 0 + && S_ISREG(st.st_mode) + && st.st_size > 0; getChild("clear_log")->setEnabled(has_logs); } diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp old mode 100755 new mode 100644 index 0bf0946c42..c3bc24c6b9 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -484,47 +484,47 @@ bool LLFloaterUIPreview::postBuild() bool found_en_us = false; std::string language_directory; std::string xui_dir = get_xui_dir(); // directory containing localizations -- don't forget trailing delim - mLanguageSelection->removeall(); // clear out anything temporarily in list from XML + mLanguageSelection->removeall(); // clear out anything temporarily in list from XML LLDirIterator iter(xui_dir, "*"); - while (found) // for every directory + while(found) // for every directory { - if ((found = iter.next(language_directory))) // get next directory + if((found = iter.next(language_directory))) // get next directory { std::string full_path = gDirUtilp->add(xui_dir, language_directory); - if (!LLFile::isdir(full_path.c_str())) // if it's not a directory, skip it + if(LLFile::isfile(full_path.c_str())) // if it's not a directory, skip it { continue; } - if (strncmp("template",language_directory.c_str(), 8) && -1 == language_directory.find(".")) // if it's not the template directory or a hidden directory + if(strncmp("template",language_directory.c_str(),8) && -1 == language_directory.find(".")) // if it's not the template directory or a hidden directory { - if (!strncmp("en",language_directory.c_str(), 5)) // remember if we've seen en, so we can make it default + if(!strncmp("en",language_directory.c_str(),5)) // remember if we've seen en, so we can make it default { found_en_us = true; } else { - mLanguageSelection->add(std::string(language_directory)); // add it to the language selection dropdown menu + mLanguageSelection->add(std::string(language_directory)); // add it to the language selection dropdown menu mLanguageSelection_2->add(std::string(language_directory)); } } } } - if (found_en_us) + if(found_en_us) { - mLanguageSelection->add(std::string("en"), ADD_TOP); // make en first item if we found it - mLanguageSelection_2->add(std::string("en"), ADD_TOP); + mLanguageSelection->add(std::string("en"),ADD_TOP); // make en first item if we found it + mLanguageSelection_2->add(std::string("en"),ADD_TOP); } else { std::string warning = std::string("No EN localization found; check your XUI directories!"); popupAndPrintWarning(warning); } - mLanguageSelection->selectFirstItem(); // select the first item + mLanguageSelection->selectFirstItem(); // select the first item mLanguageSelection_2->selectFirstItem(); - refreshList(); // refresh the list of available floaters + refreshList(); // refresh the list of available floaters return true; } @@ -892,7 +892,8 @@ void LLFloaterUIPreview::displayFloater(bool click, S32 ID) // Add localization to title so user knows whether it's localized or defaulted to en std::string full_path = getLocalizedDirectory() + path; std::string floater_lang = "EN"; - if (LLFile::isfile(full_path.c_str())) // use localized language if the file exists + llstat dummy; + if(!LLFile::stat(full_path.c_str(), &dummy)) // if the file does not exist { floater_lang = getLocStr(ID); } @@ -965,8 +966,9 @@ void LLFloaterUIPreview::onClickEditFloater() } file_path = getLocalizedDirectory() + file_name; - // Does it exist? (Some localized versions may not have it when there are no diffs, and then we try to open a nonexistent file) - if (!LLFile::isfile(file_path.c_str())) // if the file does not exist + // stat file to see if it exists (some localized versions may not have it there are no diffs, and then we try to open an nonexistent file) + llstat dummy; + if(LLFile::stat(file_path.c_str(), &dummy)) // if the file does not exist { popupAndPrintWarning("No file for this floater exists in the selected localization. Opening the EN version instead."); file_path = get_xui_dir() + mDelim + "en" + mDelim + file_name; // open the en version instead, by default @@ -1115,14 +1117,15 @@ void LLFloaterUIPreview::onClickToggleDiffHighlighting() std::string path_in_textfield = mDiffPathTextBox->getText(); // get file path bool error = false; - if(std::string("") == path_in_textfield) // check for blank file + if(std::string("") == path_in_textfield) // check for blank file { std::string warning = "Unable to highlight differences because no file was provided; fill in the relevant text field"; popupAndPrintWarning(warning); error = true; } - if (!LLFile::isfile(path_in_textfield.c_str()) && !error) // check if the file exists (empty check is redundant but useful for the informative error message) + llstat dummy; + if(LLFile::stat(path_in_textfield.c_str(), &dummy) && !error) // check if the file exists (empty check is reduntant but useful for the informative error message) { std::string warning = std::string("Unable to highlight differences because an invalid path to a difference file was provided:\"") + path_in_textfield + "\""; popupAndPrintWarning(warning); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp old mode 100755 new mode 100644 index 4fe661b055..9a991727b2 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -575,7 +575,8 @@ void LLPreviewNotecard::syncExternal() { // Sync with external editor. std::string tmp_file = getTmpFileName(); - if (LLFile::isfile(tmp_file)) // file exists + llstat s; + if (LLFile::stat(tmp_file, &s) == 0) // file exists { if (mLiveFile) mLiveFile->ignoreNextUpdate(); writeToFile(tmp_file); diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp old mode 100755 new mode 100644 index 2c436198e3..c2aa4925bd --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -694,7 +694,8 @@ void LLScriptEdCore::sync() if (mLiveFile) { std::string tmp_file = mLiveFile->filename(); - if (LLFile::isfile(tmp_file)) // file exists + llstat s; + if (LLFile::stat(tmp_file, &s) == 0) // file exists { mLiveFile->ignoreNextUpdate(); writeToFile(tmp_file); diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp old mode 100755 new mode 100644 index a6d81816ce..1a7ce74ccc --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -180,7 +180,8 @@ private: bool LLTextureCacheLocalFileWorker::doRead() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - S32 local_size = (S32)LLFile::size(mFileName); + S32 local_size = LLAPRFile::size(mFileName, mCache->getLocalAPRFilePool()); + if (local_size > 0 && mFileName.size() > 4) { mDataSize = local_size; // Only a complete file is valid @@ -209,7 +210,8 @@ bool LLTextureCacheLocalFileWorker::doRead() } mReadData = (U8*)ll_aligned_malloc_16(mDataSize); - S32 bytes_read = (S32)LLFile::read(mFileName, mReadData, mOffset, mDataSize); + S32 bytes_read = LLAPRFile::readEx(mFileName, mReadData, mOffset, mDataSize, mCache->getLocalAPRFilePool()); + if (bytes_read != mDataSize) { // LL_WARNS() << "Error reading file from local cache: " << mFileName @@ -294,7 +296,7 @@ bool LLTextureCacheRemoteWorker::doRead() // Is it a JPEG2000 file? { local_filename = filename + ".j2c"; - local_size = (S32)LLFile::size(local_filename); + local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool()); if (local_size > 0) { mImageFormat = IMG_CODEC_J2C; @@ -304,7 +306,7 @@ bool LLTextureCacheRemoteWorker::doRead() if (local_size == 0) { local_filename = filename + ".jpg"; - local_size = (S32)LLFile::size(local_filename); + local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool()); if (local_size > 0) { mImageFormat = IMG_CODEC_JPEG; @@ -315,7 +317,7 @@ bool LLTextureCacheRemoteWorker::doRead() if (local_size == 0) { local_filename = filename + ".tga"; - local_size = (S32)LLFile::size(local_filename); + local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool()); if (local_size > 0) { mImageFormat = IMG_CODEC_TGA; @@ -344,10 +346,12 @@ bool LLTextureCacheRemoteWorker::doRead() if (mReadData) { - S32 bytes_read = (S32)LLFile::read(local_filename, - mReadData, - mOffset, - mDataSize); + S32 bytes_read = LLAPRFile::readEx( local_filename, + mReadData, + mOffset, + mDataSize, + mCache->getLocalAPRFilePool()); + if (bytes_read != mDataSize) { LL_WARNS() << "Error reading file from local cache: " << local_filename @@ -406,7 +410,8 @@ bool LLTextureCacheRemoteWorker::doRead() mReadData = (U8*)ll_aligned_malloc_16(size); if (mReadData) { - S32 bytes_read = (S32)LLFile::read(mCache->mHeaderDataFileName, mReadData, offset, size); + S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName, + mReadData, offset, size, mCache->getLocalAPRFilePool()); if (bytes_read != size) { LL_WARNS() << "LLTextureCacheWorker: " << mID @@ -441,9 +446,9 @@ bool LLTextureCacheRemoteWorker::doRead() if (!done && (mState == BODY)) { std::string filename = mCache->getTextureFileName(mID); - S32 filesize = (S32)LLFile::size(filename); + S32 filesize = LLAPRFile::size(filename, mCache->getLocalAPRFilePool()); - if (filesize > 0 && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset) + if (filesize && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset) { S32 max_datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize - mOffset; mDataSize = llmin(max_datasize, mDataSize); @@ -482,9 +487,10 @@ bool LLTextureCacheRemoteWorker::doRead() mReadData = data; // Read the data at last - S32 bytes_read = (S32)LLFile::read(filename, - mReadData + data_offset, - file_offset, file_size); + S32 bytes_read = LLAPRFile::readEx(filename, + mReadData + data_offset, + file_offset, file_size, + mCache->getLocalAPRFilePool()); if (bytes_read != file_size) { LL_WARNS() << "LLTextureCacheWorker: " << mID @@ -630,13 +636,13 @@ bool LLTextureCacheRemoteWorker::doWrite() U8* padBuffer = (U8*)ll_aligned_malloc_16(TEXTURE_CACHE_ENTRY_SIZE); memset(padBuffer, 0, TEXTURE_CACHE_ENTRY_SIZE); // Init with zeros memcpy(padBuffer, mWriteData, mDataSize); // Copy the write buffer - bytes_written = (S32)LLFile::write(mCache->mHeaderDataFileName, padBuffer, offset, size); + bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, padBuffer, offset, size, mCache->getLocalAPRFilePool()); ll_aligned_free_16(padBuffer); } else { // Write the header record (== first TEXTURE_CACHE_ENTRY_SIZE bytes of the raw file) in the header file - bytes_written = (S32)LLFile::write(mCache->mHeaderDataFileName, mWriteData, offset, size); + bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, mCache->getLocalAPRFilePool()); } if (bytes_written <= 0) @@ -672,11 +678,15 @@ bool LLTextureCacheRemoteWorker::doWrite() else { S32 file_size = mDataSize - TEXTURE_CACHE_ENTRY_SIZE; + { // build the cache file name from the UUID std::string filename = mCache->getTextureFileName(mID); // LL_INFOS() << "Writing Body: " << filename << " Bytes: " << file_offset+file_size << LL_ENDL; - S32 bytes_written = (S32)LLFile::write(filename, mWriteData + TEXTURE_CACHE_ENTRY_SIZE, 0, file_size); + S32 bytes_written = LLAPRFile::writeEx(filename, + mWriteData + TEXTURE_CACHE_ENTRY_SIZE, + 0, file_size, + mCache->getLocalAPRFilePool()); if (bytes_written <= 0) { LL_WARNS() << "LLTextureCacheWorker: " << mID @@ -881,7 +891,7 @@ bool LLTextureCache::isInLocal(const LLUUID& id) // Is it a JPEG2000 file? { local_filename = filename + ".j2c"; - local_size = (S32)LLFile::size(local_filename); + local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool()); if (local_size > 0) { return true ; @@ -891,7 +901,7 @@ bool LLTextureCache::isInLocal(const LLUUID& id) // If not, is it a jpeg file? { local_filename = filename + ".jpg"; - local_size = (S32)LLFile::size(local_filename); + local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool()); if (local_size > 0) { return true ; @@ -901,7 +911,7 @@ bool LLTextureCache::isInLocal(const LLUUID& id) // Hmm... What about a targa file? (used for UI texture mostly) { local_filename = filename + ".tga"; - local_size = (S32)LLFile::size(local_filename); + local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool()); if (local_size > 0) { return true ; @@ -933,6 +943,8 @@ const char* fast_cache_filename = "FastCache.cache"; void LLTextureCache::setDirNames(ELLPath location) { + std::string delem = gDirUtilp->getDirDelimiter(); + mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, entries_filename); mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, cache_filename); mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname); @@ -954,10 +966,11 @@ void LLTextureCache::purgeCache(ELLPath location, bool remove_dir) if(LLFile::isdir(mTexturesDirName)) { std::string file_name = gDirUtilp->getExpandedFilename(location, entries_filename); - LLFile::remove(file_name); + // mHeaderAPRFilePoolp because we are under header mutex, and can be in main thread + LLAPRFile::remove(file_name, mHeaderAPRFilePoolp); file_name = gDirUtilp->getExpandedFilename(location, cache_filename); - LLFile::remove(file_name); + LLAPRFile::remove(file_name, mHeaderAPRFilePoolp); purgeAllTextures(true); } @@ -1058,9 +1071,10 @@ void LLTextureCache::readEntriesHeader() { // mHeaderEntriesInfo initializes to default values so safe not to read it llassert_always(mHeaderAPRFile == NULL); - if (LLFile::isfile(mHeaderEntriesFileName)) + if (LLAPRFile::isExist(mHeaderEntriesFileName, mHeaderAPRFilePoolp)) { - LLFile::read(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); + LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), + mHeaderAPRFilePoolp); } else //create an empty entries header. { @@ -1076,7 +1090,7 @@ void LLTextureCache::setEntriesHeader() // For simplicity we use predefined size of header, so if version string // doesn't fit, either getEngineInfo() returned malformed string or // sHeaderEncoderStringSize need to be increased. - // Also take into account that c_str() returns additional null character + // Also take into accout that c_str() returns additional null character LL_ERRS() << "Version string doesn't fit in header" << LL_ENDL; } @@ -1091,7 +1105,8 @@ void LLTextureCache::writeEntriesHeader() llassert_always(mHeaderAPRFile == NULL); if (!mReadOnly) { - LLFile::write(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); + LLAPRFile::writeEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), + mHeaderAPRFilePoolp); } } @@ -1195,7 +1210,8 @@ void LLTextureCache::writeEntryToHeaderImmediately(S32& idx, Entry& entry, bool idx = -1 ;//mark the idx invalid. return ; } - aprfile->seek(APR_SET, offset); + + mHeaderAPRFile->seek(APR_SET, offset); } else { @@ -1599,7 +1615,7 @@ void LLTextureCache::purgeAllTextures(bool purge_directories) gDirUtilp->deleteFilesInDir(mTexturesDirName, mask); // headers, fast cache if (purge_directories) { - LLFile::remove(mTexturesDirName); + LLFile::rmdir(mTexturesDirName); } } mHeaderIDMap.clear(); @@ -1779,7 +1795,8 @@ void LLTextureCache::purgeTextures(bool validate) { std::string filename = getTextureFileName(entries[idx].mID); LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL; - S32 bodysize = (S32)LLFile::size(filename); + // mHeaderAPRFilePoolp because this is under header mutex in main thread + S32 bodysize = LLAPRFile::size(filename, mHeaderAPRFilePoolp); if (bodysize != entries[idx].mBodySize) { LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mBodySize << filename << LL_ENDL; @@ -2130,7 +2147,7 @@ void LLTextureCache::openFastCache(bool first_time) mFastCachePadBuffer = (U8*)ll_aligned_malloc_16(TEXTURE_FAST_CACHE_ENTRY_SIZE); } mFastCachePoolp = new LLVolatileAPRPool(); // is_local= true by default, so not thread safe by default - if (LLFile::isfile(mFastCacheFileName)) + if (LLAPRFile::isExist(mFastCacheFileName, mFastCachePoolp)) { mFastCachep = new LLAPRFile(mFastCacheFileName, APR_READ|APR_WRITE|APR_BINARY, mFastCachePoolp) ; } @@ -2213,7 +2230,9 @@ void LLTextureCache::removeCachedTexture(const LLUUID& id) mTexturesSizeMap.erase(id); } mHeaderIDMap.erase(id); - LLFile::remove(getTextureFileName(id)); + // We are inside header's mutex so mHeaderAPRFilePoolp is safe to use, + // but getLocalAPRFilePool() is not safe, it might be in use by worker + LLAPRFile::remove(getTextureFileName(id), mHeaderAPRFilePoolp); } //called after mHeaderMutex is locked. @@ -2226,7 +2245,9 @@ void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename) if (entry.mBodySize == 0) // Always attempt to remove when mBodySize > 0. { // Sanity check. Shouldn't exist when body size is 0. - if (LLFile::isfile(filename)) + // We are inside header's mutex so mHeaderAPRFilePoolp is safe to use, + // but getLocalAPRFilePool() is not safe, it might be in use by worker + if (LLAPRFile::isExist(filename, mHeaderAPRFilePoolp)) { LL_WARNS("TextureCache") << "Entry has body size of zero but file " << filename << " exists. Deleting this file, too." << LL_ENDL; } @@ -2246,7 +2267,7 @@ void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename) if (file_maybe_exists) { - LLFile::remove(filename); + LLAPRFile::remove(filename, mHeaderAPRFilePoolp); } } diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index e76d340eda..141f370ecb 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -182,7 +182,7 @@ void LLViewerAssetStorage::storeAssetData( else { // LLAssetStorage metric: Successful Request - S32 size = (S32)LLFileSystem::getFileSize(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 ); diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 5ac7d6b1fe..65a69acc88 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -45,7 +45,11 @@ #include "llappviewer.h" #include "llviewerstats.h" #include "llfilesystem.h" +#include "llgesturemgr.h" +#include "llpreviewnotecard.h" +#include "llpreviewgesture.h" #include "llcoproceduremanager.h" +#include "llthread.h" #include "llkeyframemotion.h" #include "lldatapacker.h" #include "llvoavatarself.h" @@ -401,7 +405,6 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() std::string errorMessage; std::string errorLabel; - std::error_code ec; bool error = false; @@ -472,38 +475,30 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() error = true; // read from getFileName() - LLFile infile(getFileName(), LLFile::in | LLFile::binary, ec); - if (ec || !infile) + LLAPRFile infile; + infile.open(getFileName(),LL_APR_RB); + if (!infile.getFileHandle()) { LL_WARNS() << "Couldn't open file for reading: " << getFileName() << LL_ENDL; errorMessage = llformat("Failed to open animation file %s\n", getFileName().c_str()); } else { - S64 size = infile.size(ec); - if (ec || size <= 0) - { - LLError::LLUserWarningMsg::showMissingFiles(); - LL_ERRS() << "Invalid file" << LL_ENDL; - } - else if (size > INT_MAX) - { - LL_ERRS() << "File is too big, size: " << size << LL_ENDL; - } + S32 size = LLAPRFile::size(getFileName()); U8* buffer = new(std::nothrow) U8[size]; if (!buffer) { LLError::LLUserWarningMsg::showOutOfMemory(); LL_ERRS() << "Bad memory allocation for buffer, size: " << size << LL_ENDL; } - S64 size_read = infile.read(buffer, size, ec); - if (ec || size_read != size) + S32 size_read = infile.read(buffer,size); + if (size_read != size) { errorMessage = llformat("Failed to read animation file %s: wanted %d bytes, got %d\n", getFileName().c_str(), size, size_read); } else { - LLDataPackerBinaryBuffer dp(buffer, (S32)size); + LLDataPackerBinaryBuffer dp(buffer, size); LLKeyframeMotion *motionp = new LLKeyframeMotion(getAssetId()); motionp->setCharacter(gAgentAvatarp); if (motionp->deserialize(dp, getAssetId(), false)) @@ -549,17 +544,18 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() setAssetType(assetType); // copy this file into the cache for upload - LLFile infile(filename, LLFile::in | LLFile::binary, ec); - if (!ec && infile.size(ec) > 0) + S32 file_size; + LLAPRFile infile; + infile.open(filename, LL_APR_RB, NULL, &file_size); + if (infile.getFileHandle()) { LLFileSystem file(getAssetId(), assetType, LLFileSystem::APPEND); - S64 read_bytes; const S32 buf_size = 65536; U8 copy_buf[buf_size]; - while (((read_bytes = infile.read(copy_buf, buf_size, ec))) > 0) + while ((file_size = infile.read(copy_buf, buf_size))) { - file.write(copy_buf, (S32)read_bytes); + file.write(copy_buf, file_size); } } else @@ -573,6 +569,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() } return LLSD(); + } //========================================================================= diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp old mode 100755 new mode 100644 index 7534d778a5..35ac7919ac --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -1140,12 +1140,15 @@ std::string getProfileStatsFilename() // same second), may produce (e.g.) sec==61, but avoids collisions and // preserves chronological filename sort order. std::string name; + std::error_code ec; do { // base + missing 2-digit seconds, append ".json" // post-increment sec in case we have to try again name = stringize(base, std::setw(2), std::setfill('0'), sec++, ".json"); - } while (LLFile::exists(fsyspath(name))); + } while (std::filesystem::exists(fsyspath(name), ec)); + // Ignoring ec means we might potentially return a name that does already + // exist -- but if we can't check its existence, what more can we do? return name; } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp old mode 100755 new mode 100644 index 5cb05460bc..a77b9f6103 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1827,11 +1827,12 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ user_data_path_cache += gDirUtilp->getDirDelimiter(); // See if the plugin executable exists - if (!LLFile::isfile(launcher_name)) + llstat s; + if(LLFile::stat(launcher_name, &s)) { LL_WARNS_ONCE("Media") << "Couldn't find launcher at " << launcher_name << LL_ENDL; } - else if (!LLFile::isfile(plugin_name)) + else if(LLFile::stat(plugin_name, &s)) { #if !LL_LINUX LL_WARNS_ONCE("Media") << "Couldn't find plugin at " << plugin_name << LL_ENDL; diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp old mode 100755 new mode 100644 index cdc41baa88..5d456b1a19 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -1250,7 +1250,7 @@ void LLVOCache::removeCache(ELLPath location, bool started) std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname); LL_INFOS() << "Removing cache at " << cache_dir << LL_ENDL; gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files - LLFile::remove(cache_dir); + LLFile::rmdir(cache_dir); clearCacheInMemory(); mInitialized = false; @@ -1370,7 +1370,7 @@ void LLVOCache::removeFromCache(HeaderEntryInfo* entry) std::string filename; getObjectCacheFilename(entry->mHandle, filename); LL_WARNS("GLTF", "VOCache") << "Removing object cache for handle " << entry->mHandle << "Filename: " << filename << LL_ENDL; - LLFile::remove(filename); + LLAPRFile::remove(filename, mLocalAPRFilePoolp); // Note: `removeFromCache` should take responsibility for cleaning up all cache artefacts specfic to the handle/entry. // as such this now includes the generic extras @@ -1394,7 +1394,7 @@ void LLVOCache::readCacheHeader() clearCacheInMemory(); bool success = true ; - if (LLFile::isfile(mHeaderFileName)) + if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp)) { LLAPRFile apr_file(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp); diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp old mode 100755 new mode 100644 index e58a6577f1..d132cbfa36 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -943,7 +943,8 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() gDirUtilp->append(exe_path, "SLVoice"); #endif // See if the vivox executable exists - if (LLFile::isfile(exe_path)) + llstat s; + if (!LLFile::stat(exe_path, &s)) { // vivox executable exists. Build the command line and launch the daemon. LLProcess::Params params; diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt old mode 100755 new mode 100644 index 69c9660575..246fc5e6f8 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -16,7 +16,6 @@ set(test_SOURCE_FILES llbuffer_tut.cpp lldoubledispatch_tut.cpp llevents_tut.cpp - llfile_tut.cpp llhttpdate_tut.cpp llhttpnode_tut.cpp lliohttpserver_tut.cpp @@ -68,7 +67,7 @@ target_link_libraries(lltest if (WINDOWS) set_target_properties(lltest - PROPERTIES + PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT" LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\"" RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}" @@ -86,10 +85,10 @@ set(TEST_EXE $) SET_TEST_PATH(LD_LIBRARY_PATH) -LL_TEST_COMMAND(command +LL_TEST_COMMAND(command "${LD_LIBRARY_PATH}" "${TEST_EXE}" - "--output=${CMAKE_CURRENT_BINARY_DIR}/cpp_test_results.txt" + "--output=${CMAKE_CURRENT_BINARY_DIR}/cpp_test_results.txt" "--touch=${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt") ADD_CUSTOM_COMMAND( @@ -102,11 +101,11 @@ ADD_CUSTOM_COMMAND( set(test_results ${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt) -# This should cause the test executable to be built, but not +# This should cause the test executable to be built, but not # run if LL_TESTS is disabled. This will hopefully keep the -# tests up to date with any code changes changes even if +# tests up to date with any code changes changes even if # developers choose to disable LL_TESTS. -if (LL_TESTS) +if (LL_TESTS) add_custom_target(tests_ok ALL DEPENDS ${test_results}) if(DARWIN) # Support our "@executable_path/../Resources" load path for our test diff --git a/indra/test/llfile_tut.cpp b/indra/test/llfile_tut.cpp deleted file mode 100755 index 9f2a9b2988..0000000000 --- a/indra/test/llfile_tut.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/** - * @file llfile_tut.cpp - * @author Frederick Martian - * @date 2025-11 - * @brief LLFile test cases. - * - * $LicenseInfo:firstyear=2025&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2025, 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 -#include "lltut.h" -#include "linden_common.h" -#include "llfile.h" - -namespace tut -{ - static void clear_entire_dir(std::filesystem::path &dir) - { - std::error_code ec; - std::filesystem::remove_all(dir, ec); - } - - static std::filesystem::path append_filename(const std::filesystem::path& dir, const std::string& element) - { - std::filesystem::path path = dir; - return path.append(element); - } - - static std::filesystem::path get_testdir(const std::filesystem::path& tempdir) - { - return append_filename(tempdir, std::string("test_dir")); - } - - struct llfile_test - { - std::filesystem::path tempdir = LLFile::tmpdir(); - std::filesystem::path testdir = get_testdir(tempdir); - }; - typedef test_group llfile_test_t; - typedef llfile_test_t::object llfile_test_object_t; - tut::llfile_test_t tut_llfile_test("llfile_test"); - - template<> template<> - void llfile_test_object_t::test<1>() - { - // Test creating directories and files and deleting them and checking if the - // relevant status functions work as expected - ensure("LLFile::tmpdir() empty", !tempdir.empty()); - ensure("LLFile::tmpdir() doesn't exist", LLFile::exists(tempdir.string())); - ensure("LLFile::tmpdir() is not a directory", LLFile::isdir(tempdir.string())); - ensure("LLFile::tmpdir() should not be a file", !LLFile::isfile(tempdir.string())); - - // Make sure there is nothing left from a previous test run - clear_entire_dir(testdir); - ensure("llfile_test should not exist anymore", !LLFile::exists(testdir.string())); - - int rc = LLFile::mkdir(testdir.string()); - ensure("LLFile::mkdir() failed", rc == 0); - ensure("llfile_test should be a directory", LLFile::isdir(testdir.string())); - rc = LLFile::mkdir(testdir.string()); - ensure("LLFile::mkdir() should not fail when the directory already exists", rc == 0); - - std::filesystem::path testfile1 = testdir; - testfile1.append("llfile_test.dat"); - ensure("llfile_test1.dat should not yet exist", !LLFile::exists(testfile1.string())); - - const char* testdata = "testdata"; - S64 bytes = LLFile::write(testfile1.string(), testdata, 0, sizeof(testdata)); - ensure("LLFile::write() did not write correctly", bytes == sizeof(testdata)); - - rc = LLFile::remove(testfile1.string()); - ensure("LLFile::remove() for file test_file.dat", rc == 0); - ensure("llfile_test.dat should not exist anymore", !LLFile::exists(testfile1.string())); - ensure("llfile_test.dat should not be a file", !LLFile::isfile(testfile1.string())); - ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1.string())); - ensure("llfile_test.dat should not be a symlink", !LLFile::islink(testfile1.string())); - - rc = LLFile::remove(testdir.string()); - ensure("LLFile::remove() for directory llfile_test failed", rc == 0); - ensure("llfile_test should not exist anymore", !LLFile::exists(testdir.string())); - } - - template<> template<> - void llfile_test_object_t::test<2>() - { - // High level static file IO functions to read and write data files - LLFile::mkdir(testdir.string()); - ensure("llfile_test should exist", LLFile::isdir(testdir.string())); - - std::filesystem::path testfile1 = testdir; - testfile1.append("llfile_test.dat"); - - std::string testdata1("testdata"); - std::string testdata2("datateststuff"); - std::time_t current = time(nullptr); - S64 bytes = LLFile::write(testfile1.string(), testdata1.c_str(), 0, testdata1.length()); - ensure("LLFile::write() did not write correctly", bytes == testdata1.length()); - ensure("llfile_test.dat should exist", LLFile::exists(testfile1.string())); - ensure("llfile_test.dat should be a file", LLFile::isfile(testfile1.string())); - ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1.string())); - - bytes = LLFile::size(testfile1.string()); - ensure("LLFile::size() did not return the correct size", bytes == testdata1.length()); - - std::string data = LLFile::getContents(testfile1.string()); - ensure("LLFile::getContents() did not return the correct size data", data.length() == testdata1.length()); - ensure_memory_matches("LLFile::getContents() did not read correct data", testdata1.c_str(), (U32)testdata1.length(), data.c_str(), (U32)data.length()); - - std::time_t ctime = LLFile::getCreationTime(testfile1.string()); - ensure_approximately_equals_range("LLFile::getCreationTime() did not return correct time", (F32)(ctime - current), 0.f, 1); - - std::time_t mtime = LLFile::getModificationTime(testfile1.string()); - ensure_approximately_equals_range("LLFile::getModificationTime() did not return correct time", (F32)(mtime - current), 0.f, 1); - - char buffer[1024]; - bytes = LLFile::read(testfile1.string(), buffer, 0, testdata1.length()); - ensure("LLFile:read() did not return the correct size", bytes == testdata1.length()); - ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes); - - // What if we try to read more data than there is in the file? - bytes = LLFile::read(testfile1.string(), buffer, 0, bytes + 10); - ensure("LLFile:read() did not correctly stop on eof", bytes == testdata1.length()); - ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes); - - // Let's append more data - bytes = LLFile::write(testfile1.string(), testdata2.c_str(), -1, testdata2.length()); - ensure("LLFile::write() did not write correctly", bytes == testdata2.length()); - - bytes = LLFile::size(testfile1.string()); - ensure("LLFile::size() did not return the correct size", bytes == testdata1.length() + testdata2.length()); - bytes = LLFile::read(testfile1.string(), buffer, 0, bytes); - ensure("LLFile:read() did not read correct number of bytes", bytes == testdata1.length() + testdata2.length()); - ensure_memory_matches("LLFile:read() did not read correct testdata1", testdata1.c_str(), (U32)testdata1.length(), buffer, (U32)testdata1.length()); - ensure_memory_matches("LLFile:read() did not read correct testdata2", testdata2.c_str(), (U32)testdata2.length(), buffer + testdata1.length(), (U32)testdata2.length()); - } - - template<> template<> - void llfile_test_object_t::test<3>() - { - const size_t numints = 1024; - - // Testing the LLFile class implementation - std::filesystem::path testfile = testdir; - testfile.append("llfile_test.bin"); - - int data[numints]; - for (int &t : data) - { - t = rand(); - } - - std::error_code ec; - LLFile fileout(testfile.string(), LLFile::out, ec); - ensure("LLFile constructor did not open correctly", (bool)fileout); - ensure("error_code from LLFile constructor should not indicate an error", !ec); - if (fileout) - { - S64 length = fileout.size(ec); - ensure("freshly created file should be empty", length == 0); - ensure("error_code from LLFile::size() should not indicate an error", !ec); - S64 bytes = fileout.write(data, sizeof(data), ec); - ensure("LLFile::write() did not write correctly", bytes == sizeof(data)); - ensure("error_code from LLFile::write() should not indicate an error", !ec); - bytes = fileout.write(data, sizeof(data), ec); - ensure("LLFile::write() did not write correctly", bytes == sizeof(data)); - ensure("error_code from LLFile::write() should not indicate an error", !ec); - bytes = fileout.size(ec); - ensure("LLFile::size() returned wrong size", bytes == 2 * sizeof(data)); - ensure("error_code from LLFile::size() should not indicate an error", !ec); - fileout.close(); - } - - LLFile filein(testfile.string(), LLFile::in, ec); - ensure("LLFile constructor did not open correctly", (bool)filein); - ensure("error_code from LLFile constructor should not indicate an error", !ec); - if (filein) - { - S64 length = filein.size(ec); - ensure("LLFile::size() returned wrong size", length == 2 * sizeof(data)); - ensure("error_code from LLFile::size() should not indicate an error", !ec); - char* buffer = (char*)malloc(length); - S64 bytes = filein.read(buffer, length, ec); - ensure("LLFile::read() did not read correctly", bytes == length); - ensure("error_code from LLFile::read() should not indicate an error", !ec); - ensure_memory_matches("LLFile:read() did not read correct data1", data, (U32)sizeof(data), buffer, (U32)sizeof(data)); - ensure_memory_matches("LLFile:read() did not read correct data2", data, (U32)sizeof(data), buffer + sizeof(data), (U32)sizeof(data)); - S64 offset = filein.tell(ec); - ensure("LLFile::tell() returned a bad offset", offset == length); - ensure("error_code from LLFile::read() should not indicate an error", !ec); - offset = sizeof(data) / 2; - int rc = filein.seek(offset, ec); - ensure("LLFile::seek() indicated an error", rc == 0); - ensure("error_code from LLFile::seek() should not indicate an error", !ec); - bytes = filein.read(buffer, 2 * sizeof(data), ec); - ensure("LLFile::read() did not read correctly", bytes == sizeof(data) + offset); - ensure("error_code from LLFile::read() should not indicate an error", !ec); - ensure_memory_matches("LLFile:read() did not read correct data3", (char*)data + offset, (U32)offset, buffer, (U32)offset); - ensure_memory_matches("LLFile:read() did not read correct data4", (char*)data, (U32)sizeof(data), buffer + offset, (U32)sizeof(data)); - filein.close(); - - free(buffer); - } - } - - template<> template<> - void llfile_test_object_t::test<4>() - { - // Testing the LLFile class implementation with wrong paths and parameters - std::filesystem::path testfile = testdir; - testfile.append("llfile_test.bin"); - - std::error_code ec; - LLFile file(testfile.string(), LLFile::out | LLFile::noreplace, ec); - ensure("LLFile constructor should not have opened the already existing file", !file); - ensure("error_code from LLFile constructor should indicate an error", (bool)ec); - - LLFile::remove(testfile.string()); - file = LLFile(testfile.string(), LLFile::out | LLFile::app | LLFile::trunc, ec); - ensure("LLFile constructor should not have opened the file with conflicting flags", !file); - ensure("error_code from LLFile constructor should indicate an error", (bool)ec); - - file = LLFile(testfile.string(), LLFile::out | LLFile::app | LLFile::noreplace, ec); - ensure("LLFile constructor should not have opened the file with conflicting flags", !file); - ensure("error_code from LLFile constructor should indicate an error", (bool)ec); - - testfile = testdir; - testfile.append("llfile_test"); - testfile.append("llfile_test.bin"); - - file = LLFile(testfile.string(), LLFile::in, ec); - ensure("LLFile constructor should not have been able to open the file in the non-existing directory", !file); - ensure("error_code from LLFile constructor should indicate an error", (bool)ec); - } -} // namespace tut diff --git a/indra/test/llmessageconfig_tut.cpp b/indra/test/llmessageconfig_tut.cpp old mode 100755 new mode 100644 index b8942e99b3..93443467a2 --- a/indra/test/llmessageconfig_tut.cpp +++ b/indra/test/llmessageconfig_tut.cpp @@ -62,7 +62,7 @@ namespace tut int rmfile = LLFile::remove((mTestConfigDir + "/message.xml")); ensure_equals("rmfile value", rmfile, 0); // rm temp dir - int rmdir = LLFile::remove(mTestConfigDir); + int rmdir = LLFile::rmdir(mTestConfigDir); ensure_equals("rmdir value", rmdir, 0); } diff --git a/indra/test/message_tut.cpp b/indra/test/message_tut.cpp old mode 100755 new mode 100644 index 6fb2b92803..11cd710ef6 --- a/indra/test/message_tut.cpp +++ b/indra/test/message_tut.cpp @@ -113,7 +113,7 @@ namespace tut ensure_equals("rmfile value", rmfile, 0); // rm temp dir - int rmdir = LLFile::remove(mTestConfigDir); + int rmdir = LLFile::rmdir(mTestConfigDir); ensure_equals("rmdir value", rmdir, 0); } -- cgit v1.3 From 380da095083077a4b805a8d3b868266341944d9c Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 10 Dec 2025 19:00:16 -0800 Subject: Fix failure to reconnect after disconnect and occasional dropout issue (#5126) * Fix failure to reconnect after disconnect and occasional dropout issue We were occasionally seeing dropouts which may have been caused by ICE renegotiate requests. The code is there to reconnect in that case, but there were a few bugs, some of which were likely due to the webrtc upgrade. Also, we were seeing failures to reconnect after voice server restart. There were some issues with the PTT button that came up after the above issue was fixed. * We need to set mute state for p2p/adhoc/group calls as well --- indra/llwebrtc/llwebrtc.cpp | 3 ++- indra/newview/llvoicewebrtc.cpp | 14 ++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index d1bae49784..8e08239ee6 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -814,6 +814,7 @@ LLWebRTCPeerConnectionImpl::~LLWebRTCPeerConnectionImpl() { mSignalingObserverList.clear(); mDataObserverList.clear(); + mPeerConnectionFactory.release(); if (mPendingJobs > 0) { RTC_LOG(LS_ERROR) << __FUNCTION__ << "Destroying a connection that has " << std::to_string(mPendingJobs) << " unfinished jobs that might cause workers to crash"; @@ -877,7 +878,6 @@ void LLWebRTCPeerConnectionImpl::terminate() } mPendingJobs--; }); - mPeerConnectionFactory.release(); } void LLWebRTCPeerConnectionImpl::setSignalingObserver(LLWebRTCSignalingObserver *observer) { mSignalingObserverList.emplace_back(observer); } @@ -1004,6 +1004,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti } webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offerOptions; + this->AddRef(); // CreateOffer will deref this when it's done. Without this, the callbacks never get called. mPeerConnection->CreateOffer(this, offerOptions); mPendingJobs--; }); diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 3efcd763e3..80a0e3e5c0 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -2549,10 +2549,7 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded() LL::WorkQueue::postMaybe(mMainQueue, [=, this] { LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL; - if (!mShutDown) - { - setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); - } + setVoiceConnectionState(VOICE_STATE_SESSION_RETRY); mCurrentStatus = LLVoiceClientStatusObserver::ERROR_UNKNOWN; }); } @@ -2898,9 +2895,10 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() // this connection. // For spatial this connection will come up as muted, but will be set to the appropriate // value later on when we determine the regions we connect to. - if (!isSpatial()) + if (isSpatial()) { - mWebRTCAudioInterface->setMute(mMuted); + // we'll determine primary state later and set mute accordinly + mPrimary = false; } mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID, mRegionID); @@ -2924,6 +2922,10 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() LLWebRTCVoiceClient::getInstance()->updatePosition(); LLWebRTCVoiceClient::getInstance()->sendPositionUpdate(true); } + else + { + mWebRTCAudioInterface->setMute(mMuted); + } } break; } -- cgit v1.3 From 149f223559ad5a8c810f26c9708ab76913734b4e Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 11 Dec 2025 10:04:20 -0500 Subject: Remove dead llkeyframemotionparam and llstatemachine from llcharacter Signed-off-by: Rye --- indra/llcharacter/CMakeLists.txt | 4 - indra/llcharacter/llkeyframemotionparam.cpp | 436 ---------------------------- indra/llcharacter/llkeyframemotionparam.h | 169 ----------- indra/llcharacter/llmotioncontroller.h | 1 - indra/llcharacter/llstatemachine.cpp | 384 ------------------------ indra/llcharacter/llstatemachine.h | 147 ---------- 6 files changed, 1141 deletions(-) delete mode 100644 indra/llcharacter/llkeyframemotionparam.cpp delete mode 100644 indra/llcharacter/llkeyframemotionparam.h delete mode 100644 indra/llcharacter/llstatemachine.cpp delete mode 100644 indra/llcharacter/llstatemachine.h (limited to 'indra') diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt index bc45eb474a..c6513cdb66 100644 --- a/indra/llcharacter/CMakeLists.txt +++ b/indra/llcharacter/CMakeLists.txt @@ -17,14 +17,12 @@ set(llcharacter_SOURCE_FILES lljointsolverrp3.cpp llkeyframefallmotion.cpp llkeyframemotion.cpp - llkeyframemotionparam.cpp llkeyframestandmotion.cpp llkeyframewalkmotion.cpp llmotioncontroller.cpp llmotion.cpp llmultigesture.cpp llpose.cpp - llstatemachine.cpp lltargetingmotion.cpp llvisualparam.cpp ) @@ -45,14 +43,12 @@ set(llcharacter_HEADER_FILES lljointstate.h llkeyframefallmotion.h llkeyframemotion.h - llkeyframemotionparam.h llkeyframestandmotion.h llkeyframewalkmotion.h llmotion.h llmotioncontroller.h llmultigesture.h llpose.h - llstatemachine.h lltargetingmotion.h llvisualparam.h ) diff --git a/indra/llcharacter/llkeyframemotionparam.cpp b/indra/llcharacter/llkeyframemotionparam.cpp deleted file mode 100644 index e4552b96c9..0000000000 --- a/indra/llcharacter/llkeyframemotionparam.cpp +++ /dev/null @@ -1,436 +0,0 @@ -/** - * @file llkeyframemotionparam.cpp - * @brief Implementation of LLKeyframeMotion class. - * - * $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$ - */ - -//----------------------------------------------------------------------------- -// Header Files -//----------------------------------------------------------------------------- -#include "linden_common.h" - -#include "llkeyframemotionparam.h" -#include "llcharacter.h" -#include "llmath.h" -#include "m3math.h" -#include "lldir.h" -#include "llanimationstates.h" - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam class -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam() -// Class Constructor -//----------------------------------------------------------------------------- -LLKeyframeMotionParam::LLKeyframeMotionParam( const LLUUID &id) : LLMotion(id) -{ - mDefaultKeyframeMotion = NULL; - mCharacter = NULL; - - mEaseInDuration = 0.f; - mEaseOutDuration = 0.f; - mDuration = 0.f; - mPriority = LLJoint::LOW_PRIORITY; -} - - -//----------------------------------------------------------------------------- -// ~LLKeyframeMotionParam() -// Class Destructor -//----------------------------------------------------------------------------- -LLKeyframeMotionParam::~LLKeyframeMotionParam() -{ - for (motion_map_t::value_type& motion_pair : mParameterizedMotions) - { - motion_list_t& motionList = motion_pair.second; - for (const ParameterizedMotion& paramMotion : motionList) - { - delete paramMotion.mMotion; - } - motionList.clear(); - } - mParameterizedMotions.clear(); -} - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::onInitialize(LLCharacter *character) -//----------------------------------------------------------------------------- -LLMotion::LLMotionInitStatus LLKeyframeMotionParam::onInitialize(LLCharacter *character) -{ - mCharacter = character; - - if (!loadMotions()) - { - return STATUS_FAILURE; - } - - for (motion_map_t::value_type& motion_pair : mParameterizedMotions) - { - motion_list_t& motionList = motion_pair.second; - for (const ParameterizedMotion& paramMotion : motionList) - { - LLMotion* motion = paramMotion.mMotion; - motion->onInitialize(character); - - if (motion->getDuration() > mEaseInDuration) - { - mEaseInDuration = motion->getEaseInDuration(); - } - - if (motion->getEaseOutDuration() > mEaseOutDuration) - { - mEaseOutDuration = motion->getEaseOutDuration(); - } - - if (motion->getDuration() > mDuration) - { - mDuration = motion->getDuration(); - } - - if (motion->getPriority() > mPriority) - { - mPriority = motion->getPriority(); - } - - LLPose *pose = motion->getPose(); - - mPoseBlender.addMotion(motion); - for (LLJointState *jsp = pose->getFirstJointState(); jsp; jsp = pose->getNextJointState()) - { - LLPose *blendedPose = mPoseBlender.getBlendedPose(); - blendedPose->addJointState(jsp); - } - } - } - - return STATUS_SUCCESS; -} - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::onActivate() -//----------------------------------------------------------------------------- -bool LLKeyframeMotionParam::onActivate() -{ - for (motion_map_t::value_type& motion_pair : mParameterizedMotions) - { - motion_list_t& motionList = motion_pair.second; - for (const ParameterizedMotion& paramMotion : motionList) - { - paramMotion.mMotion->activate(mActivationTimestamp); - } - } - return true; -} - - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::onUpdate() -//----------------------------------------------------------------------------- -bool LLKeyframeMotionParam::onUpdate(F32 time, U8* joint_mask) -{ - LL_PROFILE_ZONE_SCOPED; - F32 weightFactor = 1.f / (F32)mParameterizedMotions.size(); - - // zero out all pose weights - for (motion_map_t::value_type& motion_pair : mParameterizedMotions) - { - motion_list_t& motionList = motion_pair.second; - for (const ParameterizedMotion& paramMotion : motionList) - { -// LL_INFOS() << "Weight for pose " << paramMotion.mMotion->getName() << " is " << paramMotion.mMotion->getPose()->getWeight() << LL_ENDL; - paramMotion.mMotion->getPose()->setWeight(0.f); - } - } - - - for (motion_map_t::value_type& motion_pair : mParameterizedMotions) - { - const std::string& paramName = motion_pair.first; - F32* paramValue = (F32 *)mCharacter->getAnimationData(paramName); - if (NULL == paramValue) // unexpected, but... - { - LL_WARNS() << "paramValue == NULL" << LL_ENDL; - continue; - } - - // DANGER! Do not modify mParameterizedMotions while using these pointers! - const ParameterizedMotion* firstMotion = NULL; - const ParameterizedMotion* secondMotion = NULL; - - motion_list_t& motionList = motion_pair.second; - for (const ParameterizedMotion& paramMotion : motionList) - { - paramMotion.mMotion->onUpdate(time, joint_mask); - - F32 distToParam = paramMotion.mParam - *paramValue; - - if ( distToParam <= 0.f) - { - // keep track of the motion closest to the parameter value - firstMotion = ¶mMotion; - } - else - { - // we've passed the parameter value - // so store the first motion we find as the second one we want to blend... - if (firstMotion && !secondMotion ) - { - secondMotion = ¶mMotion; - } - //...or, if we've seen no other motion so far, make sure we blend to this only - else if (!firstMotion) - { - firstMotion = ¶mMotion; - secondMotion = ¶mMotion; - } - } - } - - LLPose *firstPose; - LLPose *secondPose; - - if (firstMotion) - firstPose = firstMotion->mMotion->getPose(); - else - firstPose = NULL; - - if (secondMotion) - secondPose = secondMotion->mMotion->getPose(); - else - secondPose = NULL; - - // now modify weight of the subanim (only if we are blending between two motions) - if (firstMotion && secondMotion) - { - if (firstMotion == secondMotion) - { - firstPose->setWeight(weightFactor); - } - else if (firstMotion->mParam == secondMotion->mParam) - { - firstPose->setWeight(0.5f * weightFactor); - secondPose->setWeight(0.5f * weightFactor); - } - else - { - F32 first_weight = 1.f - - ((llclamp(*paramValue - firstMotion->mParam, 0.f, (secondMotion->mParam - firstMotion->mParam))) / - (secondMotion->mParam - firstMotion->mParam)); - first_weight = llclamp(first_weight, 0.f, 1.f); - - F32 second_weight = 1.f - first_weight; - - firstPose->setWeight(first_weight * weightFactor); - secondPose->setWeight(second_weight * weightFactor); - -// LL_INFOS() << "Parameter " << *paramName << ": " << *paramValue << LL_ENDL; -// LL_INFOS() << "Weights " << firstPose->getWeight() << " " << secondPose->getWeight() << LL_ENDL; - } - } - else if (firstMotion && !secondMotion) - { - firstPose->setWeight(weightFactor); - } - } - - // blend poses - mPoseBlender.blendAndApply(); - - LL_INFOS() << "Param Motion weight " << mPoseBlender.getBlendedPose()->getWeight() << LL_ENDL; - - return true; -} - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::onDeactivate() -//----------------------------------------------------------------------------- -void LLKeyframeMotionParam::onDeactivate() -{ - for (motion_map_t::value_type& motion_pair : mParameterizedMotions) - { - motion_list_t& motionList = motion_pair.second; - for (const ParameterizedMotion& paramMotion : motionList) - { - paramMotion.mMotion->onDeactivate(); - } - } -} - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::addKeyframeMotion() -//----------------------------------------------------------------------------- -bool LLKeyframeMotionParam::addKeyframeMotion(char *name, const LLUUID &id, char *param, F32 value) -{ - LLMotion *newMotion = mCharacter->createMotion( id ); - - if (!newMotion) - { - return false; - } - - newMotion->setName(name); - - // now add motion to this list - mParameterizedMotions[param].insert(ParameterizedMotion(newMotion, value)); - - return true; -} - - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::setDefaultKeyframeMotion() -//----------------------------------------------------------------------------- -void LLKeyframeMotionParam::setDefaultKeyframeMotion(char *name) -{ - for (motion_map_t::value_type& motion_pair : mParameterizedMotions) - { - motion_list_t& motionList = motion_pair.second; - for (const ParameterizedMotion& paramMotion : motionList) - { - if (paramMotion.mMotion->getName() == name) - { - mDefaultKeyframeMotion = paramMotion.mMotion; - } - } - } -} - -//----------------------------------------------------------------------------- -// loadMotions() -//----------------------------------------------------------------------------- -bool LLKeyframeMotionParam::loadMotions() -{ - //------------------------------------------------------------------------- - // Load named file by concatenating the character prefix with the motion name. - // Load data into a buffer to be parsed. - //------------------------------------------------------------------------- - //std::string path = gDirUtilp->getExpandedFilename(LL_PATH_MOTIONS,mCharacter->getAnimationPrefix()) - // + "_" + getName() + ".llp"; - //RN: deprecated unused reference to "motion" directory - std::string path; - - - //------------------------------------------------------------------------- - // open the file - //------------------------------------------------------------------------- - S32 fileSize = 0; - LLAPRFile infile ; - infile.open(path, LL_APR_R, NULL, &fileSize); - apr_file_t* fp = infile.getFileHandle() ; - if (!fp || fileSize == 0) - { - LL_INFOS() << "ERROR: can't open: " << path << LL_ENDL; - return false; - } - - // allocate a text buffer - std::vector text(fileSize+1); - - //------------------------------------------------------------------------- - // load data from file into buffer - //------------------------------------------------------------------------- - bool error = false; - char *p = &text[0]; - while ( 1 ) - { - if (apr_file_eof(fp) == APR_EOF) - { - break; - } - if (apr_file_gets(p, 1024, fp) != APR_SUCCESS) - { - error = true; - break; - } - while ( *(++p) ) - ; - } - - //------------------------------------------------------------------------- - // close the file - //------------------------------------------------------------------------- - infile.close(); - - //------------------------------------------------------------------------- - // check for error - //------------------------------------------------------------------------- - llassert( p <= (&text[0] + fileSize) ); - - if ( error ) - { - LL_INFOS() << "ERROR: error while reading from " << path << LL_ENDL; - return false; - } - - LL_INFOS() << "Loading parametric keyframe data for: " << getName() << LL_ENDL; - - //------------------------------------------------------------------------- - // parse the text and build keyframe data structures - //------------------------------------------------------------------------- - p = &text[0]; - S32 num; - char strA[80]; /* Flawfinder: ignore */ - char strB[80]; /* Flawfinder: ignore */ - F32 floatA = 0.0f; - - - //------------------------------------------------------------------------- - // get priority - //------------------------------------------------------------------------- - bool isFirstMotion = true; - num = sscanf(p, "%79s %79s %f", strA, strB, &floatA); /* Flawfinder: ignore */ - - while(1) - { - if (num == 0 || num == EOF) break; - if ((num != 3)) - { - LL_INFOS() << "WARNING: can't read parametric motion" << LL_ENDL; - return false; - } - - addKeyframeMotion(strA, gAnimLibrary.stringToAnimState(std::string(strA)), strB, floatA); - if (isFirstMotion) - { - isFirstMotion = false; - setDefaultKeyframeMotion(strA); - } - - p = strstr(p, "\n"); - if (!p) - { - break; - } - - p++; - num = sscanf(p, "%79s %79s %f", strA, strB, &floatA); /* Flawfinder: ignore */ - } - - return true; -} - -// End diff --git a/indra/llcharacter/llkeyframemotionparam.h b/indra/llcharacter/llkeyframemotionparam.h deleted file mode 100644 index 8c57766e9b..0000000000 --- a/indra/llcharacter/llkeyframemotionparam.h +++ /dev/null @@ -1,169 +0,0 @@ -/** - * @file llkeyframemotionparam.h - * @brief Implementation of LLKeframeMotionParam class. - * - * $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_LLKEYFRAMEMOTIONPARAM_H -#define LL_LLKEYFRAMEMOTIONPARAM_H - -//----------------------------------------------------------------------------- -// Header files -//----------------------------------------------------------------------------- - -#include - -#include "llmotion.h" -#include "lljointstate.h" -#include "v3math.h" -#include "llquaternion.h" -#include "llkeyframemotion.h" - -//----------------------------------------------------------------------------- -// class LLKeyframeMotionParam -//----------------------------------------------------------------------------- -class LLKeyframeMotionParam : - public LLMotion -{ -public: - // Constructor - LLKeyframeMotionParam(const LLUUID &id); - - // Destructor - virtual ~LLKeyframeMotionParam(); - -public: - //------------------------------------------------------------------------- - // functions to support MotionController and MotionRegistry - //------------------------------------------------------------------------- - - // static constructor - // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLKeyframeMotionParam(id); } - -public: - //------------------------------------------------------------------------- - // animation callbacks to be implemented by subclasses - //------------------------------------------------------------------------- - - // motions must specify whether or not they loop - virtual bool getLoop() { - return true; - } - - // motions must report their total duration - virtual F32 getDuration() { - return mDuration; - } - - // motions must report their "ease in" duration - virtual F32 getEaseInDuration() { - return mEaseInDuration; - } - - // motions must report their "ease out" duration. - virtual F32 getEaseOutDuration() { - return mEaseOutDuration; - } - - // motions must report their priority - virtual LLJoint::JointPriority getPriority() { - return mPriority; - } - - virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; } - - // called to determine when a motion should be activated/deactivated based on avatar pixel coverage - virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_KEYFRAME; } - - // run-time (post constructor) initialization, - // called after parameters have been set - // must return true to indicate success and be available for activation - virtual LLMotionInitStatus onInitialize(LLCharacter *character); - - // called when a motion is activated - // must return true to indicate success, or else - // it will be deactivated - virtual bool onActivate(); - - // called per time step - // must return true while it is active, and - // must return false when the motion is completed. - virtual bool onUpdate(F32 time, U8* joint_mask); - - // called when a motion is deactivated - virtual void onDeactivate(); - - virtual LLPose* getPose() { return mPoseBlender.getBlendedPose();} - -protected: - //------------------------------------------------------------------------- - // new functions defined by this subclass - //------------------------------------------------------------------------- - struct ParameterizedMotion - { - ParameterizedMotion(LLMotion* motion, F32 param) : mMotion(motion), mParam(param) {} - LLMotion* mMotion; - F32 mParam; - }; - - // add a motion and associated parameter triplet - bool addKeyframeMotion(char *name, const LLUUID &id, char *param, F32 value); - - // set default motion for LOD and retrieving blend constants - void setDefaultKeyframeMotion(char *); - - bool loadMotions(); - -protected: - //------------------------------------------------------------------------- - // Member Data - //------------------------------------------------------------------------- - - struct compare_motions - { - bool operator() (const ParameterizedMotion& a, const ParameterizedMotion& b) const - { - if (a.mParam != b.mParam) - return (a.mParam < b.mParam); - else - return a.mMotion < b.mMotion; - } - }; - - typedef std::set < ParameterizedMotion, compare_motions > motion_list_t; - typedef std::map motion_map_t; - motion_map_t mParameterizedMotions; - LLMotion* mDefaultKeyframeMotion; - LLCharacter* mCharacter; - LLPoseBlender mPoseBlender; - - F32 mEaseInDuration; - F32 mEaseOutDuration; - F32 mDuration; - LLJoint::JointPriority mPriority; - - LLUUID mTransactionID; -}; - -#endif // LL_LLKEYFRAMEMOTIONPARAM_H diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index 0c262bf24a..c2cb174821 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -37,7 +37,6 @@ #include "llmotion.h" #include "llpose.h" #include "llframetimer.h" -#include "llstatemachine.h" #include "llstring.h" //----------------------------------------------------------------------------- diff --git a/indra/llcharacter/llstatemachine.cpp b/indra/llcharacter/llstatemachine.cpp deleted file mode 100644 index 38e9ef444c..0000000000 --- a/indra/llcharacter/llstatemachine.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/** - * @file llstatemachine.cpp - * @brief LLStateMachine implementation file. - * - * $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 "llstatemachine.h" -#include "llapr.h" - -#define FSM_PRINT_STATE_TRANSITIONS (0) - -U32 LLUniqueID::sNextID = 0; - -bool operator==(const LLUniqueID &a, const LLUniqueID &b) -{ - return (a.mId == b.mId); -} - -bool operator!=(const LLUniqueID &a, const LLUniqueID &b) -{ - return (a.mId != b.mId); -} - -//----------------------------------------------------------------------------- -// LLStateDiagram -//----------------------------------------------------------------------------- -LLStateDiagram::LLStateDiagram() -{ - mDefaultState = NULL; - mUseDefaultState = false; -} - -LLStateDiagram::~LLStateDiagram() -{ - -} - -// add a state to the state graph -bool LLStateDiagram::addState(LLFSMState *state) -{ - mStates[state] = Transitions(); - return true; -} - -// add a directed transition between 2 states -bool LLStateDiagram::addTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition) -{ - StateMap::iterator state_it; - state_it = mStates.find(&start_state); - Transitions* state_transitions = NULL; - if (state_it == mStates.end() ) - { - addState(&start_state); - state_transitions = &mStates[&start_state]; - } - else - { - state_transitions = &state_it->second; - } - state_it = mStates.find(&end_state); - if (state_it == mStates.end() ) - { - addState(&end_state); - } - - Transitions::iterator transition_it = state_transitions->find(&transition); - if (transition_it != state_transitions->end()) - { - LL_ERRS() << "LLStateTable::addDirectedTransition() : transition already exists" << LL_ENDL; - return false; // transition already exists - } - - (*state_transitions)[&transition] = &end_state; - return true; -} - -// add an undirected transition between 2 states -bool LLStateDiagram::addUndirectedTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition) -{ - bool result; - result = addTransition(start_state, end_state, transition); - if (result) - { - result = addTransition(end_state, start_state, transition); - } - return result; -} - -// add a transition that exists for every state -void LLStateDiagram::addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition) -{ - mDefaultTransitions[&transition] = &end_state; -} - -// process a possible transition, and get the resulting state -LLFSMState* LLStateDiagram::processTransition(LLFSMState& start_state, LLFSMTransition& transition) -{ - // look up transition - //LLFSMState** dest_state = (mStates.getValue(&start_state))->getValue(&transition); - LLFSMState* dest_state = NULL; - StateMap::iterator state_it = mStates.find(&start_state); - if (state_it == mStates.end()) - { - return NULL; - } - Transitions::iterator transition_it = state_it->second.find(&transition); - - // try default transitions if state-specific transition not found - if (transition_it == state_it->second.end()) - { - dest_state = mDefaultTransitions[&transition]; - } - else - { - dest_state = transition_it->second; - } - - // if we have a destination state... - if (NULL != dest_state) - { - // ...return it... - return dest_state; - } - // ... otherwise ... - else - { - // ...look for default state... - if (mUseDefaultState) - { - // ...return it if we have it... - return mDefaultState; - } - else - { - // ...or else we're still in the same state. - return &start_state; - } - } -} - -void LLStateDiagram::setDefaultState(LLFSMState& default_state) -{ - mUseDefaultState = true; - mDefaultState = &default_state; -} - -S32 LLStateDiagram::numDeadendStates() -{ - S32 numDeadends = 0; - for (StateMap::value_type& state_pair : mStates) - { - if (state_pair.second.size() == 0) - { - numDeadends++; - } - } - return numDeadends; -} - -bool LLStateDiagram::stateIsValid(LLFSMState& state) -{ - if (mStates.find(&state) != mStates.end()) - { - return true; - } - return false; -} - -LLFSMState* LLStateDiagram::getState(U32 state_id) -{ - for (StateMap::value_type& state_pair : mStates) - { - if (state_pair.first->getID() == state_id) - { - return state_pair.first; - } - } - return NULL; -} - -bool LLStateDiagram::saveDotFile(const std::string& filename) -{ - LLAPRFile outfile ; - outfile.open(filename, LL_APR_W); - apr_file_t* dot_file = outfile.getFileHandle() ; - - if (!dot_file) - { - LL_WARNS() << "LLStateDiagram::saveDotFile() : Couldn't open " << filename << " to save state diagram." << LL_ENDL; - return false; - } - apr_file_printf(dot_file, "digraph StateMachine {\n\tsize=\"100,100\";\n\tfontsize=40;\n\tlabel=\"Finite State Machine\";\n\torientation=landscape\n\tratio=.77\n"); - - for (StateMap::value_type& state_pair : mStates) - { - apr_file_printf(dot_file, "\t\"%s\" [fontsize=28,shape=box]\n", state_pair.first->getName().c_str()); - } - apr_file_printf(dot_file, "\t\"All States\" [fontsize=30,style=bold,shape=box]\n"); - - for (Transitions::value_type& transition_pair : mDefaultTransitions) - { - apr_file_printf(dot_file, "\t\"All States\" -> \"%s\" [label = \"%s\",fontsize=24];\n", transition_pair.second->getName().c_str(), - transition_pair.second->getName().c_str()); - } - - if (mDefaultState) - { - apr_file_printf(dot_file, "\t\"All States\" -> \"%s\";\n", mDefaultState->getName().c_str()); - } - - - for (StateMap::value_type& state_pair : mStates) - { - LLFSMState *state = state_pair.first; - - for (Transitions::value_type& transition_pair : state_pair.second) - { - std::string state_name = state->getName(); - std::string target_name = transition_pair.second->getName(); - std::string transition_name = transition_pair.first->getName(); - apr_file_printf(dot_file, "\t\"%s\" -> \"%s\" [label = \"%s\",fontsize=24];\n", state->getName().c_str(), - target_name.c_str(), - transition_name.c_str()); - } - } - - apr_file_printf(dot_file, "}\n"); - - return true; -} - -std::ostream& operator<<(std::ostream &s, LLStateDiagram &FSM) -{ - if (FSM.mDefaultState) - { - s << "Default State: " << FSM.mDefaultState->getName() << "\n"; - } - - for (LLStateDiagram::Transitions::value_type& transition_pair : FSM.mDefaultTransitions) - { - s << "Any State -- " << transition_pair.first->getName() - << " --> " << transition_pair.second->getName() << "\n"; - } - - for (LLStateDiagram::StateMap::value_type& state_pair : FSM.mStates) - { - for (LLStateDiagram::Transitions::value_type& transition_pair : state_pair.second) - { - s << state_pair.first->getName() << " -- " << transition_pair.first->getName() - << " --> " << transition_pair.second->getName() << "\n"; - } - s << "\n"; - } - - return s; -} - -//----------------------------------------------------------------------------- -// LLStateMachine -//----------------------------------------------------------------------------- - -LLStateMachine::LLStateMachine() -{ - // we haven't received a starting state yet - mCurrentState = NULL; - mLastState = NULL; - mLastTransition = NULL; - mStateDiagram = NULL; -} - -LLStateMachine::~LLStateMachine() -{ - -} - -// returns current state -LLFSMState* LLStateMachine::getCurrentState() const -{ - return mCurrentState; -} - -// executes current state -void LLStateMachine::runCurrentState(void *data) -{ - mCurrentState->execute(data); -} - -// set current state -bool LLStateMachine::setCurrentState(LLFSMState *initial_state, void* user_data, bool skip_entry) -{ - llassert(mStateDiagram); - - if (mStateDiagram->stateIsValid(*initial_state)) - { - mLastState = mCurrentState = initial_state; - if (!skip_entry) - { - initial_state->onEntry(user_data); - } - return true; - } - - return false; -} - -bool LLStateMachine::setCurrentState(U32 state_id, void* user_data, bool skip_entry) -{ - llassert(mStateDiagram); - - LLFSMState* state = mStateDiagram->getState(state_id); - - if (state) - { - mLastState = mCurrentState = state; - if (!skip_entry) - { - state->onEntry(user_data); - } - return true; - } - - return false; -} - -void LLStateMachine::processTransition(LLFSMTransition& transition, void* user_data) -{ - llassert(mStateDiagram); - - if (NULL == mCurrentState) - { - LL_WARNS() << "mCurrentState == NULL; aborting processTransition()" << LL_ENDL; - return; - } - - LLFSMState* new_state = mStateDiagram->processTransition(*mCurrentState, transition); - - if (NULL == new_state) - { - LL_WARNS() << "new_state == NULL; aborting processTransition()" << LL_ENDL; - return; - } - - mLastTransition = &transition; - mLastState = mCurrentState; - - if (*mCurrentState != *new_state) - { - mCurrentState->onExit(user_data); - mCurrentState = new_state; - mCurrentState->onEntry(user_data); -#if FSM_PRINT_STATE_TRANSITIONS - LL_INFOS() << "Entering state " << mCurrentState->getName() << - " on transition " << transition.getName() << " from state " << - mLastState->getName() << LL_ENDL; -#endif - } -} - -void LLStateMachine::setStateDiagram(LLStateDiagram* diagram) -{ - mStateDiagram = diagram; -} diff --git a/indra/llcharacter/llstatemachine.h b/indra/llcharacter/llstatemachine.h deleted file mode 100644 index 9a33798d96..0000000000 --- a/indra/llcharacter/llstatemachine.h +++ /dev/null @@ -1,147 +0,0 @@ -/** - * @file llstatemachine.h - * @brief LLStateMachine class header file. - * - * $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_LLSTATEMACHINE_H -#define LL_LLSTATEMACHINE_H - -#include - -#include "llerror.h" -#include - -class LLUniqueID -{ - friend bool operator==(const LLUniqueID &a, const LLUniqueID &b); - friend bool operator!=(const LLUniqueID &a, const LLUniqueID &b); -protected: - static U32 sNextID; - U32 mId; -public: - LLUniqueID(){mId = sNextID++;} - virtual ~LLUniqueID(){} - U32 getID() {return mId;} -}; - -class LLFSMTransition : public LLUniqueID -{ -public: - LLFSMTransition() : LLUniqueID(){}; - virtual std::string getName()const { return "unnamed"; } -}; - -class LLFSMState : public LLUniqueID -{ -public: - LLFSMState() : LLUniqueID(){}; - virtual void onEntry(void *){}; - virtual void onExit(void *){}; - virtual void execute(void *){}; - virtual std::string getName() const { return "unnamed"; } -}; - -class LLStateDiagram -{ -typedef std::map Transitions; - -friend std::ostream& operator<<(std::ostream &s, LLStateDiagram &FSM); -friend class LLStateMachine; - -protected: - typedef std::map StateMap; - StateMap mStates; - Transitions mDefaultTransitions; - LLFSMState* mDefaultState; - bool mUseDefaultState; - -public: - LLStateDiagram(); - virtual ~LLStateDiagram(); - -protected: - // add a state to the state graph, executed implicitly when adding transitions - bool addState(LLFSMState *state); - - // add a directed transition between 2 states - bool addTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition); - - // add an undirected transition between 2 states - bool addUndirectedTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition); - - // add a transition that is taken if none other exist - void addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition); - - // process a possible transition, and get the resulting state - LLFSMState* processTransition(LLFSMState& start_state, LLFSMTransition& transition); - - // add a transition that exists for every state - void setDefaultState(LLFSMState& default_state); - - // return total number of states with no outgoing transitions - S32 numDeadendStates(); - - // does this state exist in the state diagram? - bool stateIsValid(LLFSMState& state); - - // get a state pointer by ID - LLFSMState* getState(U32 state_id); - -public: - // save the graph in a DOT file for rendering and visualization - bool saveDotFile(const std::string& filename); -}; - -class LLStateMachine -{ -protected: - LLFSMState* mCurrentState; - LLFSMState* mLastState; - LLFSMTransition* mLastTransition; - LLStateDiagram* mStateDiagram; - -public: - LLStateMachine(); - virtual ~LLStateMachine(); - - // set state diagram - void setStateDiagram(LLStateDiagram* diagram); - - // process this transition - void processTransition(LLFSMTransition &transition, void* user_data); - - // returns current state - LLFSMState* getCurrentState() const; - - // execute current state - void runCurrentState(void *data); - - // set state by state pointer - bool setCurrentState(LLFSMState *initial_state, void* user_data, bool skip_entry = true); - - // set state by unique ID - bool setCurrentState(U32 state_id, void* user_data, bool skip_entry = true); -}; - -#endif //_LL_LLSTATEMACHINE_H -- cgit v1.3 From 44e41779028e0a5574c5382b1b56455f8fb612f3 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:04:23 +0200 Subject: #2069 Activate group option in Group --- indra/llcommon/workqueue.cpp | 22 ++++++++++++-- indra/newview/llgrouplist.cpp | 2 +- indra/newview/llpanelgroup.cpp | 34 ++++++++++++++++++++++ indra/newview/llpanelgroup.h | 9 +++++- .../skins/default/xui/en/panel_group_general.xml | 9 ++++++ 5 files changed, 72 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index 7efaebd569..4db44ff2a6 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -276,12 +276,30 @@ bool LL::WorkQueue::done() bool LL::WorkQueue::post(const Work& callable) { - return mQueue.pushIfOpen(callable); + try + { + return mQueue.pushIfOpen(callable); + } + catch (std::bad_alloc&) + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS("LLCoros") << "Bad memory allocation in WorkQueue::post" << LL_ENDL; + return false; + } } bool LL::WorkQueue::tryPost(const Work& callable) { - return mQueue.tryPush(callable); + try + { + return mQueue.tryPush(callable); + } + catch (std::bad_alloc&) + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS("LLCoros") << "Bad memory allocation in WorkQueue::tryPost" << LL_ENDL; + return false; + } } LL::WorkQueue::Work LL::WorkQueue::pop_() diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 7659e5f082..074cda1683 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -317,7 +317,7 @@ void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LL // virtual bool LLGroupList::handleEvent(LLPointer event, const LLSD& userdata) { - // Why is "new group" sufficient? + // "new group" is sufficient because update_group_floaters always calls that on group changes if (event->desc() == "new group") { setDirty(); diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index ecb66f9cea..ad65293b98 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -191,6 +191,13 @@ bool LLPanelGroup::postBuild() mButtonJoin->setCommitCallback(boost::bind(&LLPanelGroup::onBtnJoin,this)); mJoinText = panel_general->getChild("join_cost_text"); + + mButtonActivate = panel_general->getChild("btn_activate"); + mButtonActivate->setVisible(false); + mButtonActivate->setEnabled(gAgent.getGroupID() != mID); + mButtonActivate->setCommitCallback(boost::bind(&LLPanelGroup::onBtnActivate, this)); + + gAgent.addListener(this, "new group"); } LLVoiceClient::addObserver(this); @@ -267,6 +274,7 @@ void LLPanelGroup::onBtnJoin() if (LLGroupActions::isInGroup(mID)) { LLGroupActions::leave(mID); + mButtonActivate->setVisible(false); } else { @@ -275,6 +283,12 @@ void LLPanelGroup::onBtnJoin() } } +void LLPanelGroup::onBtnActivate() +{ + LLGroupActions::activate(mID); + mButtonActivate->setEnabled(false); +} + void LLPanelGroup::changed(LLGroupChange gc) { for(std::vector::iterator it = mTabs.begin();it!=mTabs.end();++it) @@ -312,6 +326,8 @@ void LLPanelGroup::update(LLGroupChange gc) bool join_btn_visible = is_member || gdatap->mOpenEnrollment; mButtonJoin->setVisible(join_btn_visible); + mButtonActivate->setEnabled(gAgent.getGroupID() != mID); + mButtonActivate->setVisible(is_member); mJoinText->setVisible(join_btn_visible); if (is_member) @@ -384,6 +400,8 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) if(mButtonJoin) mButtonJoin->setVisible(false); + if (mButtonActivate) + mButtonActivate->setVisible(false); if(is_null_group_id)//creating new group @@ -598,4 +616,20 @@ void LLPanelGroup::showNotice(const std::string& subject, } +bool LLPanelGroup::handleEvent(LLPointer event, const LLSD& userdata) +{ + if (event->desc() == "new group") + { + mButtonActivate->setEnabled(gAgent.getGroupID() != mID); + return true; + } + + if (event->desc() == "value_changed") + { + mButtonActivate->setEnabled(gAgent.getGroupID() != mID); + return true; + } + + return false; +} diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h index b43a93bc40..d3550264a0 100644 --- a/indra/newview/llpanelgroup.h +++ b/indra/newview/llpanelgroup.h @@ -26,6 +26,7 @@ #ifndef LL_LLPANELGROUP_H #define LL_LLPANELGROUP_H +#include "llevent.h" #include "llgroupmgr.h" #include "llpanel.h" #include "lltimer.h" @@ -44,7 +45,8 @@ class LLAgent; class LLPanelGroup : public LLPanel, public LLGroupMgrObserver, - public LLVoiceClientStatusObserver + public LLVoiceClientStatusObserver, + public LLOldEvents::LLSimpleListener { public: LLPanelGroup(); @@ -94,6 +96,7 @@ protected: void onBackBtnClick(); void onBtnJoin(); + void onBtnActivate(); static void onBtnApply(void*); static void onBtnRefresh(void*); @@ -120,11 +123,15 @@ protected: LLUICtrl* mGroupNameCtrl = nullptr; LLButton* mButtonJoin = nullptr; + LLButton* mButtonActivate = nullptr; LLButton* mButtonApply = nullptr; LLButton* mButtonCall = nullptr; LLButton* mButtonChat = nullptr; LLButton* mButtonRefresh = nullptr; LLUICtrl* mJoinText; + +private: + bool handleEvent(LLPointer event, const LLSD& userdata); // for agent group list changes }; class LLPanelGroupTab : public LLPanel diff --git a/indra/newview/skins/default/xui/en/panel_group_general.xml b/indra/newview/skins/default/xui/en/panel_group_general.xml index 7290cbb5c6..74bc7736f9 100644 --- a/indra/newview/skins/default/xui/en/panel_group_general.xml +++ b/indra/newview/skins/default/xui/en/panel_group_general.xml @@ -86,6 +86,15 @@ Hover your mouse over the options for more help. name="btn_join" visible="true" width="120" /> +