#include "precomp.h" #include <sys/stat.h> /*************************** * File Channel Operations * ***************************/ typedef struct { FILE *fd; DWORD mode; } FileContext; /* * Writes the supplied data to the file */ static DWORD file_channel_write(Channel *channel, Packet *request, LPVOID context, LPVOID buffer, DWORD bufferSize, LPDWORD bytesWritten) { FileContext *ctx = (FileContext *)context; DWORD result= ERROR_SUCCESS; LONG written = 0; // Write a chunk if ((written = fwrite(buffer, 1, bufferSize, ctx->fd)) <= 0) { written = 0; result = GetLastError(); } // Set bytesWritten if (bytesWritten) *bytesWritten = written; return result; } /* * Closes the file */ static DWORD file_channel_close(Channel *channel, Packet *request, LPVOID context) { FileContext *ctx = (FileContext *)context; fclose(ctx->fd); free(ctx); return ERROR_SUCCESS; } /* * Reads data from the file (if any) */ static DWORD file_channel_read(Channel *channel, Packet *request, LPVOID context, LPVOID buffer, DWORD bufferSize, LPDWORD bytesRead) { FileContext *ctx = (FileContext *)context; DWORD result = ERROR_SUCCESS; LONG bytes = 0; // Read a chunk if ((bytes= fread(buffer, 1, bufferSize, ctx->fd)) <= 0) { bytes = 0; result = GetLastError(); } // Set bytesRead if (bytesRead) *bytesRead = bytes; return ERROR_SUCCESS; } /* * Checks to see if the file pointer is currently at the end of the file */ static DWORD file_channel_eof(Channel *channel, Packet *request, LPVOID context, LPBOOL isEof) { FileContext *ctx = (FileContext *)context; return feof(ctx->fd) ? TRUE : FALSE; } /* * Changes the current file pointer position in the file */ static DWORD file_channel_seek(Channel *channel, Packet *request, LPVOID context, LONG offset, DWORD whence) { FileContext *ctx = (FileContext *)context; return fseek(ctx->fd, offset, whence); } /* * Returns the current offset in the file to the requestor */ static DWORD file_channel_tell(Channel *channel, Packet *request, LPVOID context, LPLONG offset) { FileContext *ctx = (FileContext *)context; DWORD result = ERROR_SUCCESS; LONG pos = 0; if ((pos = ftell(ctx->fd)) < 0) result = GetLastError(); if (offset) *offset = pos; return result; } /* * Handles the open request for a file channel and returns a valid channel * identifier to the requestor if the file is opened successfully */ DWORD request_fs_file_channel_open(Remote *remote, Packet *packet) { Packet *response = NULL; PCHAR filePath, mode; DWORD res = ERROR_SUCCESS; DWORD flags = 0; Channel *newChannel = NULL; PoolChannelOps chops = { 0 }; FileContext *ctx; LPSTR expandedFilePath = NULL; do { // Allocate a response response = packet_create_response(packet); // Get the channel flags flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS); // Allocate storage for the file context if (!(ctx = (FileContext *)malloc(sizeof(FileContext)))) { res = ERROR_NOT_ENOUGH_MEMORY; break; } // Get the file path and the mode filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); mode = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_MODE); // No file path? bogus. if (!filePath) { res = ERROR_INVALID_PARAMETER; break; } // Expand the file path if (!(expandedFilePath = fs_expand_path(filePath))) { res = ERROR_NOT_ENOUGH_MEMORY; break; } if (!mode) mode = "rb"; // Invalid file? if (!(ctx->fd = fopen(expandedFilePath, mode))) { res = GetLastError(); break; } memset(&chops, 0, sizeof(chops)); // Initialize the pool operation handlers chops.native.context = ctx; chops.native.write = file_channel_write; chops.native.close = file_channel_close; chops.read = file_channel_read; chops.eof = file_channel_eof; chops.seek = file_channel_seek; chops.tell = file_channel_tell; // Check the response allocation & allocate a un-connected // channel if ((!response) || (!(newChannel = channel_create_pool(0, flags, &chops)))) { res = ERROR_NOT_ENOUGH_MEMORY; break; } // Add the channel identifier to the response packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(newChannel)); } while (0); // Transmit the packet if it's valid packet_transmit_response(res, remote, response); // Clean up on failure if (res != ERROR_SUCCESS) { if (newChannel) channel_destroy(newChannel, NULL); if (ctx) free(ctx); } // Free the expanded file path if it was allocated if (expandedFilePath) free(expandedFilePath); return res; } /* * Gets information about the file path that is supplied and returns it to the * requestor * * TLVs: * * req: TLV_TYPE_FILE_PATH - The file path that is to be stat'd */ DWORD request_fs_stat(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); struct stat buf; LPCSTR filePath; LPSTR expanded = NULL; DWORD result = ERROR_SUCCESS; filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); // Validate parameters if (!filePath) result = ERROR_INVALID_PARAMETER; else if (!(expanded = fs_expand_path(filePath))) result = ERROR_NOT_ENOUGH_MEMORY; else { // Stat the file using the Microsoft stat wrapper so that we don't have to // do translations if (stat(expanded, &buf) < 0) result = GetLastError(); else packet_add_tlv_raw(response, TLV_TYPE_STAT_BUF, &buf, sizeof(buf)); } // Set the result and transmit the response packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); packet_transmit(remote, response, NULL); if (expanded) free(expanded); return ERROR_SUCCESS; } /* * Removes the supplied file from disk * * TLVs: * * req: TLV_TYPE_FILE_PATH - The file that is to be removed. */ DWORD request_fs_delete_file(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); LPCSTR path; DWORD result = ERROR_SUCCESS; path = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); if (!path) result = ERROR_INVALID_PARAMETER; #ifdef _WIN32 else if (!DeleteFile(path)) #else else if (!unlink(path)) #endif result = GetLastError(); packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); packet_transmit(remote, response, NULL); return ERROR_SUCCESS; } /* * Expands a file path and returns the expanded path to the requestor * * req: TLV_TYPE_FILE_PATH - The file path to expand */ DWORD request_fs_file_expand_path(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD result = ERROR_SUCCESS; LPSTR expanded = NULL; LPSTR regular; regular = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); do { // No regular path? if (!regular) { result = ERROR_INVALID_PARAMETER; break; } // Allocate storage for the expanded path if (!(expanded = fs_expand_path(regular))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } packet_add_tlv_string(response, TLV_TYPE_FILE_PATH, expanded); } while (0); // Transmit the response to the mofo packet_transmit_response(result, remote, response); if (expanded) free(expanded); return ERROR_SUCCESS; }