github.com/Cloud-Foundations/Dominator@v0.3.4/lib/fsutil/copy.go (about)

     1  package fsutil
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path"
     9  
    10  	"github.com/Cloud-Foundations/Dominator/lib/wsyscall"
    11  )
    12  
    13  func copyToFile(destFilename string, perm os.FileMode, reader io.Reader,
    14  	length uint64) error {
    15  	tmpFilename := destFilename + "~"
    16  	destFile, err := os.OpenFile(tmpFilename,
    17  		os.O_CREATE|os.O_TRUNC|os.O_WRONLY, perm)
    18  	if err != nil {
    19  		return err
    20  	}
    21  	defer os.Remove(tmpFilename)
    22  	defer destFile.Close()
    23  	var nCopied int64
    24  	iLength := int64(length)
    25  	if length < 1 {
    26  		if _, err := io.Copy(destFile, reader); err != nil {
    27  			return fmt.Errorf("error copying: %s", err)
    28  		}
    29  	} else {
    30  		if nCopied, err = io.CopyN(destFile, reader, iLength); err != nil {
    31  			return fmt.Errorf("error copying: %s", err)
    32  		}
    33  		if nCopied != iLength {
    34  			return fmt.Errorf("expected length: %d, got: %d for: %s\n",
    35  				length, nCopied, tmpFilename)
    36  		}
    37  	}
    38  	return os.Rename(tmpFilename, destFilename)
    39  }
    40  
    41  func copyTree(destDir, sourceDir string,
    42  	copyFunc func(destFilename, sourceFilename string,
    43  		mode os.FileMode) error) error {
    44  	file, err := os.Open(sourceDir)
    45  	if err != nil {
    46  		if os.IsNotExist(err) {
    47  			return nil
    48  		}
    49  		return err
    50  	}
    51  	names, err := file.Readdirnames(-1)
    52  	file.Close()
    53  	if err != nil {
    54  		return err
    55  	}
    56  	for _, name := range names {
    57  		sourceFilename := path.Join(sourceDir, name)
    58  		destFilename := path.Join(destDir, name)
    59  		var stat wsyscall.Stat_t
    60  		if err := wsyscall.Lstat(sourceFilename, &stat); err != nil {
    61  			return errors.New(sourceFilename + ": " + err.Error())
    62  		}
    63  		switch stat.Mode & wsyscall.S_IFMT {
    64  		case wsyscall.S_IFDIR:
    65  			if err := os.Mkdir(destFilename, DirPerms); err != nil {
    66  				if !os.IsExist(err) {
    67  					return err
    68  				}
    69  			}
    70  			err := copyTree(destFilename, sourceFilename, copyFunc)
    71  			if err != nil {
    72  				return err
    73  			}
    74  		case wsyscall.S_IFREG:
    75  			err := copyFunc(destFilename, sourceFilename,
    76  				os.FileMode(stat.Mode)&os.ModePerm)
    77  			if err != nil {
    78  				return err
    79  			}
    80  		case wsyscall.S_IFLNK:
    81  			sourceTarget, err := os.Readlink(sourceFilename)
    82  			if err != nil {
    83  				return errors.New(sourceFilename + ": " + err.Error())
    84  			}
    85  			if destTarget, err := os.Readlink(destFilename); err == nil {
    86  				if sourceTarget == destTarget {
    87  					continue
    88  				}
    89  			}
    90  			os.Remove(destFilename)
    91  			if err := os.Symlink(sourceTarget, destFilename); err != nil {
    92  				return err
    93  			}
    94  		default:
    95  			return errors.New("unsupported file type")
    96  		}
    97  	}
    98  	return nil
    99  }
   100  
   101  func copyFile(destFilename, sourceFilename string, mode os.FileMode) error {
   102  	if mode == 0 {
   103  		var stat wsyscall.Stat_t
   104  		if err := wsyscall.Stat(sourceFilename, &stat); err != nil {
   105  			return errors.New(sourceFilename + ": " + err.Error())
   106  		}
   107  		mode = os.FileMode(stat.Mode & wsyscall.S_IFMT)
   108  	}
   109  	sourceFile, err := os.Open(sourceFilename)
   110  	if err != nil {
   111  		return errors.New(sourceFilename + ": " + err.Error())
   112  	}
   113  	defer sourceFile.Close()
   114  	return copyToFile(destFilename, mode, sourceFile, 0)
   115  }