123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- /*
- Copyright The containerd Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package fs
- import (
- "fmt"
- "io"
- "os"
- "path/filepath"
- "github.com/sirupsen/logrus"
- )
- // XAttrErrorHandler transform a non-nil xattr error.
- // Return nil to ignore an error.
- // xattrKey can be empty for listxattr operation.
- type XAttrErrorHandler func(dst, src, xattrKey string, err error) error
- type copyDirOpts struct {
- xeh XAttrErrorHandler
- // xex contains a set of xattrs to exclude when copying
- xex map[string]struct{}
- }
- type CopyDirOpt func(*copyDirOpts) error
- // WithXAttrErrorHandler allows specifying XAttrErrorHandler
- // If nil XAttrErrorHandler is specified (default), CopyDir stops
- // on a non-nil xattr error.
- func WithXAttrErrorHandler(xeh XAttrErrorHandler) CopyDirOpt {
- return func(o *copyDirOpts) error {
- o.xeh = xeh
- return nil
- }
- }
- // WithAllowXAttrErrors allows ignoring xattr errors.
- func WithAllowXAttrErrors() CopyDirOpt {
- xeh := func(dst, src, xattrKey string, err error) error {
- return nil
- }
- return WithXAttrErrorHandler(xeh)
- }
- // WithXAttrExclude allows for exclusion of specified xattr during CopyDir operation.
- func WithXAttrExclude(keys ...string) CopyDirOpt {
- return func(o *copyDirOpts) error {
- if o.xex == nil {
- o.xex = make(map[string]struct{}, len(keys))
- }
- for _, key := range keys {
- o.xex[key] = struct{}{}
- }
- return nil
- }
- }
- // CopyDir copies the directory from src to dst.
- // Most efficient copy of files is attempted.
- func CopyDir(dst, src string, opts ...CopyDirOpt) error {
- var o copyDirOpts
- for _, opt := range opts {
- if err := opt(&o); err != nil {
- return err
- }
- }
- inodes := map[uint64]string{}
- return copyDirectory(dst, src, inodes, &o)
- }
- func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) error {
- stat, err := os.Stat(src)
- if err != nil {
- return fmt.Errorf("failed to stat %s: %w", src, err)
- }
- if !stat.IsDir() {
- return fmt.Errorf("source %s is not directory", src)
- }
- if st, err := os.Stat(dst); err != nil {
- if err := os.Mkdir(dst, stat.Mode()); err != nil {
- return fmt.Errorf("failed to mkdir %s: %w", dst, err)
- }
- } else if !st.IsDir() {
- return fmt.Errorf("cannot copy to non-directory: %s", dst)
- } else {
- if err := os.Chmod(dst, stat.Mode()); err != nil {
- return fmt.Errorf("failed to chmod on %s: %w", dst, err)
- }
- }
- entries, err := os.ReadDir(src)
- if err != nil {
- return fmt.Errorf("failed to read %s: %w", src, err)
- }
- if err := copyFileInfo(stat, src, dst); err != nil {
- return fmt.Errorf("failed to copy file info for %s: %w", dst, err)
- }
- if err := copyXAttrs(dst, src, o.xex, o.xeh); err != nil {
- return fmt.Errorf("failed to copy xattrs: %w", err)
- }
- for _, entry := range entries {
- source := filepath.Join(src, entry.Name())
- target := filepath.Join(dst, entry.Name())
- fileInfo, err := entry.Info()
- if err != nil {
- return fmt.Errorf("failed to get file info for %s: %w", entry.Name(), err)
- }
- switch {
- case entry.IsDir():
- if err := copyDirectory(target, source, inodes, o); err != nil {
- return err
- }
- continue
- case (fileInfo.Mode() & os.ModeType) == 0:
- link, err := getLinkSource(target, fileInfo, inodes)
- if err != nil {
- return fmt.Errorf("failed to get hardlink: %w", err)
- }
- if link != "" {
- if err := os.Link(link, target); err != nil {
- return fmt.Errorf("failed to create hard link: %w", err)
- }
- } else if err := CopyFile(target, source); err != nil {
- return fmt.Errorf("failed to copy files: %w", err)
- }
- case (fileInfo.Mode() & os.ModeSymlink) == os.ModeSymlink:
- link, err := os.Readlink(source)
- if err != nil {
- return fmt.Errorf("failed to read link: %s: %w", source, err)
- }
- if err := os.Symlink(link, target); err != nil {
- return fmt.Errorf("failed to create symlink: %s: %w", target, err)
- }
- case (fileInfo.Mode() & os.ModeDevice) == os.ModeDevice,
- (fileInfo.Mode() & os.ModeNamedPipe) == os.ModeNamedPipe,
- (fileInfo.Mode() & os.ModeSocket) == os.ModeSocket:
- if err := copyIrregular(target, fileInfo); err != nil {
- return fmt.Errorf("failed to create irregular file: %w", err)
- }
- default:
- logrus.Warnf("unsupported mode: %s: %s", source, fileInfo.Mode())
- continue
- }
- if err := copyFileInfo(fileInfo, source, target); err != nil {
- return fmt.Errorf("failed to copy file info: %w", err)
- }
- if err := copyXAttrs(target, source, o.xex, o.xeh); err != nil {
- return fmt.Errorf("failed to copy xattrs: %w", err)
- }
- }
- return nil
- }
- // CopyFile copies the source file to the target.
- // The most efficient means of copying is used for the platform.
- func CopyFile(target, source string) error {
- return copyFile(target, source)
- }
- func openAndCopyFile(target, source string) error {
- src, err := os.Open(source)
- if err != nil {
- return fmt.Errorf("failed to open source %s: %w", source, err)
- }
- defer src.Close()
- tgt, err := os.Create(target)
- if err != nil {
- return fmt.Errorf("failed to open target %s: %w", target, err)
- }
- defer tgt.Close()
- _, err = io.Copy(tgt, src)
- return err
- }
|