1 /* 2 * Copyright (C) 2020 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package okio 17 18 import java.nio.file.FileSystemException 19 import java.nio.file.Files 20 import java.nio.file.LinkOption 21 import java.nio.file.NoSuchFileException 22 import java.nio.file.Path as NioPath 23 import java.nio.file.StandardCopyOption.ATOMIC_MOVE 24 import java.nio.file.StandardCopyOption.REPLACE_EXISTING 25 import java.nio.file.attribute.BasicFileAttributes 26 import java.nio.file.attribute.FileTime 27 import okio.Path.Companion.toOkioPath 28 29 /** 30 * Extends [JvmSystemFileSystem] for platforms that support `java.nio.file` first introduced in 31 * Java 7 and Android 8.0 (API level 26). 32 */ 33 internal open class NioSystemFileSystem : JvmSystemFileSystem() { metadataOrNullnull34 override fun metadataOrNull(path: Path): FileMetadata? { 35 return metadataOrNull(path.toNioPath()) 36 } 37 metadataOrNullnull38 protected fun metadataOrNull(nioPath: NioPath): FileMetadata? { 39 val attributes = try { 40 Files.readAttributes( 41 nioPath, 42 BasicFileAttributes::class.java, 43 LinkOption.NOFOLLOW_LINKS, 44 ) 45 } catch (_: NoSuchFileException) { 46 return null 47 } catch (_: FileSystemException) { 48 return null 49 } 50 51 val symlinkTarget: NioPath? = if (attributes.isSymbolicLink) { 52 Files.readSymbolicLink(nioPath) 53 } else { 54 null 55 } 56 57 return FileMetadata( 58 isRegularFile = attributes.isRegularFile, 59 isDirectory = attributes.isDirectory, 60 symlinkTarget = symlinkTarget?.toOkioPath(), 61 size = attributes.size(), 62 createdAtMillis = attributes.creationTime()?.zeroToNull(), 63 lastModifiedAtMillis = attributes.lastModifiedTime()?.zeroToNull(), 64 lastAccessedAtMillis = attributes.lastAccessTime()?.zeroToNull(), 65 ) 66 } 67 68 /** 69 * Returns this time as an epoch millis. If this is 0L this returns null, because epoch time 0L is 70 * a special value that indicates the requested time was not available. 71 */ FileTimenull72 private fun FileTime.zeroToNull(): Long? { 73 return toMillis().takeIf { it != 0L } 74 } 75 atomicMovenull76 override fun atomicMove(source: Path, target: Path) { 77 try { 78 Files.move(source.toNioPath(), target.toNioPath(), ATOMIC_MOVE, REPLACE_EXISTING) 79 } catch (e: NoSuchFileException) { 80 throw FileNotFoundException(e.message) 81 } catch (e: UnsupportedOperationException) { 82 throw IOException("atomic move not supported") 83 } 84 } 85 createSymlinknull86 override fun createSymlink(source: Path, target: Path) { 87 Files.createSymbolicLink(source.toNioPath(), target.toNioPath()) 88 } 89 toStringnull90 override fun toString() = "NioSystemFileSystem" 91 } 92