github.com/golang/dep@v0.5.4/gps/filesystem.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gps
     6  
     7  import (
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // fsLink represents a symbolic link.
    16  type fsLink struct {
    17  	path string
    18  	to   string
    19  
    20  	// circular denotes if evaluating the symlink fails with "too many links" error.
    21  	// This error means that it's very likely that the symlink has a circular reference.
    22  	circular bool
    23  
    24  	// broken denotes that attempting to resolve the link fails, most likely because
    25  	// the destaination doesn't exist.
    26  	broken bool
    27  }
    28  
    29  // filesystemState represents the state of a file system.
    30  type filesystemState struct {
    31  	root  string
    32  	dirs  []string
    33  	files []string
    34  	links []fsLink
    35  }
    36  
    37  func (s filesystemState) setup() error {
    38  	for _, dir := range s.dirs {
    39  		p := filepath.Join(s.root, dir)
    40  
    41  		if err := os.MkdirAll(p, 0777); err != nil {
    42  			return errors.Errorf("os.MkdirAll(%q, 0777) err=%q", p, err)
    43  		}
    44  	}
    45  
    46  	for _, file := range s.files {
    47  		p := filepath.Join(s.root, file)
    48  
    49  		f, err := os.Create(p)
    50  		if err != nil {
    51  			return errors.Errorf("os.Create(%q) err=%q", p, err)
    52  		}
    53  
    54  		if err := f.Close(); err != nil {
    55  			return errors.Errorf("file %q Close() err=%q", p, err)
    56  		}
    57  	}
    58  
    59  	for _, link := range s.links {
    60  		p := filepath.Join(s.root, link.path)
    61  
    62  		// On Windows, relative symlinks confuse filepath.Walk. So, we'll just sigh
    63  		// and do absolute links, assuming they are relative to the directory of
    64  		// link.path.
    65  		//
    66  		// Reference: https://github.com/golang/go/issues/17540
    67  		//
    68  		// TODO(ibrasho): This was fixed in Go 1.9. Remove this when support for
    69  		// 1.8 is dropped.
    70  		dir := filepath.Dir(p)
    71  		to := ""
    72  		if link.to != "" {
    73  			to = filepath.Join(dir, link.to)
    74  		}
    75  
    76  		if err := os.Symlink(to, p); err != nil {
    77  			return errors.Errorf("os.Symlink(%q, %q) err=%q", to, p, err)
    78  		}
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  // deriveFilesystemState returns a filesystemState based on the state of
    85  // the filesystem on root.
    86  func deriveFilesystemState(root string) (filesystemState, error) {
    87  	fs := filesystemState{root: root}
    88  
    89  	err := filepath.Walk(fs.root, func(path string, info os.FileInfo, err error) error {
    90  		if err != nil {
    91  			return err
    92  		}
    93  
    94  		if path == fs.root {
    95  			return nil
    96  		}
    97  
    98  		relPath, err := filepath.Rel(fs.root, path)
    99  		if err != nil {
   100  			return err
   101  		}
   102  
   103  		if (info.Mode() & os.ModeSymlink) != 0 {
   104  			l := fsLink{path: relPath}
   105  
   106  			l.to, err = filepath.EvalSymlinks(path)
   107  			if err != nil && strings.HasSuffix(err.Error(), "too many links") {
   108  				l.circular = true
   109  			} else if err != nil && os.IsNotExist(err) {
   110  				l.broken = true
   111  			} else if err != nil {
   112  				return err
   113  			}
   114  
   115  			fs.links = append(fs.links, l)
   116  
   117  			return nil
   118  		}
   119  
   120  		if info.IsDir() {
   121  			fs.dirs = append(fs.dirs, relPath)
   122  
   123  			return nil
   124  		}
   125  
   126  		fs.files = append(fs.files, relPath)
   127  
   128  		return nil
   129  	})
   130  
   131  	if err != nil {
   132  		return filesystemState{}, err
   133  	}
   134  
   135  	return fs, nil
   136  }