/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsVolume.h" #include "base/message_loop.h" #include "base/task.h" #include "nsIPowerManagerService.h" #include "nsISupportsUtils.h" #include "nsIVolume.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "nsVolumeStat.h" #include "nsXULAppAPI.h" #include "Volume.h" #include "AutoMounter.h" #include "VolumeManager.h" #undef VOLUME_MANAGER_LOG_TAG #define VOLUME_MANAGER_LOG_TAG "nsVolume" #include "VolumeManagerLog.h" namespace mozilla { namespace system { const char * NS_VolumeStateStr(int32_t aState) { switch (aState) { case nsIVolume::STATE_INIT: return "Init"; case nsIVolume::STATE_NOMEDIA: return "NoMedia"; case nsIVolume::STATE_IDLE: return "Idle"; case nsIVolume::STATE_PENDING: return "Pending"; case nsIVolume::STATE_CHECKING: return "Checking"; case nsIVolume::STATE_MOUNTED: return "Mounted"; case nsIVolume::STATE_UNMOUNTING: return "Unmounting"; case nsIVolume::STATE_FORMATTING: return "Formatting"; case nsIVolume::STATE_SHARED: return "Shared"; case nsIVolume::STATE_SHAREDMNT: return "Shared-Mounted"; case nsIVolume::STATE_CHECKMNT: return "Check-Mounted"; case nsIVolume::STATE_MOUNT_FAIL: return "Mount-Fail"; } return "???"; } // While nsVolumes can only be used on the main thread, in the // UpdateVolumeRunnable constructor (which is called from IOThread) we // allocate an nsVolume which is then passed to MainThread. Since we // have a situation where we allocate on one thread and free on another // we use a thread safe AddRef implementation. NS_IMPL_ISUPPORTS(nsVolume, nsIVolume) nsVolume::nsVolume(const Volume* aVolume) : mName(NS_ConvertUTF8toUTF16(aVolume->Name())), mMountPoint(NS_ConvertUTF8toUTF16(aVolume->MountPoint())), mState(aVolume->State()), mMountGeneration(aVolume->MountGeneration()), mMountLocked(aVolume->IsMountLocked()), mIsFake(!aVolume->CanBeShared()), mIsMediaPresent(aVolume->MediaPresent()), mIsSharing(aVolume->IsSharing()), mIsFormatting(aVolume->IsFormatting()), mIsUnmounting(aVolume->IsUnmounting()), mIsRemovable(aVolume->IsRemovable()), mIsHotSwappable(aVolume->IsHotSwappable()) { } nsVolume::nsVolume(const nsVolume* aVolume) : mName(aVolume->mName), mMountPoint(aVolume->mMountPoint), mState(aVolume->mState), mMountGeneration(aVolume->mMountGeneration), mMountLocked(aVolume->mMountLocked), mIsFake(aVolume->mIsFake), mIsMediaPresent(aVolume->mIsMediaPresent), mIsSharing(aVolume->mIsSharing), mIsFormatting(aVolume->mIsFormatting), mIsUnmounting(aVolume->mIsUnmounting), mIsRemovable(aVolume->mIsRemovable), mIsHotSwappable(aVolume->mIsHotSwappable) { } void nsVolume::Dump(const char* aLabel) const { LOG("%s: Volume: %s is %s and %s @ %s gen %d locked %d", aLabel, NameStr().get(), StateStr(), IsMediaPresent() ? "inserted" : "missing", MountPointStr().get(), MountGeneration(), (int)IsMountLocked()); LOG("%s: IsSharing %s IsFormating %s IsUnmounting %s", aLabel, (IsSharing() ? "y" : "n"), (IsFormatting() ? "y" : "n"), (IsUnmounting() ? "y" : "n")); } bool nsVolume::Equals(nsIVolume* aVolume) { nsString volName; aVolume->GetName(volName); if (!mName.Equals(volName)) { return false; } nsString volMountPoint; aVolume->GetMountPoint(volMountPoint); if (!mMountPoint.Equals(volMountPoint)) { return false; } int32_t volState; aVolume->GetState(&volState); if (mState != volState){ return false; } int32_t volMountGeneration; aVolume->GetMountGeneration(&volMountGeneration); if (mMountGeneration != volMountGeneration) { return false; } bool volIsMountLocked; aVolume->GetIsMountLocked(&volIsMountLocked); if (mMountLocked != volIsMountLocked) { return false; } bool isFake; aVolume->GetIsFake(&isFake); if (mIsFake != isFake) { return false; } bool isSharing; aVolume->GetIsSharing(&isSharing); if (mIsSharing != isSharing) { return false; } bool isFormatting; aVolume->GetIsFormatting(&isFormatting); if (mIsFormatting != isFormatting) { return false; } bool isUnmounting; aVolume->GetIsUnmounting(&isUnmounting); if (mIsUnmounting != isUnmounting) { return false; } bool isRemovable; aVolume->GetIsRemovable(&isRemovable); if (mIsRemovable != isRemovable) { return false; } bool isHotSwappable; aVolume->GetIsHotSwappable(&isHotSwappable); if (mIsHotSwappable != isHotSwappable) { return false; } return true; } NS_IMETHODIMP nsVolume::GetIsMediaPresent(bool* aIsMediaPresent) { *aIsMediaPresent = mIsMediaPresent; return NS_OK; } NS_IMETHODIMP nsVolume::GetIsMountLocked(bool* aIsMountLocked) { *aIsMountLocked = mMountLocked; return NS_OK; } NS_IMETHODIMP nsVolume::GetIsSharing(bool* aIsSharing) { *aIsSharing = mIsSharing; return NS_OK; } NS_IMETHODIMP nsVolume::GetIsFormatting(bool* aIsFormatting) { *aIsFormatting = mIsFormatting; return NS_OK; } NS_IMETHODIMP nsVolume::GetIsUnmounting(bool* aIsUnmounting) { *aIsUnmounting = mIsUnmounting; return NS_OK; } NS_IMETHODIMP nsVolume::GetName(nsAString& aName) { aName = mName; return NS_OK; } NS_IMETHODIMP nsVolume::GetMountGeneration(int32_t* aMountGeneration) { *aMountGeneration = mMountGeneration; return NS_OK; } NS_IMETHODIMP nsVolume::GetMountLockName(nsAString& aMountLockName) { aMountLockName = NS_LITERAL_STRING("volume-") + Name(); aMountLockName.AppendPrintf("-%d", mMountGeneration); return NS_OK; } NS_IMETHODIMP nsVolume::GetMountPoint(nsAString& aMountPoint) { aMountPoint = mMountPoint; return NS_OK; } NS_IMETHODIMP nsVolume::GetState(int32_t* aState) { *aState = mState; return NS_OK; } NS_IMETHODIMP nsVolume::GetStats(nsIVolumeStat **aResult) { if (mState != STATE_MOUNTED) { return NS_ERROR_NOT_AVAILABLE; } NS_IF_ADDREF(*aResult = new nsVolumeStat(mMountPoint)); return NS_OK; } NS_IMETHODIMP nsVolume::GetIsFake(bool *aIsFake) { *aIsFake = mIsFake; return NS_OK; } NS_IMETHODIMP nsVolume::GetIsRemovable(bool *aIsRemovable) { *aIsRemovable = mIsRemovable; return NS_OK; } NS_IMETHODIMP nsVolume::GetIsHotSwappable(bool *aIsHotSwappable) { *aIsHotSwappable = mIsHotSwappable; return NS_OK; } NS_IMETHODIMP nsVolume::Format() { MOZ_ASSERT(XRE_IsParentProcess()); XRE_GetIOMessageLoop()->PostTask( NewRunnableFunction(FormatVolumeIOThread, NameStr())); return NS_OK; } /* static */ void nsVolume::FormatVolumeIOThread(const nsCString& aVolume) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { return; } AutoMounterFormatVolume(aVolume); } NS_IMETHODIMP nsVolume::Mount() { MOZ_ASSERT(XRE_IsParentProcess()); XRE_GetIOMessageLoop()->PostTask( NewRunnableFunction(MountVolumeIOThread, NameStr())); return NS_OK; } /* static */ void nsVolume::MountVolumeIOThread(const nsCString& aVolume) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { return; } AutoMounterMountVolume(aVolume); } NS_IMETHODIMP nsVolume::Unmount() { MOZ_ASSERT(XRE_IsParentProcess()); XRE_GetIOMessageLoop()->PostTask( NewRunnableFunction(UnmountVolumeIOThread, NameStr())); return NS_OK; } /* static */ void nsVolume::UnmountVolumeIOThread(const nsCString& aVolume) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { return; } AutoMounterUnmountVolume(aVolume); } void nsVolume::LogState() const { if (mState == nsIVolume::STATE_MOUNTED) { LOG("nsVolume: %s state %s @ '%s' gen %d locked %d fake %d " "media %d sharing %d formatting %d unmounting %d removable %d hotswappable %d", NameStr().get(), StateStr(), MountPointStr().get(), MountGeneration(), (int)IsMountLocked(), (int)IsFake(), (int)IsMediaPresent(), (int)IsSharing(), (int)IsFormatting(), (int)IsUnmounting(), (int)IsRemovable(), (int)IsHotSwappable()); return; } LOG("nsVolume: %s state %s", NameStr().get(), StateStr()); } void nsVolume::UpdateMountLock(nsVolume* aOldVolume) { MOZ_ASSERT(NS_IsMainThread()); bool oldMountLocked = aOldVolume ? aOldVolume->mMountLocked : false; if (mState != nsIVolume::STATE_MOUNTED) { // Since we're not in the mounted state, we need to // forgot whatever mount generation we may have had. mMountGeneration = -1; mMountLocked = oldMountLocked; return; } int32_t oldMountGeneration = aOldVolume ? aOldVolume->mMountGeneration : -1; if (mMountGeneration == oldMountGeneration) { // No change in mount generation, nothing else to do mMountLocked = oldMountLocked; return; } if (!XRE_IsParentProcess()) { // Child processes just track the state, not maintain it. return; } // Notify the Volume on IOThread whether the volume is locked or not. nsCOMPtr pmService = do_GetService(POWERMANAGERSERVICE_CONTRACTID); if (!pmService) { return; } nsString mountLockName; GetMountLockName(mountLockName); nsString mountLockState; pmService->GetWakeLockState(mountLockName, mountLockState); UpdateMountLock(mountLockState); } void nsVolume::UpdateMountLock(const nsAString& aMountLockState) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); // There are 3 states, unlocked, locked-background, and locked-foreground // I figured it was easier to use negtive logic and compare for unlocked. UpdateMountLock(!aMountLockState.EqualsLiteral("unlocked")); } void nsVolume::UpdateMountLock(bool aMountLocked) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); if (aMountLocked == mMountLocked) { return; } // The locked/unlocked state changed. Tell IOThread about it. mMountLocked = aMountLocked; LogState(); XRE_GetIOMessageLoop()->PostTask( NewRunnableFunction(Volume::UpdateMountLock, NS_LossyConvertUTF16toASCII(Name()), MountGeneration(), aMountLocked)); } void nsVolume::SetIsFake(bool aIsFake) { mIsFake = aIsFake; if (mIsFake) { // The media is always present for fake volumes. mIsMediaPresent = true; MOZ_ASSERT(!mIsSharing); } } void nsVolume::SetIsRemovable(bool aIsRemovable) { mIsRemovable = aIsRemovable; if (!mIsRemovable) { mIsHotSwappable = false; } } void nsVolume::SetIsHotSwappable(bool aIsHotSwappable) { mIsHotSwappable = aIsHotSwappable; if (mIsHotSwappable) { mIsRemovable = true; } } void nsVolume::SetState(int32_t aState) { static int32_t sMountGeneration = 0; MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(IsFake()); if (aState == mState) { return; } if (aState == nsIVolume::STATE_MOUNTED) { mMountGeneration = ++sMountGeneration; } mState = aState; } } // system } // mozilla