From 20ddc736730a9f9c62ef71a808e33ed5587ae19e Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Wed, 3 Dec 2025 12:58:43 -0800 Subject: update CLA exceptions allowlist --- .github/workflows/cla.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml index 5b31c584d5..800f3c42d1 100644 --- a/.github/workflows/cla.yaml +++ b/.github/workflows/cla.yaml @@ -23,4 +23,4 @@ jobs: path-to-signatures: signatures.json remote-organization-name: secondlife remote-repository-name: cla-signatures - allowlist: callum@mbp.localdomain,rye@lindenlab.com,rye,bot* + allowlist: callum@mbp.localdomain,rye@lindenlab.com,rye,signal@lindenlab.com,dependabot*,bot* -- cgit v1.3 From 9f82b907f2f7d57580e17cef89bbd08be90f94fb Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:29:04 +0200 Subject: #3612 "Copy SLURL" from Favorites bar not working If region isn't online, can't get region position and form slurl --- indra/newview/llfavoritesbar.cpp | 7 +++++++ indra/newview/skins/default/xui/en/menu_favorites.xml | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 377710c170..acffc856bc 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -1395,6 +1395,13 @@ bool LLFavoritesBarCtrl::enableSelected(const LLSD& userdata) { return !LLAgentPicksInfo::getInstance()->isPickLimitReached(); } + else if (param == "copy_slurl" + || param == "show_on_map") + { + LLVector3d posGlobal; + LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); + return !posGlobal.isExactlyZero(); + } return false; } diff --git a/indra/newview/skins/default/xui/en/menu_favorites.xml b/indra/newview/skins/default/xui/en/menu_favorites.xml index 6345394b46..f82f705fb7 100644 --- a/indra/newview/skins/default/xui/en/menu_favorites.xml +++ b/indra/newview/skins/default/xui/en/menu_favorites.xml @@ -35,6 +35,9 @@ + + Date: Fri, 5 Dec 2025 18:01:28 +0200 Subject: #5109 LLExperienceCache crashes on a coroutine --- indra/llmessage/llexperiencecache.cpp | 33 ++++++++++++++++++++------------- indra/llmessage/llexperiencecache.h | 4 ++-- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 149741b9f9..e4c7deb1c5 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -112,9 +112,7 @@ void LLExperienceCache::initSingleton() constexpr size_t CORO_QUEUE_SIZE = 2048; LLCoprocedureManager::instance().initializePool("ExpCache", CORO_QUEUE_SIZE); - LLCoros::instance().launch("LLExperienceCache::idleCoro", - boost::bind(&LLExperienceCache::idleCoro, this)); - + LLCoros::instance().launch("LLExperienceCache::idleCoro", LLExperienceCache::idleCoro); } void LLExperienceCache::cleanup() @@ -246,6 +244,7 @@ const LLExperienceCache::cache_t& LLExperienceCache::getCached() return mCache; } +// static because used by coroutine and can outlive the instance void LLExperienceCache::requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, std::string url, RequestQueue_t requests) { LLCore::HttpRequest::ptr_t httpRequest = std::make_shared(); @@ -254,6 +253,13 @@ void LLExperienceCache::requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdap LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + if (sShutdown) + { + return; + } + + LLExperienceCache* self = LLExperienceCache::getInstance(); + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); @@ -265,7 +271,7 @@ void LLExperienceCache::requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdap // build dummy entries for the failed requests for (RequestQueue_t::const_iterator it = requests.begin(); it != requests.end(); ++it) { - LLSD exp = get(*it); + LLSD exp = self->get(*it); //leave the properties alone if we already have a cache entry for this xp if (exp.isUndefined()) { @@ -278,7 +284,7 @@ void LLExperienceCache::requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdap exp["error"] = (LLSD::Integer)status.getType(); exp[QUOTA] = DEFAULT_QUOTA; - processExperience(*it, exp); + self->processExperience(*it, exp); } return; } @@ -294,7 +300,7 @@ void LLExperienceCache::requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdap LL_DEBUGS("ExperienceCache") << "Received result for " << public_key << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL; - processExperience(public_key, row); + self->processExperience(public_key, row); } LLSD error_ids = result["error_ids"]; @@ -310,7 +316,7 @@ void LLExperienceCache::requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdap exp[MISSING] = true; exp[QUOTA] = DEFAULT_QUOTA; - processExperience(id, exp); + self->processExperience(id, exp); LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL; } @@ -361,7 +367,7 @@ void LLExperienceCache::requestExperiences() if (mRequestQueue.empty() || (ostr.tellp() > EXP_URL_SEND_THRESHOLD)) { // request is placed in the coprocedure pool for the ExpCache cache. Throttling is done by the pool itself. LLCoprocedureManager::instance().enqueueCoprocedure("ExpCache", "RequestExperiences", - boost::bind(&LLExperienceCache::requestExperiencesCoro, this, _1, ostr.str(), requests) ); + boost::bind(&LLExperienceCache::requestExperiencesCoro, _1, ostr.str(), requests) ); ostr.str(std::string()); ostr << urlBase << "?page_size=" << PAGE_SIZE1; @@ -393,7 +399,7 @@ void LLExperienceCache::setCapabilityQuery(LLExperienceCache::CapabilityQuery_t mCapability = queryfn; } - +// static, because coro can outlive the instance void LLExperienceCache::idleCoro() { const F32 SECS_BETWEEN_REQUESTS = 0.5f; @@ -402,14 +408,15 @@ void LLExperienceCache::idleCoro() LL_INFOS("ExperienceCache") << "Launching Experience cache idle coro." << LL_ENDL; do { - if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) + LLExperienceCache* self = LLExperienceCache::getInstance(); + if (self->mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) { - eraseExpired(); + self->eraseExpired(); } - if (!mRequestQueue.empty()) + if (!self->mRequestQueue.empty()) { - requestExperiences(); + self->requestExperiences(); } llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS); diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index 4b344347d5..9ecdb9efca 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -144,9 +144,9 @@ private: std::string mCacheFileName; static bool sShutdown; // control for coroutines, they exist out of LLExperienceCache's scope, so they need a static control - void idleCoro(); + static void idleCoro(); void eraseExpired(); - void requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, std::string, RequestQueue_t); + static void requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, std::string, RequestQueue_t); void requestExperiences(); void fetchAssociatedExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, LLUUID, LLUUID, std::string, ExperienceGetFn_t); -- cgit v1.3 From 3fd68bcc8290f2ce156ea8414983e51d2ef74e33 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Fri, 5 Dec 2025 18:07:16 +0200 Subject: #4931 Fix missed name cache connection #2 --- indra/newview/llpanelplaceprofile.cpp | 4 ++-- indra/newview/llpanelplaceprofile.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/indra/newview/llpanelplaceprofile.cpp b/indra/newview/llpanelplaceprofile.cpp index 87f05f2028..c380b6860f 100644 --- a/indra/newview/llpanelplaceprofile.cpp +++ b/indra/newview/llpanelplaceprofile.cpp @@ -517,7 +517,7 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel, std::string parcel_owner = LLSLURL("agent", parcel->getOwnerID(), "inspect").getSLURLString(); mParcelOwner->setText(parcel_owner); - LLAvatarNameCache::get(region->getOwner(), boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, _1, _2, mRegionOwnerText)); + mAvatarNameCacheConnection = LLAvatarNameCache::get(region->getOwner(), boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, _1, _2, mRegionOwnerText)); mRegionGroupText->setText( getString("none_text")); } @@ -548,7 +548,7 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel, const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID(); if(auth_buyer_id.notNull()) { - LLAvatarNameCache::get(auth_buyer_id, boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, _1, _2, mSaleToText)); + mAvatarNameCacheConnection = LLAvatarNameCache::get(auth_buyer_id, boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, _1, _2, mSaleToText)); // Show sales info to a specific person or a group he belongs to. if (auth_buyer_id != gAgent.getID() && !gAgent.isInGroup(auth_buyer_id)) diff --git a/indra/newview/llpanelplaceprofile.h b/indra/newview/llpanelplaceprofile.h index f562be0f5d..0c161198f8 100644 --- a/indra/newview/llpanelplaceprofile.h +++ b/indra/newview/llpanelplaceprofile.h @@ -118,6 +118,8 @@ private: LLTextEditor* mResaleText; LLTextBox* mSaleToText; LLAccordionCtrl* mAccordionCtrl; + + boost::signals2::scoped_connection mAvatarNameCacheConnection; }; #endif // LL_LLPANELPLACEPROFILE_H -- cgit v1.3 From 49c73ac7297ec7760a00bf36fa6339f2d0be1f95 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Mon, 8 Dec 2025 12:21:25 +0200 Subject: #3612 "Copy SLURL" from Favorites bar not working #2 --- indra/newview/llfavoritesbar.cpp | 35 ++++++++++++++++++---- .../newview/skins/default/xui/en/notifications.xml | 11 +++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index acffc856bc..98b3ca820b 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -1398,9 +1398,15 @@ bool LLFavoritesBarCtrl::enableSelected(const LLSD& userdata) else if (param == "copy_slurl" || param == "show_on_map") { - LLVector3d posGlobal; - LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); - return !posGlobal.isExactlyZero(); + LLViewerInventoryItem* item = gInventory.getItem(mSelectedItemID); + if (nullptr == item) + return false; // shouldn't happen as it is selected from existing items + + const LLUUID& asset_id = item->getAssetUUID(); + + // Favorites are supposed to be loaded first, it should be here already + LLLandmark* landmark = gLandmarkList.getAsset(asset_id, NULL /*callback*/); + return nullptr != landmark; } return false; @@ -1432,10 +1438,17 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) LLVector3d posGlobal; LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); + // inventory item and asset exist, otherwise + // enableSelected wouldn't have let it get here, + // only need to check location validity if (!posGlobal.isExactlyZero()) { LLLandmarkActions::getSLURLfromPosGlobal(posGlobal, copy_slurl_to_clipboard_cb); } + else + { + LLNotificationsUtil::add("LandmarkLocationUnknown"); + } } else if (action == "show_on_map") { @@ -1444,10 +1457,20 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) LLVector3d posGlobal; LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); - if (!posGlobal.isExactlyZero() && worldmap_instance) + if (worldmap_instance) { - worldmap_instance->trackLocation(posGlobal); - LLFloaterReg::showInstance("world_map", "center"); + // inventory item and asset exist, otherwise + // enableSelected wouldn't have let it get here, + // only need to check location validity + if (!posGlobal.isExactlyZero()) + { + worldmap_instance->trackLocation(posGlobal); + LLFloaterReg::showInstance("world_map", "center"); + } + else + { + LLNotificationsUtil::add("LandmarkLocationUnknown"); + } } } else if (action == "create_pick") diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index dbd513afe8..d0261a930c 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2465,6 +2465,17 @@ You already have a landmark for this location. fail + +Viewer wasn't able to get region's location. Region might be temporarily unavailable or was removed. + + fail + + Date: Mon, 8 Dec 2025 21:15:04 +0200 Subject: #4604 Tweak LLImageBase's 'out of memory' handling --- indra/llimage/llimage.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index ca8a4199e8..35bc7065b1 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -709,8 +709,20 @@ U8* LLImageBase::allocateData(S32 size) mData = (U8*)ll_aligned_malloc_16(size); if (!mData) { - LL_WARNS() << "Failed to allocate image data size [" << size << "]" << LL_ENDL; - mBadBufferAllocation = true; + constexpr S32 MAX_TOLERANCE = 1024 * 1024 * 4; // 4 MB + if (size > MAX_TOLERANCE) + { + // If a big image failed to allocate, tollerate it for now. + // It's insightfull when crash logs without obvious cause are being analyzed, + // so a crash in a random location that normally is a mystery can get proper handling. + LL_WARNS() << "Failed to allocate image data size [" << size << "]" << LL_ENDL; + } + else + { + // We are too far gone if we can't allocate a small buffer. + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS() << "Failed to allocate image data size [" << size << "]" << LL_ENDL; + } } } -- 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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 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 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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 058ee5c66f850b01c255aa7b1de6342a6bf00c47 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Wed, 10 Dec 2025 20:50:58 +0200 Subject: #5122 update version format --- indra/newview/VIEWER_VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 4703009f54..4df38dcc24 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -2026.01.0 +2026.1.0 -- 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(-) 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(+) 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(+) 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 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(-) 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 2c69552453723dac41f3f644bd46f02f0406497c Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Thu, 11 Dec 2025 18:01:40 +0200 Subject: #5122 update version format #2 --- indra/newview/VIEWER_VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 4df38dcc24..2aaedf9944 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -2026.1.0 +26.1.0 -- 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 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(-) 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" /> +