Beispiel #1
0
// walkSymlinks returns true if the symlink should be skipped, or an error if
// we should stop walking altogether. filepath.Walk isn't supposed to
// transcend into symlinks at all, but there are rumours that this may have
// happened anyway under some circumstances, possibly Windows reparse points
// or something. Hence the "skip" return from this one.
func (w *Walker) walkSymlink(absPath, relPath string, dchan chan protocol.FileInfo) (skip bool, err error) {
	// If the target is a directory, do NOT descend down there. This will
	// cause files to get tracked, and removing the symlink will as a result
	// remove files in their real location.
	if !symlinks.Supported {
		return true, nil
	}

	// We always rehash symlinks as they have no modtime or
	// permissions. We check if they point to the old target by
	// checking that their existing blocks match with the blocks in
	// the index.

	target, targetType, err := symlinks.Read(absPath)
	if err != nil {
		l.Debugln("readlink error:", absPath, err)
		return true, nil
	}

	blocks, err := Blocks(strings.NewReader(target), w.BlockSize, 0, nil)
	if err != nil {
		l.Debugln("hash link error:", absPath, err)
		return true, nil
	}

	var currentVersion protocol.Vector
	if w.CurrentFiler != nil {
		// A symlink is "unchanged", if
		//  - it exists
		//  - it wasn't deleted (because it isn't now)
		//  - it was a symlink
		//  - it wasn't invalid
		//  - the symlink type (file/dir) was the same
		//  - the block list (i.e. hash of target) was the same
		cf, ok := w.CurrentFiler.CurrentFile(relPath)
		if ok && !cf.IsDeleted() && cf.IsSymlink() && !cf.IsInvalid() && SymlinkTypeEqual(targetType, cf) && BlocksEqual(cf.Blocks, blocks) {
			return true, nil
		}
		currentVersion = cf.Version
	}

	f := protocol.FileInfo{
		Name:     relPath,
		Version:  currentVersion.Update(w.ShortID),
		Flags:    uint32(protocol.FlagSymlink | protocol.FlagNoPermBits | 0666 | SymlinkFlags(targetType)),
		Modified: 0,
		Blocks:   blocks,
	}

	l.Debugln("symlink changedb:", absPath, f)

	select {
	case dchan <- f:
	case <-w.Cancel:
		return false, errors.New("cancelled")
	}

	return false, nil
}
Beispiel #2
0
func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan error {
	walker := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		rn, _ := filepath.Rel(dir, path)
		if rn == "." || rn == ".stfolder" {
			return nil
		}
		if rn == ".stversions" {
			return filepath.SkipDir
		}

		var f fileInfo
		if info.Mode()&os.ModeSymlink != 0 {
			f = fileInfo{
				name: rn,
				mode: os.ModeSymlink,
			}

			tgt, _, err := symlinks.Read(path)
			if err != nil {
				return err
			}
			h := md5.New()
			h.Write([]byte(tgt))
			hash := h.Sum(nil)

			copy(f.hash[:], hash)
		} else if info.IsDir() {
			f = fileInfo{
				name: rn,
				mode: info.Mode(),
				// hash and modtime zero for directories
			}
		} else {
			f = fileInfo{
				name: rn,
				mode: info.Mode(),
				mod:  info.ModTime().Unix(),
				size: info.Size(),
			}
			sum, err := md5file(path)
			if err != nil {
				return err
			}
			f.hash = sum
		}

		select {
		case res <- f:
			return nil
		case <-abort:
			return errors.New("abort")
		}
	}

	errc := make(chan error)
	go func() {
		err := filepath.Walk(dir, walker)
		close(res)
		if err != nil {
			errc <- err
		}
		close(errc)
	}()
	return errc
}