Repository: googleapis/java-storage-nio (java)

Issue:
Java Storage NIO:  FileSystemProvider::checkAccess fails on '/' with StorageException
Using com.google.cloud:google-cloud-nio:0.124.20 the CloudStorageFileSystemProvider implementation of FileSystemProvider.checkAccess fails when asking about the root directory.

Take this code fragment and fill in a cloud storage bucket name you have access to:

```java
public class App {

    private static final Logger LOG = LoggerFactory.getLogger(App.class);

    public static void main(String[] args) {

        String bucketName = "xxxxxxxxxxxx";

        FileSystem fileSystem = FileSystems.getFileSystem(URI.create("gs://" + bucketName));

        Path path = fileSystem.getPath("/");

        FileSystemProvider provider = fileSystem.provider();
        try {
            provider.checkAccess(path, AccessMode.READ, AccessMode.WRITE);
        }
        catch (Exception e) {
            LOG.error("{}", e);
        }
        LOG.warn("{}", path);
    }
}
```

#### Stack trace
NOTE: I have hidden the real bucket name.

```
com.google.cloud.storage.StorageException: Invalid field selection bucket
	at com.google.cloud.storage.StorageException.translate(StorageException.java:163)
	at com.google.cloud.storage.spi.v1.HttpStorageRpc.translate(HttpStorageRpc.java:291)
	at com.google.cloud.storage.spi.v1.HttpStorageRpc.get(HttpStorageRpc.java:517)
	at com.google.cloud.storage.StorageImpl.lambda$get$6(StorageImpl.java:298)
	at com.google.api.gax.retrying.DirectRetryingExecutor.submit(DirectRetryingExecutor.java:103)
	at com.google.cloud.RetryHelper.run(RetryHelper.java:76)
	at com.google.cloud.RetryHelper.runWithRetries(RetryHelper.java:50)
	at com.google.cloud.storage.Retrying.run(Retrying.java:60)
	at com.google.cloud.storage.StorageImpl.run(StorageImpl.java:1476)
	at com.google.cloud.storage.StorageImpl.get(StorageImpl.java:296)
	at com.google.cloud.storage.contrib.nio.CloudStorageFileSystemProvider.checkAccess(CloudStorageFileSystemProvider.java:732)
	at nl.basjes.bugreports.App.main(App.java:31)
Caused by: com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
GET https://storage.googleapis.com/storage/v1/b/xxxxxxxxxxxx/o/?fields=bucket,name,id&projection=full
{
  "code" : 400,
  "errors" : [ {
    "domain" : "global",
    "location" : "fields",
    "locationType" : "parameter",
    "message" : "Invalid field selection bucket",
    "reason" : "invalidParameter"
  } ],
  "message" : "Invalid field selection bucket"
}
	at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146)
	at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:118)
	at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:37)
	at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:439)
	at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1111)
	at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:525)
	at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:466)
	at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:576)
	at com.google.cloud.storage.spi.v1.HttpStorageRpc.get(HttpStorageRpc.java:514)
	... 9 more
```

#### External references such as API reference guides

- https://docs.oracle.com/javase/8/docs/api/java/nio/file/spi/FileSystemProvider.html#checkAccess-java.nio.file.Path-java.nio.file.AccessMode...-

#### Any additional information below

I ran into this problem when "simply" trying to attach Apache Mina SFTP to a Google Cloud Bucket.
Note that this all does work as expected on 
- existing files/directories
- non-existing files/directories

Only on '/' it fails.


Pull request:
feat: FileSystemProvider::readAttributes faked posix support
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
- [x] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/java-storage-nio/issues/new/choose) before writing your code!  That way we can discuss the change, evaluate designs, and agree on the general idea
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [x] Appropriate docs were updated (if necessary)

This is the follow up patch after #1066 to add fake posix support in FileSystemProvider::readAttributes.
Important to note is that the values returned in this patch are all fixed and fake.

The main goal of this patch is to have minimal support for the posix attributes because some existing libraries fail if a filesystem does not support this at all. By adding faked support many of these libraries become usable in combination with cloud storage with minimal effort.

I expect that after this some may want to add a more accurate mapping of the attributes.

Fixes #1062 ☕️


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f71dccd..302eebd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
 # Changelog
 
+## [0.125.0](https://github.com/googleapis/java-storage-nio/compare/v0.124.21...v0.125.0) (2022-12-07)
+
+
+### Features
+
+* FileSystemProvider::readAttributes for basic and gcs views ([#1066](https://github.com/googleapis/java-storage-nio/issues/1066)) ([2f13792](https://github.com/googleapis/java-storage-nio/commit/2f13792040302899b6d7db00aa9c673dd470d460))
+
+
+### Bug Fixes
+
+* FileSystemProvider::checkAccess fails on '/' with StorageException ([d287cf5](https://github.com/googleapis/java-storage-nio/commit/d287cf5a9292221db251c987ff3a3ec736a815ac))
+
+
+### Dependencies
+
+* Update dependency com.google.cloud:google-cloud-shared-dependencies to v3.1.0 ([#1070](https://github.com/googleapis/java-storage-nio/issues/1070)) ([a6e55e7](https://github.com/googleapis/java-storage-nio/commit/a6e55e77d06eba990b81400e4973a9a4098175cb))
+* Update dependency com.google.cloud:google-cloud-storage to v2.16.0 ([#1071](https://github.com/googleapis/java-storage-nio/issues/1071)) ([882e06b](https://github.com/googleapis/java-storage-nio/commit/882e06b13630259bd5254e8b4f10b6ab9dddbf84))
+
 ## [0.124.21](https://github.com/googleapis/java-storage-nio/compare/v0.124.20...v0.124.21) (2022-11-18)
 
 
diff --git a/google-cloud-nio-examples/pom.xml b/google-cloud-nio-examples/pom.xml
index 07b6924..13c1afc 100644
--- a/google-cloud-nio-examples/pom.xml
+++ b/google-cloud-nio-examples/pom.xml
@@ -2,7 +2,7 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>google-cloud-nio-examples</artifactId>
-  <version>0.124.22-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
+  <version>0.125.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
   <packaging>jar</packaging>
   <name>Google Cloud NIO Examples</name>
   <url>https://github.com/googleapis/java-storage-nio</url>
@@ -13,7 +13,7 @@
   <parent>
     <groupId>com.google.cloud</groupId>
     <artifactId>google-cloud-nio-parent</artifactId>
-    <version>0.124.22-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
+    <version>0.125.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
   </parent>
   <dependencies>
     <dependency>
diff --git a/google-cloud-nio-retrofit/pom.xml b/google-cloud-nio-retrofit/pom.xml
index a1379e6..642b8bc 100644
--- a/google-cloud-nio-retrofit/pom.xml
+++ b/google-cloud-nio-retrofit/pom.xml
@@ -2,7 +2,7 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>google-cloud-nio-retrofit</artifactId>
-  <version>0.124.22-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
+  <version>0.125.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
   <packaging>jar</packaging>
   <name>Google Cloud NIO Retrofit Example</name>
   <url>https://github.com/googleapis/java-storage-nio</url>
@@ -12,7 +12,7 @@
   <parent>
     <groupId>com.google.cloud</groupId>
     <artifactId>google-cloud-nio-parent</artifactId>
-    <version>0.124.22-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
+    <version>0.125.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
   </parent>
   <properties>
     <site.installationModule>google-cloud-nio-retrofit</site.installationModule>
diff --git a/google-cloud-nio/pom.xml b/google-cloud-nio/pom.xml
index f609b51..1e16e56 100644
--- a/google-cloud-nio/pom.xml
+++ b/google-cloud-nio/pom.xml
@@ -2,7 +2,7 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>google-cloud-nio</artifactId>
-  <version>0.124.22-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
+  <version>0.125.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
   <packaging>jar</packaging>
   <name>Google Cloud NIO</name>
   <url>https://github.com/googleapis/java-storage-nio</url>
@@ -12,7 +12,7 @@
   <parent>
     <groupId>com.google.cloud</groupId>
     <artifactId>google-cloud-nio-parent</artifactId>
-    <version>0.124.22-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
+    <version>0.125.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
   </parent>
   <properties>
     <site.installationModule>google-cloud-nio</site.installationModule>
diff --git a/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java b/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java
index 4718318..8f923c5 100644
--- a/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java
+++ b/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java
@@ -64,9 +64,11 @@ public final class CloudStorageFileSystem extends FileSystem {
   public static final String URI_SCHEME = "gs";
   public static final String GCS_VIEW = "gcs";
   public static final String BASIC_VIEW = "basic";
+  public static final String POSIX_VIEW = "posix";
   public static final int BLOCK_SIZE_DEFAULT = 2 * 1024 * 1024;
   public static final FileTime FILE_TIME_UNKNOWN = FileTime.fromMillis(0);
-  public static final Set<String> SUPPORTED_VIEWS = ImmutableSet.of(BASIC_VIEW, GCS_VIEW);
+  public static final Set<String> SUPPORTED_VIEWS =
+      ImmutableSet.of(BASIC_VIEW, GCS_VIEW, POSIX_VIEW);
   private final CloudStorageFileSystemProvider provider;
   private final String bucket;
   private final CloudStorageConfiguration config;
diff --git a/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
index b2a55fc..bba5d87 100644
--- a/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
+++ b/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
@@ -19,6 +19,12 @@ package com.google.cloud.storage.contrib.nio;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.nio.file.attribute.PosixFilePermission.GROUP_EXECUTE;
+import static java.nio.file.attribute.PosixFilePermission.GROUP_READ;
+import static java.nio.file.attribute.PosixFilePermission.GROUP_WRITE;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;
 
 import com.google.api.gax.paging.Page;
 import com.google.cloud.storage.Acl;
@@ -62,10 +68,13 @@ import java.nio.file.attribute.BasicFileAttributeView;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.FileAttribute;
 import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.GroupPrincipal;
+import java.nio.file.attribute.UserPrincipal;
 import java.nio.file.spi.FileSystemProvider;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -728,6 +737,11 @@ public final class CloudStorageFileSystemProvider extends FileSystemProvider {
     // Loop will terminate via an exception if all retries are exhausted
     while (true) {
       try {
+        // Edge case is the root directory which triggers the storage.get to throw a
+        // StorageException.
+        if (cloudPath.normalize().equals(cloudPath.getRoot())) {
+          return;
+        }
         boolean nullId;
         if (isNullOrEmpty(userProject)) {
           nullId =
@@ -847,7 +861,7 @@ public final class CloudStorageFileSystemProvider extends FileSystemProvider {
     //             (eg. BasicFileAttributeView and PosixFileAttributeView), so rather than a partial
     //             implementation we rely on the other overload for now.
 
-    // Partial implementation for a few commonly used ones: basic, gcs
+    // Partial implementation for a few commonly used ones: basic, gcs, posix
     String[] split = attributes.split(":", 2);
     if (split.length != 2) {
       throw new UnsupportedOperationException();
@@ -863,6 +877,9 @@ public final class CloudStorageFileSystemProvider extends FileSystemProvider {
       case "gcs":
         fileAttributes = readAttributes(path, CloudStorageFileAttributes.class, options);
         break;
+      case "posix":
+        // There is no real support for posix.
+        // Some systems expect Posix support for everything so these attributes are faked.
       case "basic":
         fileAttributes = readAttributes(path, BasicFileAttributes.class, options);
         break;
@@ -900,6 +917,53 @@ public final class CloudStorageFileSystemProvider extends FileSystemProvider {
       results.put("size", fileAttributes.size());
     }
 
+    // There is no real support for posix.
+    // Some systems fail if there is no posix support at all.
+    // To let these systems use this FileSystem these attributes are faked.
+    if (view.equals("posix")) {
+      if (allAttributes || attributeNames.contains("owner")) {
+        results.put(
+            "owner",
+            new UserPrincipal() {
+              @Override
+              public String getName() {
+                return "fakeowner";
+              }
+
+              @Override
+              public String toString() {
+                return "fakeowner";
+              }
+            });
+      }
+      if (allAttributes || attributeNames.contains("group")) {
+        results.put(
+            "group",
+            new GroupPrincipal() {
+              @Override
+              public String getName() {
+                return "fakegroup";
+              }
+
+              @Override
+              public String toString() {
+                return "fakegroup";
+              }
+            });
+      }
+      if (allAttributes || attributeNames.contains("permissions")) {
+        if (fileAttributes.isRegularFile()) {
+          results.put("permissions", EnumSet.of(OWNER_READ, OWNER_WRITE, GROUP_READ, GROUP_WRITE));
+        } else {
+          // Directories, Symlinks and Other:
+          results.put(
+              "permissions",
+              EnumSet.of(
+                  OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE));
+        }
+      }
+    }
+
     // CloudStorageFileAttributes
     if (fileAttributes instanceof CloudStorageFileAttributes) {
       CloudStorageFileAttributes cloudStorageFileAttributes =
diff --git a/pom.xml b/pom.xml
index 1ea4869..c7603ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
   <groupId>com.google.cloud</groupId>
   <artifactId>google-cloud-nio-parent</artifactId>
   <packaging>pom</packaging>
-  <version>0.124.22-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
+  <version>0.125.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-nio:current} -->
   <name>Storage Parent</name>
   <url>https://github.com/googleapis/java-storage-nio</url>
   <description>
@@ -70,7 +70,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>google-cloud-shared-dependencies</artifactId>
-        <version>3.0.6</version>
+        <version>3.1.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
@@ -78,7 +78,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>google-cloud-storage</artifactId>
-        <version>2.15.1</version>
+        <version>2.16.0</version>
       </dependency>
       <dependency>
         <groupId>com.google.apis</groupId>
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index 742a6c7..97fa557 100644
--- a/samples/install-without-bom/pom.xml
+++ b/samples/install-without-bom/pom.xml
@@ -29,7 +29,7 @@
     <dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>google-cloud-nio</artifactId>
-      <version>0.124.21</version>
+      <version>0.125.0</version>
     </dependency>
     <!-- [END storage_nio_install_without_bom] -->
 
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 1d76a94..472a4db 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -28,7 +28,7 @@
     <dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>google-cloud-nio</artifactId>
-      <version>0.124.22-SNAPSHOT</version>
+      <version>0.125.1-SNAPSHOT</version>
     </dependency>
     <!-- {x-version-update-end} -->
 
diff --git a/versions.txt b/versions.txt
index 754e975..362f463 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
 # Format:
 # module:released-version:current-version
 
-google-cloud-nio:0.124.21:0.124.22-SNAPSHOT
\ No newline at end of file
+google-cloud-nio:0.125.0:0.125.1-SNAPSHOT
\ No newline at end of file


Test patch:
diff --git a/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
index 4bc004e..f44a932 100644
--- a/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
+++ b/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
@@ -59,6 +59,8 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.GroupPrincipal;
+import java.nio.file.attribute.UserPrincipal;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -906,7 +908,51 @@ public class CloudStorageFileSystemProviderTest {
     assertEquals(
         expectedSpecific,
         fileSystemProvider.readAttributes(
-            path1, "basic:lastModifiedTime,isSymbolicLink,isOther,etag,cacheControl"));
+            path1, "basic:lastModifiedTime,isSymbolicLink,isOther,etag,cacheControl,owner,group"));
+
+    // Add the attributes that are only known in posix view
+    // These are all fake values
+    expectedSpecific.put(
+        "owner",
+        new UserPrincipal() {
+          @Override
+          public String getName() {
+            return "fakeowner";
+          }
+
+          @Override
+          public String toString() {
+            return "fakeowner";
+          }
+        });
+
+    expectedSpecific.put(
+        "group",
+        new GroupPrincipal() {
+          @Override
+          public String getName() {
+            return "fakegroup";
+          }
+
+          @Override
+          public String toString() {
+            return "fakegroup";
+          }
+        });
+
+    // The equals between two anonymous classes (the UserPrincipal and GroupPrincipal) is always
+    // false
+    // so we compare the toString() instead.
+    assertEquals(
+        expectedSpecific.toString(),
+        fileSystemProvider
+            .readAttributes(
+                path1,
+                "posix:lastModifiedTime,isSymbolicLink,isOther,etag,cacheControl,owner,group")
+            .toString());
+
+    expectedSpecific.remove("owner");
+    expectedSpecific.remove("group");
 
     // Add the attributes that are only known in gcs view
     expectedSpecific.put("etag", Optional.of("TheEtag"));
@@ -915,7 +961,7 @@ public class CloudStorageFileSystemProviderTest {
     assertEquals(
         expectedSpecific,
         fileSystemProvider.readAttributes(
-            path1, "gcs:lastModifiedTime,isSymbolicLink,isOther,etag,cacheControl"));
+            path1, "gcs:lastModifiedTime,isSymbolicLink,isOther,etag,cacheControl,owner,group"));
   }
 
   private static CloudStorageConfiguration permitEmptyPathComponents(boolean value) {
diff --git a/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
index 57d698b..4033e78 100644
--- a/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
+++ b/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
@@ -179,7 +179,7 @@ public class CloudStorageFileSystemTest {
       assertThat(fs.getRootDirectories()).containsExactly(fs.getPath("/"));
       assertThat(fs.getFileStores()).isEmpty();
       assertThat(fs.getSeparator()).isEqualTo("/");
-      assertThat(fs.supportedFileAttributeViews()).containsExactly("basic", "gcs");
+      assertThat(fs.supportedFileAttributeViews()).containsExactly("basic", "gcs", "posix");
     }
   }
 
diff --git a/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java b/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java
index bd7fd79..67126b1 100644
--- a/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java
+++ b/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java
@@ -53,6 +53,7 @@ import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.SeekableByteChannel;
+import java.nio.file.AccessMode;
 import java.nio.file.FileAlreadyExistsException;
 import java.nio.file.FileSystem;
 import java.nio.file.FileVisitResult;
@@ -63,6 +64,7 @@ import java.nio.file.SimpleFileVisitor;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.spi.FileSystemProvider;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -1196,6 +1198,16 @@ public class ITGcsNio {
     }
   }
 
+  @Test
+  public void testCheckAccessRoot() throws Exception {
+    FileSystem fileSystem = getTestBucket();
+    Path path = fileSystem.getPath("/");
+    FileSystemProvider provider = fileSystem.provider();
+
+    // Against the real cloud storage this used to throw a StorageException.
+    provider.checkAccess(path, AccessMode.READ, AccessMode.WRITE);
+  }
+
   private CloudStorageFileSystem getTestBucket() throws IOException {
     // in typical usage we use the single-argument version of forBucket
     // and rely on the user being logged into their project with the