1372 lines
62 KiB
Plaintext
1372 lines
62 KiB
Plaintext
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: Services/Smb/Client/DirectoryClient.sg
|
|
//
|
|
// Note:
|
|
//
|
|
// This file contains the implementation of DirectoryServiceContract.
|
|
// The DirectoryClient class holds DirectoryServiceContract.Exp, on which
|
|
// it receives directory service requests, and SmbTransactor.Imp, which
|
|
// it uses to execute SMB transactions.
|
|
//
|
|
// Each instance of DirectoryServiceContract runs in its own thread.
|
|
// Access to the single TCP connection to the SMB server is multiplexed
|
|
// by the TransportMux class, which is accessed using the SmbTransactor
|
|
// channel.
|
|
//
|
|
// The general pattern is that the DirectoryClient thread loops in a
|
|
// switch-receive loop, and when it receives a directory request, it
|
|
// translates the request into SMB requests. It formats those requests,
|
|
// submits them to the SMB transport mux, then waits for the response,
|
|
// and translates the SMB protocol response into DirectoryServiceContract
|
|
// response messages.
|
|
//
|
|
// The top-level loop listens for DirectoryServiceContract messages,
|
|
// and listens for ChannelClosed on the SmbTransactor channel. If
|
|
// either channel closes, the thread terminates. While the thread
|
|
// is waiting for responses on its SmbTransactor channel, it also
|
|
// listens for ChannelClosed on the DirectoryServiceContract channel.
|
|
// This prevents the thread from blocking for a long time, if either
|
|
// channel closes.
|
|
//
|
|
// In addition to providing general directory-related requests, the
|
|
// directory service contract allows its importers to bind to other
|
|
// directories, and to bind to files. When DirectoryClient receives
|
|
// a request to bind to another directory or file, it will create
|
|
// another transactor, and another thread, to process those requests.
|
|
//
|
|
// TODO:
|
|
//
|
|
// * We don't actually verify that a path exists before allowing
|
|
// a bind to a subdirectory to succeed. Easy to do, just not
|
|
// done yet.
|
|
//
|
|
// * Exception handling is poor.
|
|
//
|
|
// * Because SMB_TRANSACTION2 is not properly supported, some
|
|
// transactions that return a large amount of data don't
|
|
// actually work correctly, such as enumerating directory
|
|
// contents.
|
|
//
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Threading;
|
|
using Microsoft.SingSharp;
|
|
using Microsoft.Singularity;
|
|
using Microsoft.Singularity.Directory;
|
|
using Microsoft.Singularity.Channels;
|
|
using Smb.PublicChannels;
|
|
using Smb.PrivateChannels;
|
|
using Smb.Protocol;
|
|
using Smb.Shared;
|
|
|
|
namespace Smb.Client
|
|
{
|
|
class DirectoryClient : ITrackedThreadContext, ITracked
|
|
{
|
|
public static void StartServiceThread(
|
|
[Claims]SmbTransactor.Imp:Ready! transactor,
|
|
[Claims]DirectoryServiceContract.Exp:Start! dir,
|
|
string! path)
|
|
{
|
|
DirectoryClient dir_client = new DirectoryClient(transactor, dir, path);
|
|
TrackedThreadStarter.StartTrackedThreadContext(dir_client);
|
|
}
|
|
|
|
private DirectoryClient(
|
|
[Claims]SmbTransactor.Imp:Ready! transactor,
|
|
[Claims]DirectoryServiceContract.Exp:Start! dir,
|
|
string! path)
|
|
{
|
|
_dir = dir;
|
|
_transactor = transactor;
|
|
_path = path;
|
|
_debugprefix = "DIR[" + path + "]: ";
|
|
_smbpath = path.Replace(SingularityPathSeparatorString, NtPathSeparatorString);
|
|
}
|
|
|
|
readonly DirectoryServiceContract.Exp! _dir;
|
|
readonly SmbTransactor.Imp! _transactor;
|
|
|
|
readonly string! _path;
|
|
readonly string! _smbpath;
|
|
readonly string! _debugprefix;
|
|
|
|
void ITrackedThreadContext.ThreadRoutine()
|
|
{
|
|
expose (this)
|
|
{
|
|
try
|
|
{
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Sending Success");
|
|
}
|
|
_dir.SendSuccess();
|
|
|
|
for (;;)
|
|
{
|
|
switch receive
|
|
{
|
|
case _dir.CreateDirectory(char[]! in ExHeap exdirName):
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received CreateDirectory: name: " + Bitter.ToString2(exdirName));
|
|
}
|
|
|
|
CreateDirectory(_dir, _transactor, Util.ToStringDelete(exdirName));
|
|
break;
|
|
|
|
case _dir.DeleteDirectory(char[]! in ExHeap exdirName):
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received DeleteDirectory: name: " + Bitter.ToString2(exdirName));
|
|
}
|
|
|
|
DeleteDirectory(_dir, _transactor, Util.ToStringDelete(exdirName));
|
|
break;
|
|
|
|
case _dir.DeleteFile(char[]! in ExHeap exfileName):
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received DeleteFile: name: " + Bitter.ToString2(exfileName));
|
|
}
|
|
|
|
DeleteFile(_dir, _transactor, Util.ToStringDelete(exfileName));
|
|
break;
|
|
|
|
case _dir.Bind(char[]! in ExHeap expath, ServiceContract.Exp:Start! exp):
|
|
{
|
|
string! path = Util.ToStringDelete(expath);
|
|
string! absolutePath = ToAbsolutePath(path);
|
|
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received Bind: path: " + path);
|
|
}
|
|
|
|
DirectoryServiceContract.Exp new_dir;
|
|
FileContract.Exp new_file;
|
|
|
|
if ((new_dir = exp as DirectoryServiceContract.Exp) != null) {
|
|
// The client wants to bind to a directory. If binding to the root directory,
|
|
// then we create a new directory service thread immediately, and ack the request.
|
|
// (The root directory always exists.) If it's not the root directory, then we
|
|
// need to verify that the path identifies a valid directory. (But for now, we
|
|
// elide that check.)
|
|
|
|
if (absolutePath != "/") {
|
|
// -XXX- check directory?
|
|
try {
|
|
bool dir_exists = CheckDirectoryExists(absolutePath);
|
|
if (!dir_exists) {
|
|
DebugLine("Path '{0}' does not exist. Rejecting bind request.", path);
|
|
_dir.SendNakBind(new_dir, ErrorCode.NotFound);
|
|
return;
|
|
}
|
|
|
|
// DebugLine("Path exists.");
|
|
|
|
} catch (Exception ex) {
|
|
DebugLine("FAILED to check if directory exists!");
|
|
Util.ShowExceptionDebug(ex, "failed to check if directory exists");
|
|
_dir.SendNakBind(new_dir, ErrorCode.Unknown);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a new _transactor for the child directory.
|
|
//
|
|
|
|
ErrorCode errorCode;
|
|
SmbTransactor.Imp new_transactor = AddTransactor(out errorCode);
|
|
|
|
if (new_transactor == null) {
|
|
DebugLine("Failed to bind directory client; could not create _transactor.");
|
|
_dir.SendNakBind(exp, errorCode);
|
|
break;
|
|
}
|
|
|
|
DirectoryClient.StartServiceThread(new_transactor, new_dir, absolutePath);
|
|
DebugLine("Successfully bound new directory client.");
|
|
_dir.SendAckBind();
|
|
|
|
} else if ((new_file = exp as FileContract.Exp) != null) {
|
|
|
|
ErrorCode errorCode;
|
|
SmbTransactor.Imp new_transactor = AddTransactor(out errorCode);
|
|
|
|
if (new_transactor == null) {
|
|
DebugLine("Failed to bind file client; could not create _transactor.");
|
|
_dir.SendNakBind(exp, errorCode);
|
|
break;
|
|
}
|
|
|
|
ushort fileId;
|
|
if (CreateFile(_transactor, absolutePath, SmbCreateDisposition.Open, out fileId, out errorCode)) {
|
|
FileExp.CreateServiceThread(new_file, absolutePath, fileId, new_transactor);
|
|
DebugLine("Bind: Successful, sending Ack");
|
|
_dir.SendAckBind();
|
|
} else {
|
|
DebugLine("Bind: Failed to open file, error code = ", errorCode);
|
|
_dir.SendNakBind(exp, errorCode);
|
|
delete new_transactor;
|
|
}
|
|
|
|
} else {
|
|
DebugLine("Received bind request for an unknown contract! Rejecting.");
|
|
_dir.SendNakBind(exp, ErrorCode.BadArguments);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _dir.BeginEnumeration():
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received BeginEnumeration");
|
|
}
|
|
|
|
EnumerateDirectory(_transactor, _dir);
|
|
break;
|
|
|
|
case _dir.CreateFile(char[]! in ExHeap expath):
|
|
{
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received CreateFile: path: " + Bitter.ToString2(expath));
|
|
}
|
|
|
|
string! path = Util.ToStringDelete(expath);
|
|
string! absolutePath = ToAbsolutePath(path);
|
|
|
|
// This is a request to create a file, and then to immediately close the file handle.
|
|
ErrorCode errorCode;
|
|
ushort fileId;
|
|
if (CreateFile(_transactor, absolutePath, SmbCreateDisposition.Create, out fileId, out errorCode)) {
|
|
DebugLine("File created, now closing.");
|
|
CloseFileId(_transactor, fileId);
|
|
DebugLine("File closed.");
|
|
_dir.SendAckCreateFile();
|
|
} else {
|
|
_dir.SendNakCreateFile(errorCode);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _dir.CreateAndBindFile(char[]! in ExHeap exfileName, FileContract.Exp:Start! exp):
|
|
{
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received CreateAndBindFile: path: " + Bitter.ToString2(exfileName));
|
|
}
|
|
string! path = Util.ToStringDelete(exfileName);
|
|
DebugLine("CreateAndFindFile: filename: " + path);
|
|
|
|
string! absolutePath = ToAbsolutePath(path);
|
|
|
|
ushort fileId;
|
|
ErrorCode errorCode;
|
|
SmbTransactor.Imp new_transactor = AddTransactor(out errorCode);
|
|
if (new_transactor == null) {
|
|
_dir.SendNakCreateAndBindFile(exp, ErrorCode.Unknown);
|
|
break;
|
|
}
|
|
|
|
if (CreateFile(_transactor, absolutePath, SmbCreateDisposition.Create, out fileId, out errorCode)) {
|
|
FileExp.CreateServiceThread(exp, absolutePath, fileId, new_transactor);
|
|
DebugLine("CreateAndBindFile: Successful, sending Ack");
|
|
_dir.SendAckCreateAndBindFile();
|
|
} else {
|
|
delete new_transactor;
|
|
DebugLine("CreateAndBindFile: Sending Nak, error code = " + errorCode);
|
|
_dir.SendNakCreateAndBindFile(exp, ErrorCode.NotSupported);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _dir.GetAttributes(char[]! in ExHeap expath):
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received GetAttributes: " + Bitter.ToString2(expath));
|
|
}
|
|
|
|
string path = Util.ToStringDelete(expath);
|
|
GetAttributes(_transactor, _dir, path);
|
|
break;
|
|
|
|
#region Requests that are not yet implemented
|
|
|
|
case _dir.CreateLink(char[]! in ExHeap exlinkPath, char[]! in ExHeap exlinkValue):
|
|
{
|
|
string linkpath = Util.ToStringDelete(exlinkPath);
|
|
string linkvalue = Util.ToStringDelete(exlinkValue);
|
|
DebugLine("CreateLink: linkpath=" + linkpath + " linkvalue=" + linkvalue);
|
|
DebugLine("CreateLink: Not implemented, refusing request");
|
|
_dir.SendNakCreateLink(ErrorCode.NotSupported);
|
|
break;
|
|
}
|
|
|
|
case _dir.DeleteLink(char[]! in ExHeap exlinkPath):
|
|
{
|
|
string linkpath = Util.ToStringDelete(exlinkPath);
|
|
_dir.SendNakDeleteLink(ErrorCode.NotSupported);
|
|
DebugLine("DeleteLink: linkpath=" + linkpath);
|
|
DebugLine("DeleteLink: Not implemented, refusing request");
|
|
break;
|
|
}
|
|
|
|
case _dir.GetLinkValue(char[]! in ExHeap exlinkpath):
|
|
{
|
|
string linkpath = Util.ToStringDelete(exlinkpath);
|
|
DebugLine("GetLinkValue: linkpath=" + linkpath);
|
|
_dir.SendNakGetLinkValue(ErrorCode.NotSupported);
|
|
DebugLine("GetLinkLink: Not implemented, refusing request");
|
|
break;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Requests that cannot be supported
|
|
|
|
//
|
|
// Channel registration doesn't really make sense for SMB.
|
|
// The Singularity root directory service implements this.
|
|
// There's nothing in SMB to map this to, aside from duplicating
|
|
// the work of the Singularity root directory service.
|
|
//
|
|
|
|
case _dir.Register(char[]! in ExHeap expath, ServiceProviderContract.Imp:Start! imp):
|
|
{
|
|
string path = Util.ToStringDelete(expath);
|
|
DebugLine("Register: path=" + path);
|
|
_dir.SendNakRegister(imp, ErrorCode.NotSupported);
|
|
break;
|
|
}
|
|
|
|
case _dir.Deregister(char[]! in ExHeap path):
|
|
DebugLine("Deregister: path=" + Util.ToStringDelete(path));
|
|
_dir.SendNakDeregister(ErrorCode.NotSupported);
|
|
break;
|
|
|
|
case _dir.Notify(
|
|
char[]! in ExHeap path,
|
|
char[]! in ExHeap pattern,
|
|
bool sendExisting,
|
|
NotifyContract.Imp:Start! imp):
|
|
delete path;
|
|
delete pattern;
|
|
_dir.SendNakNotify(imp, ErrorCode.NotSupported);
|
|
break;
|
|
|
|
//
|
|
// Singularity ACLs are completely different from NT ACLs. They are not even
|
|
// remotely similar, so there is no point in implementing StoreACL and QueryACL.
|
|
//
|
|
|
|
case _dir.QueryACL(char[]! in ExHeap path, bool effective):
|
|
delete path;
|
|
_dir.SendNakQueryACL(ErrorCode.NotSupported);
|
|
break;
|
|
|
|
case _dir.StoreACL(char[]! in ExHeap path, char[] in ExHeap acl1,
|
|
char[] in ExHeap acl2):
|
|
delete path;
|
|
delete acl1;
|
|
delete acl2;
|
|
_dir.SendNakStoreACL(ErrorCode.NotSupported);
|
|
break;
|
|
#endregion
|
|
|
|
case _dir.ChannelClosed():
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Client channel has closed");
|
|
}
|
|
goto quit;
|
|
}
|
|
}
|
|
|
|
quit:
|
|
if (TraceSwitches.ShowDirectoryMessages)
|
|
DebugLine("Disconnecting...");
|
|
|
|
} catch(Exception ex) {
|
|
DebugLine("DIRECTORY SERVICE ROUTINE THREW EXCEPTION!");
|
|
Util.ShowExceptionDebug(ex);
|
|
} finally {
|
|
// delete _dir;
|
|
// delete _transactor;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
delete _dir;
|
|
delete _transactor;
|
|
}
|
|
|
|
SmbTransactor.Imp AddTransactor(out ErrorCode errorCode)
|
|
{
|
|
expose (this)
|
|
{
|
|
_transactor.SendAddTransactor();
|
|
|
|
switch receive {
|
|
case _transactor.AckAddTransactor(new_transactor):
|
|
errorCode = ErrorCode.NoError;
|
|
return new_transactor;
|
|
|
|
case _transactor.NakAddTransactor():
|
|
DebugLine("Mux refused to add a new transactor.");
|
|
errorCode = ErrorCode.Unknown;
|
|
return null;
|
|
|
|
case _transactor.ChannelClosed():
|
|
DebugLine("Failed to create transactor for child directory -- transactor closed channel!");
|
|
errorCode = ErrorCode.Unknown;
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
string! ToAbsolutePath(string! path)
|
|
{
|
|
if (path.StartsWith("/")) {
|
|
return path;
|
|
} else {
|
|
if (_smbpath.EndsWith("/"))
|
|
return _smbpath + path;
|
|
else
|
|
return _smbpath + "/" + path;
|
|
}
|
|
}
|
|
|
|
bool CreateFile(
|
|
SmbTransactor.Imp! transactor,
|
|
string! smbpath,
|
|
SmbCreateDisposition disposition,
|
|
out ushort fileId,
|
|
out ErrorCode errorCode)
|
|
{
|
|
return CreateFile(
|
|
transactor,
|
|
smbpath,
|
|
AccessMask.MaximumAllowed,
|
|
0,
|
|
SmbFileAttributes.Normal,
|
|
SmbFileShareAccess.Read | SmbFileShareAccess.Write,
|
|
disposition,
|
|
SmbFileCreateOptions.None,
|
|
out fileId,
|
|
out errorCode);
|
|
}
|
|
|
|
bool CreateFile(
|
|
SmbTransactor.Imp! transactor,
|
|
string! smbpath,
|
|
AccessMask DesiredAccess,
|
|
long AllocationSize,
|
|
SmbFileAttributes FileAttributes,
|
|
SmbFileShareAccess ShareAccess,
|
|
SmbCreateDisposition CreateDisposition,
|
|
SmbFileCreateOptions CreateOptions,
|
|
out ushort fileId,
|
|
out ErrorCode errorCode)
|
|
{
|
|
if (smbpath.IndexOf('/') != -1)
|
|
smbpath = smbpath.Replace('/', '\\');
|
|
|
|
ByteWriter data = new ByteWriter();
|
|
data.WriteStringUnicode(smbpath, false);
|
|
|
|
int requestLength = sizeof(SmbNtCreateAndXRequest) + data.Length;
|
|
|
|
byte[]! in ExHeap requestEncoded = new[ExHeap] byte[requestLength];
|
|
ref SmbNtCreateAndXRequest request = ref requestEncoded[0];
|
|
|
|
request.Header.Prepare(SmbCommand.NtCreateAndX, 24);
|
|
|
|
request.AndXCommand = 0xff; // no andx request
|
|
request.AndXReserved = 0;
|
|
request.AndXOffset = 0;
|
|
request.Reserved = 0;
|
|
request.NameLength = (ushort)data.Length;
|
|
request.Flags = 0x10; // 0x10 = request extended errors
|
|
request.RootDirectoryFid = 0;
|
|
request.DesiredAccess = (uint)DesiredAccess;
|
|
request.AllocationSize = (ulong)AllocationSize;
|
|
request.ExtFileAttributes = (uint)FileAttributes;
|
|
request.ShareAccess = (uint)ShareAccess;
|
|
request.CreateDisposition = (uint)CreateDisposition;
|
|
request.CreateOptions = (uint)CreateOptions | 0x40; // 0x40 == must be a file
|
|
request.ImpersonationLevel = (uint)SmbImpersonationLevel.Impersonation;
|
|
request.SecurityFlags = 3; // 1 = dynamic security tracking, 2 = only enable aspects avail to server
|
|
request.ByteCount = (ushort)data.Length;
|
|
|
|
data.CopyTo(requestEncoded, sizeof(SmbNtCreateAndXRequest));
|
|
|
|
transactor.SendRequest(requestEncoded);
|
|
|
|
switch receive {
|
|
case transactor.Response(responseEncoded):
|
|
try {
|
|
ref SmbHeader responseHeader = ref responseEncoded[0];
|
|
if (responseHeader.IsError) {
|
|
DebugLine("Server returned error code for CreateAndX request: " + responseHeader.GetErrorText());
|
|
errorCode = GetDsErrorFromSmb(ref responseHeader);
|
|
fileId = 0;
|
|
return false;
|
|
}
|
|
|
|
ref SmbNtCreateAndXResponse response = ref responseEncoded[0];
|
|
if (TraceSwitches.ShowIncrediblyNoisyDetail) {
|
|
DebugLine("NtCreateFile response:");
|
|
DebugLine(" Fid: " + response.Fid);
|
|
DebugLine(" CreateAction: " + response.CreateAction);
|
|
DebugLine(" CreationTime: " + response.CreationTime);
|
|
DebugLine(" LastAccessTime: " + response.LastAccessTime);
|
|
DebugLine(" LastWriteTime: " + response.LastWriteTime);
|
|
DebugLine(" ChangeTime: " + response.ChangeTime);
|
|
DebugLine(" ExtFileAttributes: {0:x8}", response.ExtFileAttributes);
|
|
DebugLine(" AllocationSize: " + response.AllocationSize);
|
|
DebugLine(" EndOfFile: " + response.EndOfFile);
|
|
DebugLine(" FileType: " + response.FileType);
|
|
DebugLine(" DeviceState: " + response.DeviceState);
|
|
DebugLine(" Directory: " + response.Directory);
|
|
DebugLine(" ByteCount: " + response.ByteCount);
|
|
}
|
|
|
|
fileId = response.Fid;
|
|
errorCode = ErrorCode.NoError;
|
|
} finally {
|
|
delete responseEncoded;
|
|
}
|
|
return true;
|
|
|
|
case transactor.RequestFailed(transactionError):
|
|
fileId = 0;
|
|
errorCode = ErrorCode.Unknown;
|
|
return false;
|
|
|
|
case transactor.ChannelClosed():
|
|
fileId = 0;
|
|
errorCode = ErrorCode.Unknown;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool CheckDirectoryExists(string! absolutePath)
|
|
{
|
|
/*
|
|
ByteWriter! data = new ByteWriter();
|
|
data.WriteStringUnicode(absolutePath, true);
|
|
|
|
int messageLength = sizeof(SmbCheckDirectoryRequest) + data.Length;
|
|
byte[] in ExHeap request_encoded = new[ExHeap] byte[messageLength];
|
|
ref SmbCheckDirectoryRequest request = ref request_encoded[0];
|
|
|
|
request.TransactionHeader.Header.Prepare(SmbCommand.CheckDirectory, 0);
|
|
request.TransactionHeader.TotalParameterCount = 0;
|
|
request.TransactionHeader.TotalDataCount = (ushort)data.Length;
|
|
request.TransactionHeader.MaxParameterCount = 50;
|
|
|
|
request.TransactionHeader.BufferFormat = 4;
|
|
data.CopyTo(request_encoded, sizeof(SmbCheckDirectoryRequest));
|
|
|
|
transactor.SendRequest(request_encoded, messageLength);
|
|
|
|
byte[]! in ExHeap response_encoded = WaitResponse(dir, transactor);
|
|
|
|
delete response_encoded;
|
|
*/
|
|
DebugLine("CheckDirectoryExists - not yet implemented, returning true");
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
This method implements directory enumeration. It uses the SMB request
|
|
TRANS2_FIND_FIRST2 to search a directory.
|
|
*/
|
|
void EnumerateDirectory(SmbTransactor.Imp! transactor, DirectoryServiceContract.Exp:Enumerate! dir)
|
|
{
|
|
byte[]! in ExHeap request = EncodeFindFirst2Request(_path);
|
|
transactor.SendRequest(request);
|
|
|
|
ushort searchId = 0;
|
|
uint resumeKey = 0;
|
|
bool first = true;
|
|
|
|
bool moreRecords;
|
|
|
|
for (;;)
|
|
{
|
|
// At this point, we are waiting for a response from either a FIND FIRST or a
|
|
// FIND NEXT request. Since most of the processing is the same, we combine it here.
|
|
|
|
byte[]! in ExHeap response = WaitResponse(dir, transactor, first ? "FIND FIRST" : "FIND CLOSE");
|
|
try {
|
|
|
|
ref SmbHeader header = ref response[0];
|
|
|
|
if (header.IsError) {
|
|
NtStatus status = header.GetNtStatus();
|
|
|
|
if (status == NtStatus.NoSuchFile) {
|
|
DebugLine("Received STATUS_NO_SUCH_FILE from server.");
|
|
dir.SendEnumerationTerminated(ErrorCode.NoError);
|
|
} else {
|
|
DebugLine("FIND_FIRST2 request failed. Received error from server.");
|
|
dir.SendEnumerationTerminated(GetDsErrorFromSmb(ref header));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// DebugLine("Received successful response to FIND FIRST request.");
|
|
|
|
// Next, scan through the response, enumerate all entries, and convert to
|
|
// EnumerationRecords[], and dir.SendEnumerationRecords. Then wait for
|
|
// the next message from dir, then decide what to do next.
|
|
|
|
ushort responseSearchId;
|
|
EnumerationRecords[] in ExHeap records = ExtractDirectoryEntries(response, out moreRecords, out responseSearchId);
|
|
if (records == null) {
|
|
// DebugLine("No enumeration records returned.");
|
|
dir.SendEnumerationTerminated(ErrorCode.NoError);
|
|
return;
|
|
}
|
|
|
|
if (first)
|
|
searchId = responseSearchId;
|
|
|
|
// DebugLine("Sending enumeration records to client, count = " + records.Length.ToString());
|
|
dir.SendEnumerationEntries(records, moreRecords);
|
|
// Now 'dir' is in the EnumerateAck state.
|
|
// Valid in messages are: ReadEnumeration, EndEnumeration.
|
|
} finally {
|
|
delete response;
|
|
}
|
|
|
|
switch receive {
|
|
case dir.ChannelClosed():
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Directory client has closed channel.");
|
|
}
|
|
if (moreRecords) {
|
|
FindClose(transactor, searchId);
|
|
}
|
|
return;
|
|
|
|
case transactor.ChannelClosed():
|
|
DebugLine("SMB transactor has closed channel.");
|
|
throw new Exception("SMB transactor has closed channel.");
|
|
|
|
case dir.ReadEnumeration():
|
|
if (moreRecords) {
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received ReadEnumeration.");
|
|
}
|
|
} else {
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received ReadEnumeration, but we already told the client there aren't any more!");
|
|
DebugLine("Silly client. Sending EnumerationEntries with more = false again.");
|
|
}
|
|
for (;;) {
|
|
dir.SendEnumerationEntries(new[ExHeap] EnumerationRecords[0], false);
|
|
switch receive {
|
|
case dir.EndEnumeration():
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received EndEnumeration");
|
|
}
|
|
return;
|
|
|
|
|
|
case dir.ChannelClosed():
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Directory channel has closed");
|
|
}
|
|
return;
|
|
|
|
case dir.ReadEnumeration():
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received ReadEnumeration");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case dir.EndEnumeration():
|
|
if (TraceSwitches.ShowDirectoryMessages) {
|
|
DebugLine("Received EndEnumeration.");
|
|
}
|
|
if (moreRecords) {
|
|
DebugLine("Local client ended directory enumeration before the end of the records. Sending FIND CLOSE.");
|
|
FindClose(transactor, searchId);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
// At this point, we know that the server still has more enumeration records for us.
|
|
// So we need to loop, sending FIND NEXT requests to the server, and supplying the
|
|
// search handle that we received from the FIND FIRST response.
|
|
|
|
|
|
// DebugLine("Sending FIND NEXT request, search id = " + searchId);
|
|
byte[]! in ExHeap find_next_request = EncodeFindNextRequest(searchId, resumeKey);
|
|
transactor.SendRequest(find_next_request);
|
|
}
|
|
}
|
|
|
|
void FindClose(SmbTransactor.Imp! transactor, ushort searchId)
|
|
{
|
|
DebugLine("FindClose: not yet implemented");
|
|
/*
|
|
byte[]! in ExHeap request = EncodeFindCloseRequest(searchId);
|
|
transactor.SendRequest(request);
|
|
byte[]! in ExHeap response = WaitResponse(transactor, "FIND CLOSE");
|
|
delete response;
|
|
*/
|
|
}
|
|
|
|
EnumerationRecords[] in ExHeap ExtractDirectoryEntries(byte[]! in ExHeap smbresponse, out bool moreRecords, out ushort searchId)
|
|
{
|
|
ref SmbTransaction2Response response = ref smbresponse[0];
|
|
|
|
int parameterOffset = response.Transaction.ParameterOffset;
|
|
int parameterCount = response.Transaction.TotalParameterCount;
|
|
assert parameterCount >= 0;
|
|
assert parameterOffset >= 0;
|
|
|
|
if (parameterCount == 0) {
|
|
DebugLine("No directory entries.");
|
|
moreRecords = false;
|
|
searchId = 0;
|
|
return null;
|
|
}
|
|
|
|
if (parameterOffset + SmbHeader.FramingHeaderLength + parameterCount > smbresponse.Length) {
|
|
DebugLine("Message is invalid. The combination of ParameterCount and ParameterOffset cannot lie within the SMB message.");
|
|
moreRecords = false;
|
|
searchId = 0;
|
|
return null;
|
|
}
|
|
|
|
ref SmbFindFirstResponseParametersParameters parameters = ref smbresponse[parameterOffset + SmbHeader.FramingHeaderLength];
|
|
if (TraceSwitches.ShowIncrediblyNoisyDetail) {
|
|
DebugLine("FindFirst / FindNext Response Parameters:");
|
|
DebugLine(" SearchId: " + parameters.SearchId);
|
|
DebugLine(" SearchCount: " + parameters.SearchCount);
|
|
DebugLine(" EndOfSearch: " + parameters.EndOfSearch);
|
|
DebugLine(" EaErrorOffset: " + parameters.EaErrorOffset);
|
|
DebugLine(" LastNameOffset: " + parameters.LastNameOffset);
|
|
}
|
|
|
|
int offset = response.Transaction.DataOffset + SmbHeader.FramingHeaderLength;
|
|
int limit = offset + response.Transaction.TotalDataCount;
|
|
|
|
if (limit > smbresponse.Length) {
|
|
DebugLine("Message (Find First / Next Response) is invalid. The combination of DataOffset and DataCount exceeds the length of the message.");
|
|
moreRecords = false;
|
|
searchId = 0;
|
|
return null;
|
|
}
|
|
|
|
ArrayList list = new ArrayList();
|
|
|
|
if (TraceSwitches.ShowIncrediblyNoisyDetail) {
|
|
DebugLine("Enumeration entries (hopefully): offset 0x{0:x} limit 0x{1:x}", offset, limit);
|
|
Util.DumpBuffer(smbresponse, offset, limit - offset);
|
|
}
|
|
|
|
while (offset < limit) {
|
|
if (offset + sizeof(SmbFindFileDirectoryInfo) > limit) {
|
|
DebugLine("Invalid data near the end of the directory enumeration data.");
|
|
DebugLine("Too few bytes to be a real SmbFindFileDirectoryInfo.");
|
|
break;
|
|
}
|
|
|
|
ref SmbFindFileDirectoryInfo entry = ref smbresponse[offset];
|
|
|
|
if (TraceSwitches.ShowIncrediblyNoisyDetail) {
|
|
DebugLine(" Directory Entry:");
|
|
DebugLine(" NextEntryOffset: " + entry.NextEntryOffset);
|
|
DebugLine(" FileIndex: " + entry.FileIndex);
|
|
DebugLine(" CreationTime: " + entry.CreationTime);
|
|
DebugLine(" LastAccessTime: " + entry.LastAccessTime);
|
|
DebugLine(" LastWriteTime: " + entry.LastWriteTime);
|
|
DebugLine(" ChangeTime: " + entry.ChangeTime);
|
|
DebugLine(" EndOfFile: " + entry.EndOfFile);
|
|
DebugLine(" AllocationSize: " + entry.AllocationSize);
|
|
DebugLine(" ExtFileAttributes: {0:x8}", entry.ExtFileAttributes);
|
|
DebugLine(" FileNameLength: " + entry.FileNameLength);
|
|
}
|
|
|
|
byte[]! raw_name = new byte[entry.FileNameLength];
|
|
int nameOffset = offset + SmbFindFileDirectoryInfo.SizeOf;
|
|
Bitter.ToByteArray(smbresponse, nameOffset, (int)entry.FileNameLength, raw_name, 0);
|
|
string! name = System.Text.Encoding.Unicode.GetString(raw_name);
|
|
|
|
if (TraceSwitches.ShowIncrediblyNoisyDetail) {
|
|
DebugLine(" Name raw bytes: ");
|
|
Util.DumpBuffer(raw_name);
|
|
DebugLine(" Name: " + name);
|
|
}
|
|
|
|
bool is_dir = (entry.ExtFileAttributes & 0x10) != 0;
|
|
|
|
DirEntry dentry = new DirEntry(name, is_dir ? NodeType.Directory : NodeType.File);
|
|
list.Add(dentry);
|
|
|
|
// DebugStub.Break();
|
|
|
|
if (entry.NextEntryOffset == 0)
|
|
break;
|
|
|
|
if (entry.NextEntryOffset < SmbFindFileDirectoryInfo.SizeOf + entry.FileNameLength) {
|
|
DebugLine("WARNING: Directory enumeration entry contains illegal NextEntryOffset; the value is too small. No more enumeration entries will be parsed.");
|
|
DebugLine("This is occurring due to a known bug/limitation -- we don't properly support TRANSACTION2 bodies that span multiple SMB messages.");
|
|
DebugStub.Break();
|
|
break;
|
|
}
|
|
|
|
offset += (int)entry.NextEntryOffset;
|
|
}
|
|
|
|
int recordCount = list.Count;
|
|
EnumerationRecords[] in ExHeap records = new[ExHeap] EnumerationRecords[recordCount];
|
|
for (int i = 0; i < recordCount; i++) {
|
|
DirEntry! direntry = (!)(DirEntry)list[i];
|
|
|
|
expose(records[i])
|
|
{
|
|
char[]! in ExHeap previousPath = records[i].Path;
|
|
if (previousPath != null)
|
|
delete previousPath;
|
|
|
|
records[i].Path = Bitter.FromString2(direntry.Name);
|
|
records[i].Type = direntry.Type;
|
|
}
|
|
}
|
|
|
|
moreRecords = (parameters.EndOfSearch == 0);
|
|
searchId = parameters.SearchId;
|
|
|
|
return records;
|
|
}
|
|
|
|
class DirEntry
|
|
{
|
|
public string! Name;
|
|
public NodeType Type;
|
|
|
|
public DirEntry(string! name, NodeType type)
|
|
{
|
|
this.Name = name;
|
|
this.Type = type;
|
|
}
|
|
}
|
|
|
|
const char SingularityPathSeparator = '/';
|
|
const char NtPathSeparator = '\\';
|
|
const string SingularityPathSeparatorString = "/";
|
|
const string NtPathSeparatorString = "\\";
|
|
|
|
static byte[]! in ExHeap EncodeFindFirst2Request(string! path)
|
|
{
|
|
string! pattern = path.Replace(SingularityPathSeparator, NtPathSeparator);
|
|
if (!pattern.StartsWith(NtPathSeparatorString))
|
|
pattern = NtPathSeparatorString + pattern;
|
|
|
|
if (pattern.EndsWith(NtPathSeparatorString))
|
|
pattern += "*";
|
|
else
|
|
pattern += "\\*";
|
|
|
|
// 'data' contains the following:
|
|
// STRING Name[]; must be empty
|
|
// byte Pad[]; pad to alignment
|
|
// byte Parameters[]; not sure
|
|
// byte Pad1[]; pad to alignment
|
|
//
|
|
|
|
ByteWriter parameters = new ByteWriter();
|
|
// USHORT SearchAttributes;
|
|
parameters.WriteUInt16Le((ushort)(
|
|
SmbSearchAttributes.Hidden
|
|
| SmbSearchAttributes.System
|
|
| SmbSearchAttributes.Directory));
|
|
// SearchCount
|
|
parameters.WriteUInt16Le((ushort)1000); // max number of entries to return
|
|
// USHORT Flags
|
|
parameters.WriteUInt16Le((ushort)(
|
|
SmbSearchFlags.CloseIfLastResponse
|
|
| SmbSearchFlags.ReturnResumeKeysForEachEntry));
|
|
// USHORT InformationLevel
|
|
parameters.WriteUInt16Le((ushort)SmbSearchInformationLevel.FileFullDirectoryInfo);
|
|
// ULONG SearchStorageType
|
|
parameters.WriteUInt32Le((uint)0);
|
|
// parameters.WriteZero(1); // alignment padding
|
|
parameters.WriteStringUnicode(pattern, true);
|
|
|
|
//ByteWriter data = new ByteWriter();
|
|
// data.Write(parameters);
|
|
|
|
int trantailOffset = sizeof(SmbHeader) + sizeof(SmbTransaction2RequestHeader);
|
|
|
|
ByteWriter trantail = new ByteWriter();
|
|
trantail.WriteUInt16Le(0); // ByteCount
|
|
trantail.WriteZero(2); // STRING[] name; always empty
|
|
int parametersOffset = trantailOffset + trantail.Position;
|
|
trantail.Write(parameters);
|
|
|
|
int requestLength = trantailOffset + trantail.Length;
|
|
byte[] in ExHeap request_encoded = new[ExHeap] byte[requestLength];
|
|
|
|
ref SmbHeader header = ref request_encoded[0];
|
|
header.Prepare(SmbCommand.Transaction2, 15);
|
|
|
|
ref SmbTransaction2RequestHeader transaction = ref request_encoded[sizeof(SmbHeader)];
|
|
transaction.TotalDataCount = 0; // total size of extended attr list
|
|
transaction.SetupCount = 1;
|
|
transaction.TotalParameterCount = (ushort)parameters.Length;
|
|
transaction.MaxParameterCount = 10;
|
|
transaction.MaxDataCount = 0x4000;
|
|
transaction.MaxSetupCount = 0;
|
|
transaction.Reserved = 0;
|
|
transaction.Flags = 0;
|
|
transaction.Timeout = 0;
|
|
transaction.Reserved2 = 0;
|
|
transaction.ParameterCount = (ushort)parameters.Length;
|
|
transaction.ParameterOffset = (ushort)(parametersOffset - 4); // -4 compensates for SMB framing header
|
|
transaction.DataCount = 0;
|
|
transaction.DataOffset = 0;
|
|
transaction.SetupCount = 1;
|
|
transaction.Setup0 = (ushort)SmbTransaction2Code.FindFirst;
|
|
|
|
request_encoded[parametersOffset] = (byte)(parameters.Length & 0xff);
|
|
request_encoded[parametersOffset] = (byte)(parameters.Length >> 8);
|
|
trantail.CopyTo(request_encoded, trantailOffset);
|
|
|
|
return request_encoded;
|
|
}
|
|
|
|
static byte[]! in ExHeap EncodeFindNextRequest(ushort searchId, uint resumeKey)
|
|
{
|
|
// 'data' contains the following:
|
|
// STRING Name[]; must be empty
|
|
// byte Pad[]; pad to alignment
|
|
// byte Parameters[]; not sure
|
|
// byte Pad1[]; pad to alignment
|
|
//
|
|
|
|
ByteWriter parameters = new ByteWriter();
|
|
// USHORT Sid; // search ID
|
|
parameters.WriteUInt16Le(searchId);
|
|
// SearchCount
|
|
parameters.WriteUInt16Le((ushort)1000); // max number of entries to return
|
|
// USHORT InformationLevel
|
|
parameters.WriteUInt16Le((ushort)SmbSearchInformationLevel.FileFullDirectoryInfo);
|
|
parameters.WriteUInt32Le(resumeKey);
|
|
// USHORT Flags
|
|
parameters.WriteUInt16Le((ushort)(
|
|
SmbSearchFlags.CloseIfLastResponse
|
|
| SmbSearchFlags.ReturnResumeKeysForEachEntry));
|
|
// STRING FileName;
|
|
parameters.WriteStringUnicode("", true);
|
|
|
|
//ByteWriter data = new ByteWriter();
|
|
// data.Write(parameters);
|
|
|
|
int trantailOffset = sizeof(SmbHeader) + sizeof(SmbTransaction2RequestHeader);
|
|
|
|
ByteWriter trantail = new ByteWriter();
|
|
trantail.WriteUInt16Le(0); // ByteCount
|
|
trantail.WriteZero(2); // STRING[] name; always empty
|
|
int parametersOffset = trantailOffset + trantail.Position;
|
|
trantail.Write(parameters);
|
|
|
|
int requestLength = trantailOffset + trantail.Length;
|
|
byte[] in ExHeap request_encoded = new[ExHeap] byte[requestLength];
|
|
|
|
ref SmbHeader header = ref request_encoded[0];
|
|
header.Prepare(SmbCommand.Transaction2, 15);
|
|
|
|
ref SmbTransaction2RequestHeader transaction = ref request_encoded[sizeof(SmbHeader)];
|
|
transaction.TotalDataCount = 0; // total size of extended attr list
|
|
transaction.SetupCount = 1;
|
|
transaction.TotalParameterCount = (ushort)parameters.Length;
|
|
transaction.MaxParameterCount = 10;
|
|
transaction.MaxDataCount = 0x4000;
|
|
transaction.MaxSetupCount = 0;
|
|
transaction.Reserved = 0;
|
|
transaction.Flags = 0;
|
|
transaction.Timeout = 0;
|
|
transaction.Reserved2 = 0;
|
|
transaction.ParameterCount = (ushort)parameters.Length;
|
|
transaction.ParameterOffset = (ushort)(parametersOffset - 4); // -4 compensates for SMB framing header
|
|
transaction.DataCount = 0;
|
|
transaction.DataOffset = 0;
|
|
transaction.SetupCount = 1;
|
|
transaction.Setup0 = (ushort)SmbTransaction2Code.FindNext;
|
|
|
|
request_encoded[parametersOffset] = (byte)(parameters.Length & 0xff);
|
|
request_encoded[parametersOffset] = (byte)(parameters.Length >> 8);
|
|
trantail.CopyTo(request_encoded, trantailOffset);
|
|
|
|
return request_encoded;
|
|
}
|
|
|
|
byte[]! in ExHeap WaitResponse(DirectoryServiceContract.Exp! dir, SmbTransactor.Imp! transactor, string! annotation)
|
|
{
|
|
switch receive {
|
|
case dir.ChannelClosed():
|
|
throw new Exception("The directory service client has closed its channel.");
|
|
|
|
case transactor.Response(byte[]! in ExHeap response_encoded):
|
|
return response_encoded;
|
|
|
|
case transactor.ChannelClosed():
|
|
throw new Exception("An internal error has occurred. The SMB multiplexer closed the SmbTransactor channel before replying.");
|
|
|
|
case transactor.RequestFailed(SmbTransactionError error):
|
|
throw new Exception("The SMB transaction failed. Error code: " + error.ToString());
|
|
}
|
|
}
|
|
|
|
byte[]! in ExHeap WaitResponse(SmbTransactor.Imp! transactor, string! annotation)
|
|
{
|
|
switch receive {
|
|
case transactor.Response(byte[]! in ExHeap response_encoded):
|
|
return response_encoded;
|
|
|
|
case transactor.ChannelClosed():
|
|
throw new Exception("An internal error has occurred. The SMB multiplexer closed the SmbTransactor channel before replying.");
|
|
|
|
case transactor.RequestFailed(error):
|
|
throw new Exception("The SMB transaction failed. Error code: " + error.ToString());
|
|
}
|
|
}
|
|
|
|
void CreateDirectory(DirectoryServiceContract.Exp! dir, SmbTransactor.Imp! transactor, string! singpath)
|
|
{
|
|
if (ContainsSpecialChars(singpath)) {
|
|
dir.SendNakCreateDirectory(ErrorCode.BadArguments);
|
|
return;
|
|
}
|
|
|
|
string! smbpath = ToAbsoluteSmbPath(singpath);
|
|
|
|
ByteWriter parameters = new ByteWriter();
|
|
parameters.WriteUInt32Le(0); // reserved
|
|
parameters.WriteStringUnicode(smbpath, true);
|
|
|
|
int parameterOffset = Util.Align4(sizeof(SmbCreateDirectoryRequest));
|
|
int requestLength = parameterOffset + parameters.Length;
|
|
byte[] in ExHeap requestEncoded = new[ExHeap] byte[requestLength];
|
|
|
|
ref SmbCreateDirectoryRequest request = ref requestEncoded[0];
|
|
request.Header.Prepare(SmbCommand.Transaction2, 15);
|
|
request.Transaction.MaxSetupCount = 0;
|
|
request.Transaction.SetupCount = 1;
|
|
request.Transaction.TotalParameterCount = (ushort)parameters.Length;
|
|
request.Transaction.ParameterCount = (ushort)parameters.Length;
|
|
request.Transaction.ParameterOffset = (ushort)(parameterOffset - SmbHeader.FramingHeaderLength);
|
|
request.Transaction.MaxParameterCount = (ushort)100;
|
|
request.Transaction.Setup0 = (ushort)SmbTransaction2Code.CreateDirectory;
|
|
|
|
parameters.CopyTo(requestEncoded, sizeof(SmbCreateDirectoryRequest));
|
|
|
|
transactor.SendRequest(requestEncoded);
|
|
|
|
switch receive {
|
|
case transactor.Response(responseEncoded):
|
|
ref SmbHeader response = ref responseEncoded[0];
|
|
if (response.IsError) {
|
|
DebugLine("FAILED to create directory: " + response.GetErrorText());
|
|
ErrorCode errorCode = GetDsErrorFromSmb(ref response);
|
|
dir.SendNakCreateDirectory(errorCode);
|
|
} else {
|
|
DebugLine("Successfully created directory: " + singpath);
|
|
dir.SendAckCreateDirectory();
|
|
}
|
|
delete responseEncoded;
|
|
break;
|
|
|
|
case transactor.RequestFailed(error):
|
|
dir.SendNakCreateDirectory(ErrorCode.Unknown);
|
|
break;
|
|
|
|
case transactor.ChannelClosed():
|
|
dir.SendNakCreateDirectory(ErrorCode.Unknown);
|
|
break;
|
|
}
|
|
}
|
|
|
|
string! ToAbsoluteSmbPath(string! path)
|
|
{
|
|
string! path_as_smb = path.Replace(SingularityPathSeparator, NtPathSeparator);
|
|
|
|
if (path_as_smb.StartsWith(NtPathSeparatorString))
|
|
{
|
|
// The path is already in absolute form.
|
|
return path_as_smb;
|
|
}
|
|
else
|
|
{
|
|
// The path is a relative path. Qualify it using the path of the current directory.
|
|
if (_smbpath.EndsWith(NtPathSeparatorString))
|
|
return _smbpath + path_as_smb;
|
|
else
|
|
return _smbpath + NtPathSeparatorString + path_as_smb;
|
|
}
|
|
}
|
|
|
|
static bool ContainsSpecialChars(string! path)
|
|
{
|
|
return path.IndexOfAny(_specialChars) != -1;
|
|
}
|
|
|
|
static readonly char[]! _specialChars = { '<', '>', '*', '?', '"' };
|
|
|
|
void DeleteFile(DirectoryServiceContract.Exp! dir, SmbTransactor.Imp! transactor, string! singpath)
|
|
{
|
|
// The SMB protocol actually allows for wildcards in the DELETE FILE request.
|
|
// However, I don't want to support this until DirectoryServiceContract explicitly
|
|
// provides for wildcards.
|
|
if (ContainsSpecialChars(singpath)) {
|
|
dir.SendNakDeleteFile(ErrorCode.BadArguments);
|
|
return;
|
|
}
|
|
|
|
string! smbpath = ToAbsoluteSmbPath(singpath);
|
|
|
|
ByteWriter data = new ByteWriter();
|
|
data.WriteStringUnicode(smbpath, true);
|
|
|
|
int requestLength = sizeof(SmbDeleteFileRequest) + data.Length;
|
|
byte[]! in ExHeap requestEncoded = new[ExHeap] byte[requestLength];
|
|
|
|
ref SmbDeleteFileRequest request = ref requestEncoded[0];
|
|
request.Header.Prepare(SmbCommand.DeleteFile, SmbDeleteFileRequest.ParameterCount);
|
|
request.SearchAttributes = 0;
|
|
request.ByteCount = (ushort)data.Length;
|
|
request.BufferFormat = 4;
|
|
|
|
data.CopyTo(requestEncoded, sizeof(SmbDeleteFileRequest));
|
|
|
|
transactor.SendRequest(requestEncoded);
|
|
|
|
switch receive {
|
|
case transactor.Response(byte[]! in ExHeap responseEncoded):
|
|
ref SmbHeader response = ref responseEncoded[0];
|
|
if (response.IsError) {
|
|
// DebugLine("Server failed DELETE FILE request: " + response.GetErrorText());
|
|
ErrorCode errorCode = GetDsErrorFromSmb(ref response);
|
|
dir.SendNakDeleteFile(errorCode);
|
|
} else {
|
|
// DebugLine("File successfully deleted.");
|
|
dir.SendAckDeleteFile();
|
|
}
|
|
delete responseEncoded;
|
|
break;
|
|
|
|
case transactor.RequestFailed(SmbTransactionError error):
|
|
dir.SendNakDeleteFile(ErrorCode.Unknown);
|
|
break;
|
|
|
|
case transactor.ChannelClosed():
|
|
DebugLine("Transactor closed while waiting for response.");
|
|
dir.SendNakDeleteFile(ErrorCode.Unknown);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DeleteDirectory(DirectoryServiceContract.Exp! dir, SmbTransactor.Imp! transactor, string! singpath)
|
|
{
|
|
if (ContainsSpecialChars(singpath)) {
|
|
dir.SendNakDeleteDirectory(ErrorCode.BadArguments);
|
|
return;
|
|
}
|
|
|
|
string! smbpath = ToAbsoluteSmbPath(singpath);
|
|
|
|
ByteWriter data = new ByteWriter();
|
|
data.WriteStringUnicode(smbpath, true);
|
|
|
|
int requestLength = sizeof(SmbDeleteDirectoryRequest) + data.Length;
|
|
byte[]! in ExHeap requestEncoded = new[ExHeap] byte[requestLength];
|
|
|
|
ref SmbDeleteFileRequest request = ref requestEncoded[0];
|
|
request.Header.Prepare(SmbCommand.DeleteDirectory, SmbDeleteDirectoryRequest.ParameterCount);
|
|
request.ByteCount = (ushort)data.Length;
|
|
request.BufferFormat = 4;
|
|
|
|
data.CopyTo(requestEncoded, sizeof(SmbDeleteDirectoryRequest));
|
|
transactor.SendRequest(requestEncoded);
|
|
|
|
switch receive {
|
|
case transactor.Response(byte[]! in ExHeap responseEncoded):
|
|
ref SmbHeader response = ref responseEncoded[0];
|
|
if (response.IsError) {
|
|
DebugLine("Server failed DELETE FILE request: " + response.GetErrorText());
|
|
ErrorCode errorCode = GetDsErrorFromSmb(ref response);
|
|
dir.SendNakDeleteFile(errorCode);
|
|
} else {
|
|
DebugLine("File successfully deleted.");
|
|
dir.SendAckDeleteFile();
|
|
}
|
|
delete responseEncoded;
|
|
break;
|
|
|
|
case transactor.RequestFailed(SmbTransactionError error):
|
|
dir.SendNakDeleteFile(ErrorCode.Unknown);
|
|
break;
|
|
|
|
case transactor.ChannelClosed():
|
|
DebugLine("Transactor closed while waiting for response.");
|
|
dir.SendNakDeleteFile(ErrorCode.Unknown);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CloseFileId(SmbTransactor.Imp! transactor, ushort fileId)
|
|
{
|
|
int requestLength = sizeof(SmbCloseFileRequest);
|
|
byte[]! in ExHeap request_encoded = new[ExHeap] byte[requestLength];
|
|
ref SmbCloseFileRequest request = ref request_encoded[0];
|
|
request.Header.Prepare(SmbCommand.CloseFile, 3);
|
|
request.Fid = fileId;
|
|
request.ByteCount = 0;
|
|
|
|
transactor.SendRequest(request_encoded);
|
|
|
|
switch receive {
|
|
case transactor.Response(responseEncoded):
|
|
delete responseEncoded;
|
|
break;
|
|
|
|
case transactor.RequestFailed(error):
|
|
DebugLine("The CLOSE FILE request has failed. The server returned an error code: " + error);
|
|
break;
|
|
|
|
case transactor.ChannelClosed():
|
|
DebugLine("Failed to execute CLOSE FILE request. The transactor channel has closed.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static ErrorCode GetDsErrorFromSmb(ref SmbHeader header)
|
|
{
|
|
if (!header.IsError)
|
|
return ErrorCode.NoError;
|
|
|
|
// -XXX- This is a placeholder for some more sophisticated mapping
|
|
// -XXX- from SMB error codes to Singularity error codes.
|
|
|
|
NtStatus status = header.GetNtStatus();
|
|
switch (status) {
|
|
case NtStatus.ObjectNameNotFound: return ErrorCode.NotFound;
|
|
case NtStatus.ObjectPathNotFound: return ErrorCode.NotFound;
|
|
|
|
default:
|
|
return ErrorCode.Unknown;
|
|
}
|
|
|
|
}
|
|
|
|
void GetAttributes(SmbTransactor.Imp! transactor, DirectoryServiceContract.Exp! dir, string! path)
|
|
{
|
|
string! smbpath = ToAbsoluteSmbPath(path);
|
|
|
|
ushort informationLevel = 0x102;
|
|
|
|
ByteWriter parameters = new ByteWriter();
|
|
parameters.WriteUInt16Le(informationLevel);
|
|
parameters.WriteUInt32Le(0); // reserved
|
|
parameters.WriteStringUnicode(smbpath, true);
|
|
|
|
int requestLength = Util.Align4(sizeof(SmbTransaction2Request) + sizeof(ushort));
|
|
int parameterOffset = requestLength;
|
|
requestLength += parameters.Length;
|
|
|
|
byte[] in ExHeap requestEncoded = new[ExHeap] byte[requestLength];
|
|
ref SmbTransaction2Request request = ref requestEncoded[0];
|
|
|
|
request.Header.Prepare(SmbCommand.Transaction2, 15);
|
|
request.Transaction.TotalParameterCount = (ushort)parameters.Length;
|
|
request.Transaction.ParameterCount = (ushort)parameters.Length;
|
|
request.Transaction.ParameterOffset = (ushort)(parameterOffset - SmbHeader.FramingHeaderLength);
|
|
request.Transaction.MaxParameterCount = 1000;
|
|
request.Transaction.MaxDataCount = 1000;
|
|
request.Transaction.SetupCount = 1;
|
|
request.Transaction.Setup0 = (ushort)SmbTransaction2Code.QueryPathInformation;
|
|
ref ushort ByteCount = ref requestEncoded[sizeof(SmbTransaction2Request)];
|
|
ByteCount = 0;
|
|
|
|
parameters.CopyTo(requestEncoded, parameterOffset);
|
|
|
|
transactor.SendRequest(requestEncoded);
|
|
|
|
switch receive {
|
|
case transactor.Response(responseEncoded):
|
|
try {
|
|
ref SmbHeader header = ref responseEncoded[0];
|
|
if (header.IsError) {
|
|
DebugLine("GetAttributes: received error response: " + header.GetErrorText());
|
|
dir.SendNakGetAttributes(GetDsErrorFromSmb(ref header));
|
|
return;
|
|
}
|
|
|
|
if (responseEncoded.Length < sizeof(SmbTransaction2Response)) {
|
|
DebugLine("GetAttributes: Response is too small to be a valid response!");
|
|
dir.SendNakGetAttributes(ErrorCode.Unknown);
|
|
return;
|
|
}
|
|
|
|
ref SmbTransaction2Response response = ref responseEncoded[0];
|
|
if (response.Transaction.DataOffset < sizeof(SmbTransaction2Response)
|
|
|| (response.Transaction.DataOffset + response.Transaction.TotalDataCount + SmbHeader.FramingHeaderLength > responseEncoded.Length)
|
|
|| (response.Transaction.TotalDataCount < sizeof(SmbQueryFileStandardInfo))) {
|
|
DebugLine("Response has invalid fields (DataOffset and/or DataCount)");
|
|
dir.SendNakGetAttributes(ErrorCode.Unknown);
|
|
return;
|
|
}
|
|
|
|
ref SmbQueryFileStandardInfo info = ref responseEncoded[SmbHeader.FramingHeaderLength + response.Transaction.DataOffset];
|
|
|
|
if (TraceSwitches.ShowIncrediblyNoisyDetail) {
|
|
DebugLine("GetAttributes response:");
|
|
DebugLine(" AllocationSize: " + info.AllocationSize);
|
|
DebugLine(" EndOfFile: " + info.EndOfFile);
|
|
DebugLine(" NumberOfLinks: " + info.NumberOfLinks);
|
|
DebugLine(" DeletePending: " + (info.DeletePending != 0));
|
|
DebugLine(" Directory: " + (info.Directory != 0));
|
|
}
|
|
|
|
NodeType nodeType = (info.Directory != 0) ? NodeType.Directory : NodeType.File;
|
|
|
|
dir.SendAckGetAttributes(nodeType, info.EndOfFile);
|
|
} finally {
|
|
delete responseEncoded;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
void DebugLine(string msg)
|
|
{
|
|
SmbDebug.WriteLine(_debugprefix + msg);
|
|
}
|
|
|
|
void DebugLine(string format, params object[] args)
|
|
{
|
|
SmbDebug.WriteLine(_debugprefix + String.Format(format, args));
|
|
}
|
|
|
|
|
|
void ITracked.Expose() {}
|
|
void ITracked.UnExpose() {}
|
|
void ITracked.Acquire() {}
|
|
void ITracked.Release() {}
|
|
}
|
|
}
|