Giter VIP home page Giter VIP logo

smb-nio's Introduction

SMB NIO.2

Maven Central

This is a Java NIO.2 file system provider that can be used to access CIFS/SMB 1.0 and 2.0 file systems. CIFS is the standard file sharing protocol on the Microsoft Windows platform (e.g. to map a network drive).

This library uses jcifs-ng internally, which is an Open Source client library that implements the CIFS/SMB networking protocol in 100% Java.

Dependencies

This library requires the jcifs-ng library as a dependency.

How to use

The easiest way to use the library is to add it as a Maven dependency to your project. Just add the following entry to your pom.xml file.

<dependencies>
  <dependency>
    <groupId>ch.pontius.nio</groupId>
    <artifactId>smb-nio</artifactId>
    <version>0.13.0</version>
  </dependency>
</dependencies>

Once you have added the dependency, you should be able to create SMBPath objects by using the Paths#get(URI) method of the Java NIO.2 library. The URI must be constructed using the 'smb://' scheme. If you want to use credentials, there are several ways to do so. You can either do so directly:

final Path dir = Paths.get(new URI("smb://username:password@host/share/dir1/dir2/"));

Or you can manually create an SMB file system using the following snippet. In this case, the default credentials will always be used.

final Map<String, Object> env = new HashMap<>(); 
env.put("jcifs.smb.client.username", "<username>");
env.put("jcifs.smb.client.password", "<password>"); 
env.put("jcifs.smb.client.domain", "<domain>");

SmbFileSystem fileSystem = SMBFileSystemProvider().newFileSystem(URI.create("smb://host), env);
Path dir = fileSystem.getPath("/dir1", "dir2/");

Last but not least it is also possible to set the aforementioned properties as global system properties when starting the JVM. A complete list of properties supported by jcifs-ng can be found on their GitHub repository.

Important: Please note that paths to directories MUST end with a trailing slash due to requirements of jcifs-ng.

Issues

Please report issues using the GitHub issue tracker.

smb-nio's People

Contributors

axelrindle avatar iils-hwellmann avatar jonnyboman avatar joschiwald avatar mattdrees avatar ppanopticon avatar stefanhirche avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

smb-nio's Issues

Trailing slashes are missing for subdirectories in SMBDirectoryStream

When SMBDirectoryStream is used to iterate over directory with subdirectories, the trailing slashes are not added for these subdirectories. This is violating jcifs requirement that all directories must have trailing slash and some operations (e.g. attempt to iterate over these subdirectories) will throw "jcifs.smb.SmbException: directory must end with '/'".

As a result, it is not possible to use Files.walkFileTree(...) method to walk over any smb-nio filesystem with maxDepth bigger than 1.

java.lang.ArrayIndexOutOfBoundsException: length=0; index=-1

I've got a SMBFileSystem instance setup - which is working fine in terms of connecting - and want to get a Path pointing to the root directory with the following call:

val myPath = fileSystem!!.getPath("/")

This however results in the following exception:

java.lang.ArrayIndexOutOfBoundsException: length=0; index=-1
        at ch.pontius.nio.smb.SMBFileSystem.getPath(SMBFileSystem.java:178)

When looking into the source code, I see that the method getPath does not allow for an empty more array:

final String path = SMBPathUtil.mergePath(components, 0, components.length, first.startsWith("/"), more[more.length-1].endsWith("/"));

                                                                                                   // here /\
                                                                                                   //      ||

Is this intended behavior or a bug?

dependency conflict with spring-integration-smb ?

Seems after I added this library when I try to call my existing smb code to check whether a file/folder exists:

SmbFile userDir = new SmbFile(folder.getAsString(), getAuthToken())
if (!userDir.exists()) {

			}

the call blows up with

org.springframework.messaging.MessageHandlingException: Unexpected handler method invocation error; nested exception is java.lang.IllegalAccessError: failed to access class org.bouncycastle.asn1.DEROutputStream from class jcifs.spnego.NegTokenInit (org.bouncycastle.asn1.DEROutputStream and jcifs.spnego.NegTokenInit are in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader @4678c730)

Caused by: java.lang.IllegalAccessError: failed to access class org.bouncycastle.asn1.DEROutputStream from class jcifs.spnego.NegTokenInit (org.bouncycastle.asn1.DEROutputStream and jcifs.spnego.NegTokenInit are in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader @4678c730)
at jcifs.spnego.NegTokenInit.toByteArray(NegTokenInit.java:158) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SpnegoContext.initSecContext(SpnegoContext.java:207) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbSessionImpl.createToken(SmbSessionImpl.java:656) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbSessionImpl.sessionSetupSMB2(SmbSessionImpl.java:538) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbSessionImpl.sessionSetup(SmbSessionImpl.java:483) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbSessionImpl.send(SmbSessionImpl.java:369) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbSessionImpl.send(SmbSessionImpl.java:347) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbTreeImpl.treeConnect(SmbTreeImpl.java:604) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbTreeConnection.connectTree(SmbTreeConnection.java:610) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbTreeConnection.connectHost(SmbTreeConnection.java:564) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbTreeConnection.connectHost(SmbTreeConnection.java:485) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbTreeConnection.connect(SmbTreeConnection.java:461) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbTreeConnection.connectWrapException(SmbTreeConnection.java:422) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbFile.ensureTreeConnected(SmbFile.java:551) ~[jcifs-2.1.7.jar!/:na]
at jcifs.smb.SmbFile.exists(SmbFile.java:838) ~[jcifs-2.1.7.jar!/:na]

Is this library compatible with other smb providers, namely spring-integration-smb?

Credentials in SMB URL not working

I tried your library and did not manage to get credentials in url working.

Not working:

try (final DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Path.of(URI.create("smb://user:[email protected]/test/")))) {
	directoryStream.forEach(System.out::println);
}

Working:

System.setProperty("jcifs.smb.client.username", "user");
System.setProperty("jcifs.smb.client.password", "password");
try (final DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Path.of(URI.create("smb://192.168.0.6/test/")))) {
	directoryStream.forEach(System.out::println);
}

From the README I suppose the first one is supposed to work also?

And version: smb-nio:0.12.0 & jcifs-ng:2.1.9 (also tried jcifs-ng:2.1.8 and smb-nio:0.11.0)

trailing slashes should not be required for directories/folders

SMBPath.resolve() requires that the current path is a folder, which is fine, but it checks this by looking at whether the URI (or path) ends with a trailing slash. This seems incorrect to me; trailing slashes are generally optional, and java's default filesystem does't require trailing slashes to denote directories/folders.

URI encoding problems with passwords

Passwords with certain URI-reserved characters, like / and #, present challenges.

If you use / in the env map sent to SmbFileSystemProvider.newFilesystem(URI, Map), it is not percent-encoded before being concatenated into the URL that is fed into SmbFile, and you can get odd errors as a result.

For example, using username "foo" and password "bar/", you get

Exception in thread "main" java.net.MalformedURLException: For input string: "bar"
	at java.net.URL.<init>(URL.java:627)
	at jcifs.smb.SmbFile.<init>(SmbFile.java:485)
	at ch.pontius.nio.smb.SMBPath.getSmbFile(SMBPath.java:488)
	at ch.pontius.nio.smb.SMBFileSystemProvider.checkAccess(SMBFileSystemProvider.java:324)
	at org.cru.smb.Test.main(Test.java:29)
Caused by: java.lang.NumberFormatException: For input string: "bar"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.parseInt(Integer.java:615)
	at java.net.URLStreamHandler.parseURL(URLStreamHandler.java:222)
	at jcifs.smb.Handler.parseURL(Handler.java:51)
	at java.net.URL.<init>(URL.java:622)
	... 4 more

To work around this, I would have to percent-encode the password in the env map.

A related problem is when I put the password into the URI (and not the env map) that I pass in to SmbFileSystemProvider.newFilesystem(URI, Map). In this case, I have to percent-encode the password so that I can construct a valid URI.

But SMBFileSystemProvider decodes this password, and does not re-encode it, when it sends it on to SmbFile.

So a uri like smb://foo:bar%[email protected]/some_share results in a stack trace like this:

Exception in thread "main" java.net.MalformedURLException: For input string: "bar"
	at java.net.URL.<init>(URL.java:627)
	at jcifs.smb.SmbFile.<init>(SmbFile.java:485)
	at ch.pontius.nio.smb.SMBPath.getSmbFile(SMBPath.java:488)
	at ch.pontius.nio.smb.SMBFileSystemProvider.checkAccess(SMBFileSystemProvider.java:324)
	at org.cru.smb.Test.main(Test.java:42)
Caused by: java.lang.NumberFormatException: For input string: "bar"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.parseInt(Integer.java:615)
	at java.net.URLStreamHandler.parseURL(URLStreamHandler.java:222)
	at jcifs.smb.Handler.parseURL(Handler.java:51)
	at java.net.URL.<init>(URL.java:622)
	... 4 more

To work around this, I would have to double percent-encode the password in the URI.

Read-only files cannot be read due to write operation

I'm testing this against a read-only SMB share, and trying to read a file (via Files.copy()) results in a permission error:

jcifs.smb.SmbAuthException: Access is denied.

	at jcifs.smb.SmbTransport.checkStatus(SmbTransport.java:546)
	at jcifs.smb.SmbTransport.send(SmbTransport.java:640)
	at jcifs.smb.SmbSession.send(SmbSession.java:238)
	at jcifs.smb.SmbTree.send(SmbTree.java:119)
	at jcifs.smb.SmbFile.send(SmbFile.java:775)
	at jcifs.smb.SmbFile.setPathInformation(SmbFile.java:2578)
	at jcifs.smb.SmbFile.setAttributes(SmbFile.java:2646)
	at jcifs.smb.SmbFile.setReadOnly(SmbFile.java:2656)
	at ch.pontius.nio.smb.SeekableSMBByteChannel.<init>(SeekableSMBByteChannel.java:49)
	at ch.pontius.nio.smb.SMBFileSystemProvider.newByteChannel(SMBFileSystemProvider.java:196)
	at java.nio.file.Files.newByteChannel(Files.java:361)
	at java.nio.file.Files.newByteChannel(Files.java:407)
	at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384)
	at java.nio.file.Files.newInputStream(Files.java:152)
	at java.nio.file.Files.copy(Files.java:3068)

It seems the problem is that SeekableSMBByteChannel is calling SmbFile.setReadOnly(), which is telling the server to make the file read-only. That's not really what a read operation should be doing.

Use NtlmPasswordAuthenticator with SMBFileSystemProvider

JCIFS provides the concept of subcontexts and allows retrieving a resource from a subcontext:
NtlmPasswordAuthenticator auth = new NtlmPasswordAuthenticator("mydomain", "myuser", "mypass"); <br/> CIFSContext cifsContext = SingletonContext.getInstance().withCredentials(auth); <br/> SmbResource smbResource = cifsContext.get(uriStr); <br/> smbResource.children().forEachRemaining(p -> { System.out.println(p); });<br/>

Is there a possibility to use NtlmPasswordAuthenticator with your library?

Cheers,
Kai

Directory must end with '/' (smb-nio-ng)

https://github.com/jfrommann/smb-nio-ng. I am writing here because the tracker is not available in another repository.

Exception:

Exception in thread "main" jcifs.smb.SmbException: smb://username:pasword@server//dir1/dir2 directory must end with '/'
        at jcifs.smb.DirFileEntryEnumIterator1.open(DirFileEntryEnumIterator1.java:57)
	at jcifs.smb.DirFileEntryEnumIteratorBase.<init>(DirFileEntryEnumIteratorBase.java:67)
	at jcifs.smb.DirFileEntryEnumIterator1.<init>(DirFileEntryEnumIterator1.java:46)
	at jcifs.smb.SmbEnumerationUtil.doEnum(SmbEnumerationUtil.java:225)
	at jcifs.smb.SmbEnumerationUtil.listFiles(SmbEnumerationUtil.java:279)
	at jcifs.smb.SmbFile.listFiles(SmbFile.java:1199)
	at com.github.jfrommann.nio.smb.SmbDirectoryStream.<init>(SmbDirectoryStream.java:43)
	at com.github.jfrommann.nio.smb.SmbFileSystemProvider.newDirectoryStream(SmbFileSystemProvider.java:382)
	at java.base/java.nio.file.Files.newDirectoryStream(Files.java:476)
	at java.base/java.nio.file.Files.newDirectoryStream(Files.java:533)
	at kotlin.io.path.PathsKt__PathUtilsKt.listDirectoryEntries(PathUtils.kt:342)
	at kotlin.io.path.PathsKt__PathUtilsKt.listDirectoryEntries$default(PathUtils.kt:341)
        // my call code

Code:

    val fileSystem: SmbFileSystem = SmbFileSystemProvider
        .getDefault()
        .newFileSystem(URI.create("smb://server/"), environment)

    fileSystem.getPath("dir1", "dir2") // .normalize() analogy effetc, work: getPath("dir1", "dir2/") 
        .listDirectoryEntries() // Files.newDirectoryStream(path, "*").use { sream -> stream.toList() } // use - AutoCLoseble Stream
        .forEach(::println)

NullPointerException when creating new `FileSystem` from URI without username-component

First of all, thanks for making this library publicly available!

I'm trying it out in our project, where I need to talk to a hardware device.
In the new firmware, SMBv1 was (finally ๐ŸŽ‰ ) disabled in favour of SMBv2/3.
Unfortunately, now we need to update our driver ๐Ÿ™‚

Based on the ReadMe example, I tried to do a very simple listing of directory contents on the hardware device, as follows:

	@Test
	public void testPontiusSmbNio() throws Exception {
		SMBFileSystemProvider provider = new SMBFileSystemProvider();

		Map<String, String> env = Map.of(
				"jcifs.smb.client.username", Device.SMB_USER,
				"jcifs.smb.client.password", Device.SMB_PASS,
				"jcifs.smb.client.domain", ""
		);

		URI uri = new URI(String.format("smb://%s/%s/", Device.getIp(), SMB_ROOT));
		System.out.println("uri = " + uri);
		System.out.println("uri.getAuthority() = " + uri.getAuthority());

		try (SMBFileSystem fs = provider.newFileSystem(uri, env)) {
			labelDir = fs.getPath(Device.LABEL_DIR);
			try (Stream<Path> dir = Files.list(labelDir)){
				dir.forEach(label -> System.out.println("label = " + label));
			}
		}
	}

Output is as follows:

uri = smb:///192.168.100.230/the-share/
uri.getAuthority() = null

java.lang.NullPointerException: Cannot invoke "String.contains(java.lang.CharSequence)" because the return value of "java.net.URI.getAuthority()" is null

java.lang.NullPointerException: Cannot invoke "String.contains(java.lang.CharSequence)" because the return value of "java.net.URI.getAuthority()" is null

	at ch.pontius.nio.smb.SMBFileSystemProvider.constructAuthority(SMBFileSystemProvider.java:509)
	at ch.pontius.nio.smb.SMBFileSystemProvider.newFileSystem(SMBFileSystemProvider.java:92)
	at de.pharmacontrol.playground.smb.SmbNioTest.testPontiusSmbNio(SmbNioTest.java:33)
	<snip JUnit internals>

Indeed, inside SMBFileSystemProvider, line 508, I see:

        /* Check if URI encodes credentials. Credentials are used in the following order: */
        if (uri.getAuthority().contains(SMBFileSystem.CREDENTIALS_SEPARATOR)) {
            authority = uri.getAuthority();

which obviously doesn't like the null.

Am I using it wrong, or should there be a null-check there?
Based on the readme-example, which puts the authentication info into the env map, I wouldn't expect the URI-authority section to be required.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.