fix(fs): stop listing issuer homes at the filesystem root

This commit is contained in:
Juan Castro
2026-08-12 18:12:49 -04:00
parent 6b5d6f80ba
commit e7c856dd3f
4 changed files with 12 additions and 24 deletions
@@ -1110,7 +1110,6 @@ export class FSController extends PuterController {
const rootChildren = await listRootEntries(
actor,
this.stores.fsEntry,
this.services.permission,
);
const rootSuggestions =
await this.services.suggestedApps.getSuggestedAppsForEntries(
@@ -484,7 +484,6 @@ export class LegacyFSController extends PuterController {
const rootChildren = await listRootEntries(
actor,
this.stores.fsEntry,
this.services.permission,
);
const rootSuggestions =
await this.services.suggestedApps.getSuggestedAppsForEntries(
+7 -6
View File
@@ -66,8 +66,7 @@ const makeUser = async () => {
return { userId: created.id, username, actor };
};
const listFor = (actor: Actor) =>
listRootEntries(actor, fsEntryStore, permissionService);
const listFor = (actor: Actor) => listRootEntries(actor, fsEntryStore);
describe('listRootEntries', () => {
it('shows the actor their own home directory, exactly once', async () => {
@@ -91,7 +90,7 @@ describe('listRootEntries', () => {
);
});
it('adds the home of every user who has granted the actor a permission', async () => {
it('leaves an issuers home out of root when only a file was shared', async () => {
const holder = await makeUser();
const issuer = await makeUser();
const shared = (await fsEntryStore.getEntryByPath(
@@ -105,9 +104,11 @@ describe('listRootEntries', () => {
const entries = await listFor(holder.actor);
expect(entries.map((entry) => entry.path).sort()).toEqual(
[`/${holder.username}`, `/${issuer.username}`].sort(),
);
// Listing it here would advertise a folder readdir then refuses to
// open: the grant is on Documents, which says nothing about its parent.
expect(entries.map((entry) => entry.path)).toEqual([
`/${holder.username}`,
]);
});
it('heals a home row whose path drifted from the username', async () => {
+5 -16
View File
@@ -20,19 +20,18 @@
import type { Actor } from '../../core/actor.js';
import type { FSEntry } from '../../stores/fs/FSEntry.js';
import type { FSEntryStore } from '../../stores/fs/FSEntryStore.js';
import type { PermissionService } from '../permission/PermissionService.js';
/**
* Synthesize the listing for the virtual root `/`. There is no fsentry row at
* `/` instead root is a virtual aggregate of user-directory entries the actor
* can see: the actor's own home plus any other users' homes granted via
* permission issuers (i.e. users that have shared something with this actor).
* Mirrors v1's `LLListUsers`.
* `/` root stands in for the actor's own home.
*
* Issuer homes are deliberately absent: a grant on a file says nothing about
* its ancestors, so listing them advertised folders `readdir` then refused to
* open. Shares are reached through the sharing API instead.
*/
export async function listRootEntries(
actor: Actor,
fsEntryStore: FSEntryStore,
permissionService: PermissionService,
): Promise<FSEntry[]> {
const entries: FSEntry[] = [];
const seenPaths = new Set<string>();
@@ -69,15 +68,5 @@ export async function listRootEntries(
await pushByUsername(actor.user.username);
if (typeof userId === 'number') {
const issuers = await permissionService.listUserPermissionIssuers({
id: userId,
});
for (const issuer of issuers) {
if (!issuer) continue;
await pushByUsername(issuer.username);
}
}
return entries;
}