AgNO3 / jcifs-ng

A cleaned-up and improved version of the jCIFS library
GNU Lesser General Public License v2.1
313 stars 104 forks source link

Are symbolic links supported? #245

Open jmagesh opened 4 years ago

jmagesh commented 4 years ago

If a file in a share is a symbolic link to another file on the same file system (e.g. created using the Windows mklink command) will the link be resolved when we try to read the SmbFile?

When we tried to do so, we get this error code: 0x8000002D : STATUS_STOPPED_ON_SYMLINK : The create operation stopped after reaching a symbolic link.

Stack trace: [2020-06-16 02:30:25,850] DEBUG [-1462162349@qtp--1772318280-0] jcifs.smb.SmbTransportImpl Error code: 0x8000002D for Smb2CreateRequest [2020-06-16 02:30:25,851] DEBUG [-1462162349@qtp--1772318280-0] jcifs.smb.SmbTreeConnection Not retrying jcifs.smb.SmbException: 0x8000002D at jcifs.smb.SmbTransportImpl.checkStatus2(SmbTransportImpl.java:1462) ~[jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbTransportImpl.checkStatus(SmbTransportImpl.java:1573) ~[jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbTransportImpl.sendrecv(SmbTransportImpl.java:1028) ~[jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbTransportImpl.send(SmbTransportImpl.java:1544) ~[jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbSessionImpl.send(SmbSessionImpl.java:409) ~[jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbTreeImpl.send(SmbTreeImpl.java:472) ~[jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbTreeConnection.send0(SmbTreeConnection.java:410) ~[jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbTreeConnection.send(SmbTreeConnection.java:324) [jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbTreeConnection.send(SmbTreeConnection.java:304) [jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbTreeHandleImpl.send(SmbTreeHandleImpl.java:130) [jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbTreeHandleImpl.send(SmbTreeHandleImpl.java:117) [jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbFile.withOpen(SmbFile.java:1792) [jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbFile.withOpen(SmbFile.java:1761) [jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbFile.withOpen(SmbFile.java:1755) [jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbFile.queryPath(SmbFile.java:807) [jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbFile.exists(SmbFile.java:890) [jcifs-ng-2.1.4.jar:?] at jcifs.smb.SmbFile.lastModified(SmbFile.java:1152) [jcifs-ng-2.1.4.jar:?]

Is this expected behavior? If so, is there any way to resolve symbolic links? Our understanding is that there is some support for DFS reparse points in jcifs-ng-2.1.4.

mbechler commented 4 years ago

Unfortunately, at this point, there is no support for symlinks. This would definitely be a good addition for the next major version.

jmagesh commented 4 years ago

Thanks for the clarification @mbechler. Hope this is supported in the next major version.

GregBragg commented 2 years ago

Just bringing this to the top again... Just wondering when can we expect this functionality to be supported?

mbechler commented 2 years ago

Unfortunately I haven't found much time to work on jcifs recently. And I have to say I somewhat fear that without some redesign this is either going to be inefficient or going to be equally messy and potentially buggy as DFS unfortunately currently is.

GregBragg commented 2 years ago

Ok well I'm attempting to get this functionality in, just can't figure out where the response would be for the STATUS_STOPPED_ON_SYMLINK which according to the MS docs (first link), and I quote includes the symbolic link data, I just don't know where to get it from in the code.

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb/ecd51ae2-478c-455b-8669-254b74208d3b

I'm trying to figure out where the symbolic link error response would be returned in the code and where I can grab it based on this document so it can be parsed and processed:

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/a8da655c-8b0b-415a-b726-16dc33fa5827

mbechler commented 2 years ago

This should be part of the error response and parsed into the errorData field in

https://github.com/AgNO3/jcifs-ng/blob/09bda8630949488fc722ec4aa457e2915f310f1e/src/main/java/jcifs/internal/smb2/ServerMessageBlock2.java#L618

still wrapped in the Error Context structure:

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/9ff8837c-c4f7-452b-9272-8818a119dae9

GregBragg commented 2 years ago

Ok thanks for the tip, I'll be looking into this soon. I looked at this class but must have missed it...

GregBragg commented 2 years ago

@mbechler can you give me some help on your SMBUtil.readInt2, SMBUtil.readInt4, SMBUtil.readInt8 methods for reading a byte array? I'm getting some unintended results when using these methods...

I'm trying to figure out how to read the symlink error response message based on this document: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/f15ae37d-a787-4bf2-9940-025a8c9c0022

Thanks!

mbechler commented 2 years ago

These should read unsigned short, signed int, signed long (all little endian) from the specified absolute buffer position, so you'll need to keep track of position for yourself. See e.g.

https://github.com/AgNO3/jcifs-ng/blob/3db3e62157ce6eab2ab3719f63a53f25fd1231cb/src/main/java/jcifs/internal/dtyp/SecurityDescriptor.java#L106

for an example.

Potentially you might be running into a alignment/padding issue: "Each SMB2 ERROR Context MUST start at an 8-byte aligned boundary relative to the start of the SMB2 ERROR Response." However, the start of errorData should be 8 byte aligned as far as I can tell.

GregBragg commented 2 years ago

Ok awesome... that is what I assumed by looking at this code, however I wasn't getting the correct values from the errorData array.

Looking at the code for readErrorResponse in ServerMessageBlock2 it would appear I am missing the first 8 bytes of the message for the errorData value returned in the getErrorData method, which means I am reading from the wrong part of the array as I am missing the first 8 bytes.

I'll try another fix... I might need to update readErrorResponse or create another implementation of the same logic to capture all the bytes in the error response. Thanks!

mbechler commented 2 years ago

Hm, at least on a quick glance the offset for errorData looks correct to me and therefore should point to the ErrorDataLength field of the first context structure.

GregBragg commented 2 years ago

Hm, at least on a quick glance the offset for errorData looks correct to me and therefore should point to the ErrorDataLength field of the first context structure.

Yeah it's correct from what I can tell from the specs... I think it has to do with the 8-byte aligned boundary, however I'm still investigating.

GregBragg commented 2 years ago

Ok got it figured out... now I just need to figure out how to call the framework with the "real' path to the file or directory after getting it from the symlink error response PathBuffer variable.

@mbechler Any tips on reading/resolving the 'real' file or directory rather than the symlink?

mbechler commented 2 years ago

You could probably do something like throw an exception with the link information (along the lines of DfsReferreal) and adjust the path somewhere within the retry logic in SmbTreeConnection.send().

But as I mentioned originally, this is were I believe things will likely to get quite messy. Are you thinking about caching the link information?

GregBragg commented 2 years ago

Ok I’ll take a look at that example, thanks!

I have a new class written that will cache all the information needed after decoding the symlink error, just need to figure out how to do the redirect after the exception is caught.

I’ll eventually submit a PR for review once I get it figured out, however you can see my current progress in my fork.

GregBragg commented 2 years ago

@mbechler Just a status update on my progress... looks like I have a viable solution now, however I need to enhance my JUnit tests to cover all the bases.

Just need to understand how you configure your tests with parameterized values to a real SMB server, right now I have hardcoded values in my class.

mbechler commented 2 years ago

There is a framework to run multiple congfiguration against multiple server versions and/or configuration.

With the system properties test.config.dir/test.config.file you can provide property files describing test targets.

One of my test target configuration for example:

test.mutations.exclude=noNTStatus,noSigning,forceSigning,legacyAuth,noUnicode,forceUnicode,noNTStatus,noNTSmbs,noUnicode-cp850,noUnicode-windows-1252,noLargeReadWrite,smb1

test.server=w2k19-single-dc.w2k19single.springfield
test.user.name=test1
test.user.domain=W2K19SINGLE.SPRINGFIELD
test.user.sdomain=W2K19SINGLE
test.user.password=***********
test.user.sid=S-1-5-21-3857529893-3941372170-2395057007-1103
test.share.guest=test-guest
test.share=test

test.domain=W2K19SINGLE.SPRINGFIELD
test.domain.netbios=W2K19SINGLE
test.domain.dc=w2k19-single-dc.w2k19single.springfield
test.domain.sid=S-1-5-21-3857529893-3941372170-2395057007

Not all of that is probably relevant for you (e.g. SIDs, test.mutations.exclude in this case disables all SMB1 testing as the server does not support it), but that should get you started.

Then you can create a Test class extending BaseCIFSTest and add that to the AllTests suite, or just add it to a suitable existing class, and access the properties through the base class (getRequiredProperty). There are some utilities, e.g. to get the configured default test share root, see BaseCIFSTest.

As your tests likely require manual setup, you'll probably need to add some additional properties, e.g. a location involving a symlink and potentially the expected target. This also allows skipping the test if the setup has not been performed or the target does not support symlinks.

GregBragg commented 2 years ago

@mbechler PR #309 has been opened, please review at your convenience.

I ran AllTests against my file share server and all the file operations passed, only 12 tests failed in the whole suite due to permission errors or probably because I did not implement all the property settings that you mentioned, however I believe I have covered all the major tests.