diff --git a/web/packages/teleport/src/lib/tdp/client.ts b/web/packages/teleport/src/lib/tdp/client.ts index f570f559eaf18..1d1d93a212671 100644 --- a/web/packages/teleport/src/lib/tdp/client.ts +++ b/web/packages/teleport/src/lib/tdp/client.ts @@ -278,6 +278,7 @@ export default class Client extends EventEmitterWebAuthnSender { lastModified: BigInt(0), fileType: FileType.File, size: BigInt(0), + isEmpty: true, path: path, }, }); @@ -306,6 +307,7 @@ export default class Client extends EventEmitterWebAuthnSender { lastModified: BigInt(0), fileType: FileType.File, size: BigInt(0), + isEmpty: true, path: req.path, }, }); @@ -411,6 +413,7 @@ export default class Client extends EventEmitterWebAuthnSender { lastModified: BigInt(info.lastModified), fileType: info.kind === 'file' ? FileType.File : FileType.Directory, size: BigInt(info.size), + isEmpty: info.isEmpty, path: info.path, }; } diff --git a/web/packages/teleport/src/lib/tdp/codec.ts b/web/packages/teleport/src/lib/tdp/codec.ts index 971073a8ee629..d90b3bd3877a7 100644 --- a/web/packages/teleport/src/lib/tdp/codec.ts +++ b/web/packages/teleport/src/lib/tdp/codec.ts @@ -221,11 +221,12 @@ export type SharedDirectoryListResponse = { fsoList: FileSystemObject[]; }; -// | last_modified uint64 | size uint64 | file_type uint32 | path_length uint32 | path byte[] | +// | last_modified uint64 | size uint64 | file_type uint32 | is_empty bool | path_length uint32 | path byte[] | export type FileSystemObject = { lastModified: bigint; size: bigint; fileType: FileType; + isEmpty: boolean; path: string; }; @@ -739,11 +740,12 @@ export default class Codec { return withFsoList.buffer; } - // | last_modified uint64 | size uint64 | file_type uint32 | path_length uint32 | path byte[] | + // | last_modified uint64 | size uint64 | file_type uint32 | is_empty bool | path_length uint32 | path byte[] | encodeFileSystemObject(fso: FileSystemObject): Message { const dataUtf8array = this.encoder.encode(fso.path); - const bufLen = 2 * uint64Length + 2 * uint32Length + dataUtf8array.length; + const bufLen = + byteLength + 2 * uint64Length + 2 * uint32Length + dataUtf8array.length; const buffer = new ArrayBuffer(bufLen); const view = new DataView(buffer); let offset = 0; @@ -753,6 +755,8 @@ export default class Codec { offset += uint64Length; view.setUint32(offset, fso.fileType); offset += uint32Length; + view.setUint8(offset, fso.isEmpty ? 1 : 0); + offset += byteLength; view.setUint32(offset, dataUtf8array.length); offset += uint32Length; dataUtf8array.forEach(byte => { diff --git a/web/packages/teleport/src/lib/tdp/sharedDirectoryManager.ts b/web/packages/teleport/src/lib/tdp/sharedDirectoryManager.ts index 60b90e8a078da..16ceede2e7dc6 100644 --- a/web/packages/teleport/src/lib/tdp/sharedDirectoryManager.ts +++ b/web/packages/teleport/src/lib/tdp/sharedDirectoryManager.ts @@ -49,10 +49,27 @@ export class SharedDirectoryManager { const fileOrDir = await this.walkPath(path); + let isEmpty = true; if (fileOrDir.kind === 'directory') { + let dir = fileOrDir; + // If dir contains any files or directories, it will + // enter the loop below and we can register it as not + // empty. If it doesn't, it will skip over the loop. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + for await (const _ of dir.keys()) { + isEmpty = false; + break; + } + // Magic numbers are the values for directories where the true // value is unavailable, according to the TDP spec. - return { size: 4096, lastModified: 0, kind: fileOrDir.kind, path }; + return { + size: 4096, + lastModified: 0, + kind: fileOrDir.kind, + isEmpty, + path, + }; } let file = await fileOrDir.getFile(); @@ -60,6 +77,7 @@ export class SharedDirectoryManager { size: file.size, lastModified: file.lastModified, kind: fileOrDir.kind, + isEmpty, path, }; } @@ -260,5 +278,6 @@ export type FileOrDirInfo = { size: number; // bytes lastModified: number; // ms since unix epoch kind: 'file' | 'directory'; + isEmpty: boolean; path: string; };