yos/copy.go
package yos import ( "io" "io/ioutil" "math/bits" "os") const ( defaultDirectoryPermMode = os.FileMode(0755) defaultBufferSize = 256 * 1024 defaultNewFileFlag = os.O_RDWR | os.O_CREATE | os.O_TRUNC) // CopyFile copies a file to a target file or directory. Symbolic links are followed.//// If the target is an existing file, the target will be overwritten with the source file.//// If the target is an existing directory, the source file will be copied to the directory with the same file name.//// If the target doesn't exist but its parent directory does, the source file will be copied to the parent directory with the target name.//// If there is an error, it will be of type *os.PathError.func CopyFile(src, dest string) (err error) { if src, dest, err = refineOpPaths(opnCopy, src, dest, true); err == nil { err = bufferCopyFile(src, dest, defaultBufferSize) } return} // CopyDir copies a directory to a target directory recursively. Symbolic links inside the directories will be copied instead of being followed.//// If the target is an existing file, an error will be returned.//// If the target is an existing directory, the source directory will be copied to the directory with the same name.//// If the target doesn't exist but its parent directory does, the source directory will be copied to the parent directory with the target name.//// It stops and returns immediately if any error occurs, and the error will be of type *os.PathError.func CopyDir(src, dest string) (err error) { if src, dest, err = refineOpPaths(opnCopy, src, dest, true); err == nil { err = copyDir(src, dest) } return} // CopySymlink copies a symbolic link to a target file.//// CopySymlink only copies the content in the link and makes no attempt to read the file that the link pointed to.//// If there is an error, it will be of type *os.PathError.func CopySymlink(src, dest string) (err error) { if src, dest, err = refineOpPaths(opnCopy, src, dest, false); err == nil { err = copySymlink(src, dest) } return} // bufferCopyFile reads content from the source file and write to the destination file with a buffer.//nolint:gocyclo // buffer copy is a complicated thing indeed.Function `bufferCopyFile` has 57 lines of code (exceeds 50 allowed). Consider refactoring.
Function `bufferCopyFile` has a Cognitive Complexity of 26 (exceeds 20 allowed). Consider refactoring.func bufferCopyFile(src, dest string, bufferSize int64) (err error) { var ( srcFile, destFile *os.File srcInfo, destInfo os.FileInfo ) // check if source file exists and open for read if srcFile, srcInfo, err = openFileInfo(src); err == nil { defer srcFile.Close() } else { err = opError(opnCopy, src, err) return } // check if source and destination files are identical if destInfo, err = os.Stat(dest); err == nil { if !isFileFi(&destInfo) { err = opError(opnCopy, dest, errNotRegularFile) } else if os.SameFile(srcInfo, destInfo) { err = opError(opnCopy, dest, errSameFile) } } else if os.IsNotExist(err) { // it's okay if destination file doesn't exist err = nil } if err != nil { return } // use smaller buffer if source file is not big enough fileSize := srcInfo.Size() if bufferSize > fileSize { bufferSize = 1 << uint(bits.Len64(uint64(fileSize))) } if destFile, err = os.OpenFile(dest, defaultNewFileFlag, srcInfo.Mode()); err != nil { return } defer func() { if fe := destFile.Close(); fe != nil { err = fe } // remove destination if got any errors if err != nil { _ = os.Remove(dest) } }() var nr, nw int buf := make([]byte, bufferSize) for { if nr, err = srcFile.Read(buf); err != nil || nr == 0 { if err == io.EOF && nr > 0 { err = opError(opnCopy, src, io.ErrUnexpectedEOF) } break } if nw, err = destFile.Write(buf[:nr]); err != nil { break } else if nw != nr { err = opError(opnCopy, dest, io.ErrShortWrite) break } } if err == io.EOF { err = nil } // err = destFile.Sync() return} // copySymlink reads content from the source symbolic link and write to the destination symbolic link.func copySymlink(src, dest string) (err error) { var destInfo os.FileInfo if destInfo, err = os.Lstat(dest); err != nil { if os.IsNotExist(err) { err = nil } } else { if isDirFi(&destInfo) { // avoid overwriting directory err = opError(opnCopy, dest, errIsDirectory) } else { err = os.Remove(dest) } } if err != nil { return } var link string if link, err = os.Readlink(src); err != nil { err = opError(opnCopy, src, err) } else if err = os.Symlink(link, dest); err != nil { err = opError(opnCopy, dest, err) } return} // copyDir copies all entries of source directory to destination directory recursively.//nolint:gocyclo // copy directory refers itself with copy file and copy symlink, it's hard to reduce the complexity.Function `copyDir` has a Cognitive Complexity of 28 (exceeds 20 allowed). Consider refactoring.func copyDir(src, dest string) (err error) { var srcInfo, destInfo os.FileInfo // check if source exists and is a directory if srcInfo, err = os.Stat(src); err == nil { if !isDirFi(&srcInfo) { err = opError(opnCopy, src, errNotDirectory) } } if err != nil { return } // check if destination doesn't exist or is not a file or source itself if destInfo, err = os.Stat(dest); err == nil { if !isDirFi(&destInfo) { err = opError(opnCopy, dest, errNotDirectory) } else if os.SameFile(srcInfo, destInfo) { err = opError(opnCopy, dest, errSameFile) } } else if os.IsNotExist(err) { err = nil if err = os.MkdirAll(dest, defaultDirectoryPermMode); err == nil { originMode := srcInfo.Mode() defer os.Chmod(dest, originMode) } } if err != nil { return } // loop through entries in source directory var entries []os.FileInfo if entries, err = ioutil.ReadDir(src); err != nil { return } IterateEntry: for _, entry := range entries { srcPath, destPath := JoinPath(src, entry.Name()), JoinPath(dest, entry.Name()) switch entry.Mode() & os.ModeType { case os.ModeDir: if err = copyDir(srcPath, destPath); err != nil { break IterateEntry } case os.ModeSymlink: if err = copySymlink(srcPath, destPath); err != nil { break IterateEntry } case 0: if err = bufferCopyFile(srcPath, destPath, defaultBufferSize); err != nil { break IterateEntry } } } return}