Example #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
}
Example #2
0
func (w *Walker) walkRegular(relPath string, info os.FileInfo, mtime time.Time, fchan chan protocol.FileInfo) error {
	curMode := uint32(info.Mode())
	if runtime.GOOS == "windows" && osutil.IsWindowsExecutable(relPath) {
		curMode |= 0111
	}

	var currentVersion protocol.Vector
	if w.CurrentFiler != nil {
		// A file is "unchanged", if it
		//  - exists
		//  - has the same permissions as previously, unless we are ignoring permissions
		//  - was not marked deleted (since it apparently exists now)
		//  - had the same modification time as it has now
		//  - was not a directory previously (since it's a file now)
		//  - was not a symlink (since it's a file now)
		//  - was not invalid (since it looks valid now)
		//  - has the same size as previously
		cf, ok := w.CurrentFiler.CurrentFile(relPath)
		permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Flags, curMode)
		if ok && permUnchanged && !cf.IsDeleted() && cf.Modified == mtime.Unix() && !cf.IsDirectory() &&
			!cf.IsSymlink() && !cf.IsInvalid() && cf.Size() == info.Size() {
			return nil
		}
		currentVersion = cf.Version

		l.Debugln("rescan:", cf, mtime.Unix(), info.Mode()&os.ModePerm)
	}

	var flags = curMode & uint32(maskModePerm)
	if w.IgnorePerms {
		flags = protocol.FlagNoPermBits | 0666
	}

	f := protocol.FileInfo{
		Name:       relPath,
		Version:    currentVersion.Update(w.ShortID),
		Flags:      flags,
		Modified:   mtime.Unix(),
		CachedSize: info.Size(),
	}
	l.Debugln("to hash:", relPath, f)

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

	return nil
}
Example #3
0
func (w *Walker) walkDir(relPath string, info os.FileInfo, mtime time.Time, dchan chan protocol.FileInfo) error {
	var currentVersion protocol.Vector

	if w.CurrentFiler != nil {
		// A directory is "unchanged", if it
		//  - exists
		//  - has the same permissions as previously, unless we are ignoring permissions
		//  - was not marked deleted (since it apparently exists now)
		//  - was a directory previously (not a file or something else)
		//  - was not a symlink (since it's a directory now)
		//  - was not invalid (since it looks valid now)
		cf, ok := w.CurrentFiler.CurrentFile(relPath)
		permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Flags, uint32(info.Mode()))
		if ok && permUnchanged && !cf.IsDeleted() && cf.IsDirectory() && !cf.IsSymlink() && !cf.IsInvalid() {
			return nil
		}
		currentVersion = cf.Version
	}

	flags := uint32(protocol.FlagDirectory)
	if w.IgnorePerms {
		flags |= protocol.FlagNoPermBits | 0777
	} else {
		flags |= uint32(info.Mode() & maskModePerm)
	}
	f := protocol.FileInfo{
		Name:     relPath,
		Version:  currentVersion.Update(w.ShortID),
		Flags:    flags,
		Modified: mtime.Unix(),
	}
	l.Debugln("dir:", relPath, f)

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

	return nil
}
Example #4
0
func (f *rwFolder) inConflict(current, replacement protocol.Vector) bool {
	if current.Concurrent(replacement) {
		// Obvious case
		return true
	}
	if replacement.Counter(f.model.shortID) > current.Counter(f.model.shortID) {
		// The replacement file contains a higher version for ourselves than
		// what we have. This isn't supposed to be possible, since it's only
		// we who can increment that counter. We take it as a sign that
		// something is wrong (our index may have been corrupted or removed)
		// and flag it as a conflict.
		return true
	}
	return false
}