Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client code examples #9

Closed
hagaygo opened this issue Mar 28, 2018 · 8 comments
Closed

Client code examples #9

hagaygo opened this issue Mar 28, 2018 · 8 comments

Comments

@hagaygo
Copy link

hagaygo commented Mar 28, 2018

Hi,

I am trying to use this library to connect to existing shares on windows machines in order to list/read/write files/directories.

The highlight seem to be creating a server side so i am not sure if the library do indeed support client implementations .

I was able to connect to a remote machine and list the available shares (available to my user) with the following code :

var smb = new SMB2Client();            
var b = smb.Connect(new System.Net.IPAddress(new byte[] { 192, 168, 120, 10 }), SMBLibrary.SMBTransportType.DirectTCPTransport);
var status = smb.Login(string.Empty, Username, password);
SMBLibrary.NTStatus actionStatus;
var shares = smb.ListShares(out actionStatus);

shares variable contains the available shares, from here i could not find a way to list the share content, create a file , read an existing file.
My lead is that i need to use TreeConnect method on smb client :

var tree = smb.TreeConnect("shared", out status);
object handle;
SMBLibrary.FileStatus fs;                                  
var x = tree.CreateFile(out handle, out fs, "zzz.txt", SMBLibrary.AccessMask.GENERIC_ALL, SMBLibrary.FileAttributes.Normal, SMBLibrary.ShareAccess.None, SMBLibrary.CreateDisposition.FILE_CREATE, 0, null);

But in this case i keep getting fs = FILE_DOES_NOT_EXIST and x = Access denied (i do have access).

Any chance for a working code examples ?

Thanks

@TalAloni
Copy link
Owner

Hi Hagay,
Basic client functions are supported and should work.
Other than checking for status at the end of each operation, your code seems correct,
First you connect to the server, then you connect to the share using TreeConnect, once you connect to the share, you use CreateFile to get a file handle (as you would with the Windows Native API).

I have tested your code against Windows 7 SP1 as server, and I'm successfully creating the file,
Please note that STATUS_ACCESS_DENIED is coming from your server, not the client, which simply report back the status returned from the server (you can use WireShark to verify this).
Are you sure that the user you use for login have both share level permissions and file level permissions that allow for creating the file? Which Windows version do you use for server?

@hagaygo
Copy link
Author

hagaygo commented Mar 28, 2018

Hi Tal,

Thanks for your quick reply.

The server is latest windows 10 pro.

I Don't have at the moment a windows 7 machine to test against.

Maybe i am missing out on the path convention ?

On windows explorer the file is available on :

\\192.168.120.10\shared\zzz.txxt

and of course my username is able to list the content of :

\\192.168.120.10\shared

"shared" is of course available on the returned shares variable from ListShares method.

Can you share a working code to list the content of a share path (similar to my example) ?

Thanks Again,

Hagay

@TalAloni
Copy link
Owner

TalAloni commented Mar 28, 2018

Here is a Working example that I tested against Windows 7 SP1 / 8.1 / 10:

SMB2Client client = new SMB2Client();
bool success = client.Connect(System.Net.IPAddress.Parse("192.168.1.121"), SMBTransportType.DirectTCPTransport);
if (success)
{
    NTStatus status = client.Login(String.Empty, username, password);
    if (status == NTStatus.STATUS_SUCCESS)
    {
        SMB2FileStore fileStore = client.TreeConnect("Shared", out status) as SMB2FileStore;
        if (fileStore != null)
        {
            object handle;
            FileStatus fileStatus;
            // Open existing file for reading
            status = fileStore.CreateFile(out handle, out fileStatus, "Notes.txt", AccessMask.GENERIC_READ, 0, ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
            if (status == NTStatus.STATUS_SUCCESS)
            {
                byte[] data;
                status = fileStore.ReadFile(out data, handle, 0, 4096);
                fileStore.CloseFile(handle);
                if (status != NTStatus.STATUS_SUCCESS)
                {
                    return;
                }
                // Write the data to another file (create it if it does not exist)
                status = fileStore.CreateFile(out handle, out fileStatus, "Copy of Notes.txt", AccessMask.GENERIC_READ | AccessMask.GENERIC_WRITE, 0, ShareAccess.None, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
                if (status == NTStatus.STATUS_SUCCESS)
                {
                    int numberOfBytesWritten;
                    status = fileStore.WriteFile(out numberOfBytesWritten, handle, 0, data);
                    fileStore.CloseFile(handle);
                }
            }
        }
    }
}

@hagaygo
Copy link
Author

hagaygo commented Mar 28, 2018

Thank you very much , i managed to get the file creation/copy to work , also list non "share root" folder :

var smb = new SMB2Client();
            var b = smb.Connect(new System.Net.IPAddress(new byte[] { 192, 168, 120, 10 }), SMBLibrary.SMBTransportType.DirectTCPTransport);
            var status = smb.Login(string.Empty, Username, password);
            SMBLibrary.NTStatus actionStatus;
            var shares = smb.ListShares(out actionStatus);
            var tree = smb.TreeConnect("shared", out status) as SMB2FileStore;
            object handle;
            SMBLibrary.FileStatus fs;

            // list \\192.168.20.10\shared - share root - returns STATUS_INVALID_PARAMETER
            actionStatus = tree.CreateFile(out handle, out fs, "", AccessMask.GENERIC_READ, 0, ShareAccess.Read | ShareAccess.Write | ShareAccess.Delete, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
            if (actionStatus == NTStatus.STATUS_SUCCESS)
            {
                actionStatus = tree.QueryDirectory(out List<QueryDirectoryFileInformation> files, handle, "*", FileInformationClass.FileDirectoryInformation);
            }
            
            // list \\192.168.20.10\shared\Backups contents - works
            actionStatus = tree.CreateFile(out handle, out fs, "Backups", AccessMask.GENERIC_READ, 0, ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
            if (actionStatus == NTStatus.STATUS_SUCCESS)
            {
                actionStatus = tree.QueryDirectory(out List<QueryDirectoryFileInformation> files, handle, "*", FileInformationClass.FileDirectoryInformation);
            }

            // file read  , write works
            actionStatus = tree.CreateFile(out handle, out fs, "zzz.txt", AccessMask.GENERIC_READ, 0, ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
            if (actionStatus == NTStatus.STATUS_SUCCESS)
            {
                byte[] data;
                status = tree.ReadFile(out data, handle, 0, 4096);
                tree.CloseFile(handle);
                if (status != NTStatus.STATUS_SUCCESS)
                {
                    return;
                }
                // Write the data to another file (create it if it does not exist)
                status = tree.CreateFile(out handle, out fs, "yyy.txt", AccessMask.GENERIC_READ | AccessMask.GENERIC_WRITE, 0, ShareAccess.None, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
                if (status == NTStatus.STATUS_SUCCESS)
                {
                    int numberOfBytesWritten;
                    status = tree.WriteFile(out numberOfBytesWritten, handle, 0, data);
                    tree.CloseFile(handle);
                }
            }

As for listing share root content, documentation for SMB is very hard to find , i guess "" is correct , becuase if i i use "." i get different status code.
So my guess a different parameters are required than a regular folder query.

@TalAloni
Copy link
Owner

TalAloni commented Mar 28, 2018

  1. Yes, as you can see in the SMB2 specifications: "A zero length file name indicates a request to open the root of the share".

  2. I noticed that there is a specific Windows 8.1 / Windows 10 issue that I now took care of, please update to the latest commit.

  3. I have updated the previous code sample (forgot to check if fileStore is not null)

To open the root directory of the share and list it's content:

status = fileStore.CreateFile(out handle, out fileStatus, "", AccessMask.SYNCHRONIZE | (AccessMask)DirectoryAccessMask.FILE_LIST_DIRECTORY, 0, ShareAccess.Read | ShareAccess.Write | ShareAccess.Delete, CreateDisposition.FILE_OPEN, CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT | CreateOptions.FILE_DIRECTORY_FILE, null);
if (status == NTStatus.STATUS_SUCCESS)
{
    List<QueryDirectoryFileInformation> result;
    status = fileStore.QueryDirectory(out result, handle, "*", FileInformationClass.FileNamesInformation);
}

BTW, You can use WireShark to see which parameters Windows clients use, and you can also check the Native API documentation (not to be confused with the Win32 API)

@hagaygo
Copy link
Author

hagaygo commented Mar 29, 2018

Running my code with your latest commit works fine now.

Thanks a lot for all your help and work.

@rdang93
Copy link

rdang93 commented Oct 19, 2022

Hi @TalAloni

When trying to use

`

                        status = fileStore.CreateFile(out object directoryHandle, out FileStatus fileStatus, filePath, AccessMask.GENERIC_READ, FileAttributes.Directory, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);

                    if (status == NTStatus.STATUS_SUCCESS)
                    {
                        List<QueryDirectoryFileInformation> fileList;
                        status = fileStore.QueryDirectory(out fileList, directoryHandle, "*", FileInformationClass.FileNamesInformation);
                        status = fileStore.CloseFile(directoryHandle);
                  }

`

Gives Not Implemented exception, I wanted to list file names so I can read each file in a loop

@TalAloni
Copy link
Owner

@rdang93 Please see the existing examples to retrieve list of file names in a directory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants