CNA-BUG-154: A save container titled GamerServices shares its directory with the gamer-services store, so DeleteContainer("GamerServices") deletes local achievements and leaderboards
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
The gamer-services store lives at <root>/GamerServices, where a StorageContainer titled GamerServices is also created, so deleting that title removes every persisted achievement and leaderboard entry.
- Identifier
CNA-BUG-154- Category
- Bug
- Subsystem
- Storage
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- StorageDevice::DeleteContainer(const std::string& titleName) and BeginOpenContainer/EndOpenContainer with the title GamerServices; the gamer-services local store under StorageDevice::GetStorageRootEXT()
Expected behaviour
A game's save containers and CNA's own gamer-services persistence are separate things: deleting a save title removes that title's saves, which is what DeleteContainer documents ("Removes the container directory tree for the given title name entirely").
Actual behaviour at TARGET
StoreRoot() in LocalGamerServicesStore.cpp is PathFromUtf8(StorageDevice::GetStorageRootEXT()) / "GamerServices", with achievements/ and leaderboards/ below it. A container titled GamerServices is created at <root>/GamerServices/AllPlayers (or PlayerN). DeleteContainer("GamerServices") passes the containment check, since the title is a non-root child of the root, and remove_alls <root>/GamerServices, achievements and leaderboards included. Opening such a container also puts the game's saves inside CNA's store directory. Nothing reserves the name.
Source locations
modules/gamer-services/src/Internal/LocalGamerServicesStore.cpp— StoreRoot, AchievementsDir, LeaderboardsDir below GetStorageRootEXT()/GamerServicesmodules/storage/src/StorageDevice.cpp— StorageDevice::DeleteContainer removes the contained title directory recursivelymodules/storage/src/StorageContainer.cpp— the constructor places the title and player folder directly below the root
Evidence
Checked by reading LocalGamerServicesStore.cpp, StorageDevice.cpp and StorageContainer.cpp at 009d40f5; not executed. The storage internals page records the collision.
Focused reproduction
// Illustrative; not compiled or run for this entry.
StorageDevice::SetAppNameEXT("MyGame");
gamer->AwardAchievement("first_boss"); // <root>/GamerServices/achievements/...
device->DeleteContainer("GamerServices"); // removes <root>/GamerServices entirely
auto earned = gamer->GetAchievements(); // empty
Current tests
None: the storage fixture and the gamer-services fixture use different application names (CnaTestsContent002StorageDevice and CnaTestsGamerServices), so they never share a root.
Regression test
Award an achievement, call DeleteContainer("GamerServices"), and expect either a refusal or the achievement to survive. Moving the store below a reserved dot-directory (the isolated-storage coupling already uses .cna_isolated_storage) or rejecting the title would satisfy it.
Blast radius
Games whose save titles can be GamerServices (user-entered titles, or a title chosen by coincidence): one delete wipes local achievements and leaderboards. Every other title is unaffected.
Workaround
Do not use GamerServices as a container title, and filter it out of user-entered titles.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Deep dives
- GamerServices contract: persistence
- Known issues
- Bug index