From 141e45f2c1542327d976dcdb3a6bc6fad9edc3b2 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Tue, 9 Aug 2022 17:44:57 -0700 Subject: [PATCH] implements SharedDirectoryDeleteRequest and SharedDirectoryDeleteResponse --- packages/teleport/src/lib/tdp/client.ts | 26 +++++++++ packages/teleport/src/lib/tdp/codec.ts | 55 +++++++++++++++++++ .../src/lib/tdp/sharedDirectoryManager.ts | 21 +++++++ 3 files changed, 102 insertions(+) diff --git a/packages/teleport/src/lib/tdp/client.ts b/packages/teleport/src/lib/tdp/client.ts index 88de278d5..f570f559e 100644 --- a/packages/teleport/src/lib/tdp/client.ts +++ b/packages/teleport/src/lib/tdp/client.ts @@ -33,6 +33,7 @@ import Codec, { SharedDirectoryReadResponse, SharedDirectoryWriteResponse, SharedDirectoryCreateResponse, + SharedDirectoryDeleteResponse, FileSystemObject, } from './codec'; import { @@ -147,6 +148,9 @@ export default class Client extends EventEmitterWebAuthnSender { // trying to write to a file that hasn't been created yet. await this.handleSharedDirectoryCreateRequest(buffer); break; + case MessageType.SHARED_DIRECTORY_DELETE_REQUEST: + this.handleSharedDirectoryDeleteRequest(buffer); + break; case MessageType.SHARED_DIRECTORY_READ_REQUEST: this.handleSharedDirectoryReadRequest(buffer); break; @@ -309,6 +313,24 @@ export default class Client extends EventEmitterWebAuthnSender { } } + async handleSharedDirectoryDeleteRequest(buffer: ArrayBuffer) { + const req = this.codec.decodeSharedDirectoryDeleteRequest(buffer); + + try { + await this.sdManager.delete(req.path); + this.sendSharedDirectoryDeleteResponse({ + completionId: req.completionId, + errCode: SharedDirectoryErrCode.Nil, + }); + } catch (e) { + this.sendSharedDirectoryDeleteResponse({ + completionId: req.completionId, + errCode: SharedDirectoryErrCode.Failed, + }); + this.handleError(e, TdpClientEvent.CLIENT_ERROR, false); + } + } + async handleSharedDirectoryReadRequest(buffer: ArrayBuffer) { const req = this.codec.decodeSharedDirectoryReadRequest(buffer); try { @@ -495,6 +517,10 @@ export default class Client extends EventEmitterWebAuthnSender { this.send(this.codec.encodeSharedDirectoryCreateResponse(response)); } + sendSharedDirectoryDeleteResponse(response: SharedDirectoryDeleteResponse) { + this.send(this.codec.encodeSharedDirectoryDeleteResponse(response)); + } + resize(spec: ClientScreenSpec) { this.send(this.codec.encodeClientScreenSpec(spec)); } diff --git a/packages/teleport/src/lib/tdp/codec.ts b/packages/teleport/src/lib/tdp/codec.ts index b01ca1671..971073a8e 100644 --- a/packages/teleport/src/lib/tdp/codec.ts +++ b/packages/teleport/src/lib/tdp/codec.ts @@ -43,6 +43,8 @@ export enum MessageType { SHARED_DIRECTORY_INFO_RESPONSE = 14, SHARED_DIRECTORY_CREATE_REQUEST = 15, SHARED_DIRECTORY_CREATE_RESPONSE = 16, + SHARED_DIRECTORY_DELETE_REQUEST = 17, + SHARED_DIRECTORY_DELETE_RESPONSE = 18, SHARED_DIRECTORY_READ_REQUEST = 19, SHARED_DIRECTORY_READ_RESPONSE = 20, SHARED_DIRECTORY_WRITE_REQUEST = 21, @@ -141,6 +143,19 @@ export type SharedDirectoryCreateResponse = { fso: FileSystemObject; }; +// | message type (17) | completion_id uint32 | directory_id uint32 | path_length uint32 | path []byte | +export type SharedDirectoryDeleteRequest = { + completionId: number; + directoryId: number; + path: string; +}; + +// | message type (18) | completion_id uint32 | err_code uint32 | +export type SharedDirectoryDeleteResponse = { + completionId: number; + errCode: SharedDirectoryErrCode; +}; + // | message type (19) | completion_id uint32 | directory_id uint32 | path_length uint32 | path []byte | offset uint64 | length uint32 | export type SharedDirectoryReadRequest = { completionId: number; @@ -615,6 +630,25 @@ export default class Codec { ]).buffer; } + // | message type (18) | completion_id uint32 | err_code uint32 | + encodeSharedDirectoryDeleteResponse( + res: SharedDirectoryDeleteResponse + ): Message { + const bufLen = byteLength + 2 * uint32Length; + const buffer = new ArrayBuffer(bufLen); + const view = new DataView(buffer); + let offset = 0; + + view.setUint8(offset, MessageType.SHARED_DIRECTORY_DELETE_RESPONSE); + offset += byteLength; + view.setUint32(offset, res.completionId); + offset += uint32Length; + view.setUint32(offset, res.errCode); + offset += uint32Length; + + return buffer; + } + // | message type (20) | completion_id uint32 | err_code uint32 | read_data_length uint32 | read_data []byte | encodeSharedDirectoryReadResponse(res: SharedDirectoryReadResponse): Message { const bufLen = @@ -866,6 +900,27 @@ export default class Codec { }; } + // | message type (17) | completion_id uint32 | directory_id uint32 | path_length uint32 | path []byte | + decodeSharedDirectoryDeleteRequest( + buffer: ArrayBuffer + ): SharedDirectoryDeleteRequest { + const dv = new DataView(buffer); + let offset = 0; + offset += byteLength; // eat message type + const completionId = dv.getUint32(offset); + offset += uint32Length; // eat completion_id + const directoryId = dv.getUint32(offset); + offset += uint32Length; // eat directory_id + offset += uint32Length; // eat path_length + const path = this.decoder.decode(new Uint8Array(buffer.slice(offset))); + + return { + completionId, + directoryId, + path, + }; + } + // | message type (19) | completion_id uint32 | directory_id uint32 | path_length uint32 | path []byte | offset uint64 | length uint32 | decodeSharedDirectoryReadRequest( buffer: ArrayBuffer diff --git a/packages/teleport/src/lib/tdp/sharedDirectoryManager.ts b/packages/teleport/src/lib/tdp/sharedDirectoryManager.ts index 2931c89bb..60b90e8a0 100644 --- a/packages/teleport/src/lib/tdp/sharedDirectoryManager.ts +++ b/packages/teleport/src/lib/tdp/sharedDirectoryManager.ts @@ -170,6 +170,27 @@ export class SharedDirectoryManager { } } + /** + * Deletes a file or directory at path. + * If the path doesn't exist, this operation is effectively ignored. + * @throws Anything potentially thrown by getFileHandle/getDirectoryHandle. + * @throws {PathDoesNotExistError} if the path isn't a valid path to a directory. + */ + async delete(path: string): Promise { + let splitPath = path.split('/'); + const fileOrDirName = splitPath.pop(); + const dirPath = splitPath.join('/'); + + const dirHandle = await this.walkPath(dirPath); + if (dirHandle.kind !== 'directory') { + throw new PathDoesNotExistError( + 'destination was a file, not a directory' + ); + } + + await dirHandle.removeEntry(fileOrDirName, { recursive: true }); + } + /** * walkPath walks a pathstr (assumed to be in the qualified Unix format specified * in the TDP spec), returning the FileSystemDirectoryHandle | FileSystemFileHandle