Skip to content

removing directory on OneDrive fails with AccessDeniedException #404

@memo33

Description

@memo33

On Windows, removing a directory on OneDrive fails with a java.nio.file.AccessDeniedException, even if the folder was created by os-lib.

os.makeDir.all(os.home / "OneDrive" / "Documents" / "foo")  // or move an existing folder there
// later:
os.remove.all(os.home / "OneDrive" / "Documents" / "foo")  // throws java.nio.file.AccessDeniedException

If the folder contains files, they are deleted successfully, but the folder remains. The folder can still be renamed or moved, but not deleted.

(The error is presumably triggered by ERROR_ACCESS_DENIED (5) of RemoveDirectoryW (or GetFileAttributesEx), called by the native JDK implementation.)

OneDrive seems to set a read-only attribute for the folder. End users cannot clear it for folders (only files) using the folder properties in Windows Explorer. As an application author, it's difficult to know which paths might be affected.

Proposal:

Add an optional force: Boolean argument to os.remove and os.remove.all which clears the attributes ReadOnly, Hidden and System if needed. This is also what Powershell does (MIT license). It would also be in the spirit of the os.remove ScalaDocs which state:

Roughly equivalent to bash's rm -rf.

For example, I use this function as a workaround, the force=true case:

def removeAllForcibly(target: os.Path): Unit = {
  import java.nio.file.{Files, LinkOption}
  require(target.segmentCount != 0, s"Cannot remove a root directory: $target")
  if (Files.exists(target.wrapped, LinkOption.NOFOLLOW_LINKS)) {
    val paths =
      if (Files.isDirectory(target.wrapped, LinkOption.NOFOLLOW_LINKS))
        os.walk.stream(target, preOrder = false) ++ Seq(target)
      else
        geny.Generator[os.Path](target)
    for (p <- paths) {
      try {
        val _ = Files.deleteIfExists(p.wrapped)
      } catch { case e: java.io.IOException =>  // typically java.nio.file.AccessDeniedException when folders are read-only, e.g. on OneDrive on Windows
        try {
          // Attempt to clear problematic Windows flags
          val dos = Files.getFileAttributeView(p.wrapped, classOf[java.nio.file.attribute.DosFileAttributeView], LinkOption.NOFOLLOW_LINKS)
          if (dos != null) {
            dos.setReadOnly(false)
            try dos.setHidden(false) catch { case _: Throwable => /*ignore*/ }
            val _ = Files.deleteIfExists(p.wrapped)  // second attempt
          } else {
            throw e
          }
        } catch { case ignore: Throwable => throw e }
      }
    }
  }
}

As this problem is also present in the underlying java.nio, YMMV, so feel free to disregard. There's surprisingly little info about this problem on the web though, so perhaps the workaround is useful for someone.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions