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

Opening file on a share fails with STATUS_OBJECT_NAME_NOT_FOUND #452

Open
mpohajac opened this issue Apr 12, 2019 · 9 comments
Open

Opening file on a share fails with STATUS_OBJECT_NAME_NOT_FOUND #452

mpohajac opened this issue Apr 12, 2019 · 9 comments

Comments

@mpohajac
Copy link

mpohajac commented Apr 12, 2019

Hi,

Please let us know how to mitigate this? would you say this is a bug or we are doing something wrong? openFile works ok with pretty much any name except this one with strange characters. See description below.

Thank you,
Marko

Symptoms

Opening file on a share fails with the following error:

Exception in thread "main" com.hierynomus.mssmb2.SMBApiException: STATUS_OBJECT_NAME_NOT_FOUND (0xc0000034): Create failed for \\host\share\directory\21 �a
                at com.hierynomus.smbj.share.Share.receive(Share.java:366)
                at com.hierynomus.smbj.share.Share.sendReceive(Share.java:346)
                at com.hierynomus.smbj.share.Share.createFile(Share.java:159)
                at com.hierynomus.smbj.share.DiskShare.createFileAndResolve(DiskShare.java:97)
                at com.hierynomus.smbj.share.DiskShare.resolveAndCreateFile(DiskShare.java:79)
                at com.hierynomus.smbj.share.DiskShare.open(DiskShare.java:66)
                at com.hierynomus.smbj.share.DiskShare.openFile(DiskShare.java:148)
                at com.demo.App.main(App.java:67)

Cause

Bytes representation of file name "21 �a" is as follows: 323120EFBFBE61
When listing files with SMBJ the bytes representation of this particular file is slightly changed to: 323120EFBFBD61
-> Instead of EFBFBE, EFBFBD was used, which is a bytes representation of replacement character.
Since the file name gets changed, file could not be opened and fails with the above exception.

How to verify

Prerequisites

Attached problematic file "21 �a" resides on the share in the folder named "directory".
directory.zip

Steps to reproduce

Use the following code to reproduce the problem:

String path = "directory";

String server = "";
String user = "";
String password = "";
String domain = "";
String shareName = "";

SMBClient client = new SMBClient();

try (Connection connection = client.connect(server)) {
    AuthenticationContext ac = new AuthenticationContext(user, password.toCharArray(), domain);
    Session session = connection.authenticate(ac);

    // Connect to Share
    try (DiskShare share = (DiskShare) session.connectShare(shareName)) {
        for (FileIdBothDirectoryInformation f : share.list(path, "*")) {
            if (f.getFileName().equals("..") || f.getFileName().equals(".")) {
                continue;
            }
            System.out.println("File : " + f.getFileName() + " (" + String.format("%X)", new BigInteger(1, f.getFileName().getBytes())));

            try (File file = share.openFile(path + "/" + f.getFileName(), of(READ_CONTROL), null,
                SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN,
                Collections.singleton(SMB2CreateOptions.FILE_OPEN_FOR_BACKUP_INTENT)
            )) {
               System.out.println("Successfully opened...");
            }
        }
    }
}
catch (IOException e) {
    e.printStackTrace();
}
@pepijnve
Copy link
Contributor

At the moment SMBJ takes the UTF-16 byte sequence representing the file name from the message and attempts to decode it to a Java String. Then when you try to open the file it reencodes the Java String to a UTF-16 byte sequence again. In situations where the original input data is invalid UTF-16 you're going to get the replacement character code point and as a consequence the reencoded version may not be identical to the original. In other words, the UTF-16 -> String transformation is not guaranteed to be reversible.

The only way I can come up with to work around this is to hold on to the original 'raw' filename so that you can send it back to the server verbatim.

@pepijnve
Copy link
Contributor

To actually answer your question, this is kind of a bug when dealing with filenames that are not valid UTF-16. There's no workaround for this. Proper fix would require changing the internal representation of paths in SMBJ from String to something lower level that holds on to the original byte representation of the filename.

@mpohajac
Copy link
Author

@pepijnve , I appreciate your prompt reply. It sounds like there is no roundtrip conversion from UTF-16 to String and this explains the behavior.

@hierynomus
Copy link
Owner

The round-trip works when all characters are valid UTF-16. In your case one of them isn't.

@mpohajac
Copy link
Author

@hierynomus yes, that's the case.

@hierynomus
Copy link
Owner

Just checked the MS-SMB2 spec:

Unless otherwise specified, all textual strings MUST be in Unicode version 5.0 format, as specified in
[UNICODE], using the 16-bit Unicode Transformation Format (UTF-16) form of the encoding. Textual
strings with separate fields identifying the length of the string MUST NOT be null-terminated unless
otherwise specified.

And for the SMB2 CREATE request:

Buffer (variable): A variable-length buffer that contains the Unicode file name ...

This means that the filename must be valid UTF-16 in order to work. It seems that the server is not adhering to the spec.

@pepijnve
Copy link
Contributor

pepijnve commented Apr 15, 2019

@mpohajac The sequence you provided, 323120EFBFBE61 is actually UTF-8 and decodes to the codepoint sequence 50 49 32 65534 97 or 2, 1, , , a. 65534 is actually a valid codepoint and should roundtrip just fine. The alternative EFBFBD is 65533 and is also valid and should also roundtrip identically.

Would it be possible to provide a hexdump of the UTF-16 byte sequence that actually gets sent from the server instead? Wireshark, tcpdump or netsh packet capture would be ideal, but a hexdump from the Java code works too of course.

@mpohajac
Copy link
Author

mpohajac commented Apr 15, 2019

@hierynomus , @pepijnve thanks for the info.

These are the bytes from the wireshark for the Filename:

addr
01c0   32 00 31 00 20 00 fe ff 61 00                     2.1. ...a.

Four valid UTF-16 chars '2','1','<space>','a' and the one feff before 'a' that appears to be a sort of bom, but strange to appear in between.

This is reproducible with:

  • the attached directory.zip, the filename inside has this character
  • demo code

@pepijnve
Copy link
Contributor

@hierynomus how would you prefer to handle this case? Best option IMO is to use something like the SmbPath class everywhere that can wrap a raw byte[]. In other words, defer conversion to Java String until it's requested. That would allow you to take the path object from a directory info and pass that on to open unprocessed.

I agree with you that what the server is sending is not 100% spec compliant, but that's just a fact of life we have to deal with. We see this kind of mess all the time, especially when data is written via NFS and then accessed via SMB.

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