// 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 }
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 }
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 }
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 }