func nativeFileIterator(fn Iterator) Iterator { return func(fi FileIntf) bool { switch f := fi.(type) { case protocol.FileInfo: f.Name = osutil.NativeFilename(f.Name) return fn(f) case FileInfoTruncated: f.Name = osutil.NativeFilename(f.Name) return fn(f) default: panic("unknown interface type") } } }
func init() { defer func() { if err := recover(); err != nil { // Ensure that the supported flag is disabled when we hit an // error, even though it should already be. Also, silently swallow // the error since it's fine for a system not to support symlinks. Supported = false } }() // Needs administrator priviledges. // Let's check that everything works. // This could be done more officially: // http://stackoverflow.com/questions/2094663/determine-if-windows-process-has-privilege-to-create-symbolic-link // But I don't want to define 10 more structs just to look this up. base := os.TempDir() path := filepath.Join(base, "symlinktest") defer os.Remove(path) err := Create(path, base, protocol.FlagDirectory) if err != nil { return } stat, err := os.Lstat(path) if err != nil || stat.Mode()&os.ModeSymlink == 0 { return } target, flags, err := Read(path) if err != nil || osutil.NativeFilename(target) != base || flags&protocol.FlagDirectory == 0 { return } Supported = true }
func (s *FileSet) GetGlobalTruncated(file string) (FileInfoTruncated, bool) { fi, ok := ldbGetGlobal(s.db, []byte(s.folder), []byte(osutil.NormalizedFilename(file)), true) if !ok { return FileInfoTruncated{}, false } f := fi.(FileInfoTruncated) f.Name = osutil.NativeFilename(f.Name) return f, true }
func (s *FileSet) GetGlobal(file string) (protocol.FileInfo, bool) { fi, ok := ldbGetGlobal(s.db, []byte(s.folder), []byte(osutil.NormalizedFilename(file)), false) if !ok { return protocol.FileInfo{}, false } f := fi.(protocol.FileInfo) f.Name = osutil.NativeFilename(f.Name) return f, true }
// An iterator function which iterates over all matching blocks for the given // hash. The iterator function has to return either true (if they are happy with // the block) or false to continue iterating for whatever reason. // The iterator finally returns the result, whether or not a satisfying block // was eventually found. func (f *BlockFinder) Iterate(hash []byte, iterFn func(string, string, int32) bool) bool { f.mut.RLock() folders := f.folders f.mut.RUnlock() for _, folder := range folders { key := toBlockKey(hash, folder, "") iter := f.db.NewIterator(util.BytesPrefix(key), nil) defer iter.Release() for iter.Next() && iter.Error() == nil { folder, file := fromBlockKey(iter.Key()) index := int32(binary.BigEndian.Uint32(iter.Value())) if iterFn(folder, osutil.NativeFilename(file), index) { return true } } } return false }
func Create(source, target string, flags uint32) error { srcp, err := syscall.UTF16PtrFromString(source) if err != nil { return err } trgp, err := syscall.UTF16PtrFromString(osutil.NativeFilename(target)) if err != nil { return err } // Sadly for Windows we need to specify the type of the symlink, // whether it's a directory symlink or a file symlink. // If the flags doesn't reveal the target type, try to evaluate it // ourselves, and worst case default to the symlink pointing to a file. mode := 0 if flags&protocol.FlagSymlinkMissingTarget != 0 { path := target if !filepath.IsAbs(target) { path = filepath.Join(filepath.Dir(source), target) } stat, err := os.Stat(path) if err == nil && stat.IsDir() { mode = SYMBOLIC_LINK_FLAG_DIRECTORY } } else if flags&protocol.FlagDirectory != 0 { mode = SYMBOLIC_LINK_FLAG_DIRECTORY } r0, _, err := syscall.Syscall(procCreateSymbolicLink.Addr(), 3, uintptr(unsafe.Pointer(srcp)), uintptr(unsafe.Pointer(trgp)), uintptr(mode)) if r0 == 1 { return nil } return err }
func (s *FileSet) Get(device protocol.DeviceID, file string) (protocol.FileInfo, bool) { f, ok := ldbGet(s.db, []byte(s.folder), device[:], []byte(osutil.NormalizedFilename(file))) f.Name = osutil.NativeFilename(f.Name) return f, ok }
func (s *Set) GetGlobal(file string) protocol.FileInfo { f := ldbGetGlobal(s.db, []byte(s.folder), []byte(osutil.NormalizedFilename(file))) f.Name = osutil.NativeFilename(f.Name) return f }
func (m *Model) GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{} { m.fmut.RLock() files, ok := m.folderFiles[folder] m.fmut.RUnlock() if !ok { return nil } output := make(map[string]interface{}) sep := string(filepath.Separator) prefix = osutil.NativeFilename(prefix) if prefix != "" && !strings.HasSuffix(prefix, sep) { prefix = prefix + sep } files.WithPrefixedGlobalTruncated(prefix, func(fi db.FileIntf) bool { f := fi.(db.FileInfoTruncated) if f.IsInvalid() || f.IsDeleted() || f.Name == prefix { return true } f.Name = strings.Replace(f.Name, prefix, "", 1) var dir, base string if f.IsDirectory() && !f.IsSymlink() { dir = f.Name } else { dir = filepath.Dir(f.Name) base = filepath.Base(f.Name) } if levels > -1 && strings.Count(f.Name, sep) > levels { return true } last := output if dir != "." { for _, path := range strings.Split(dir, sep) { directory, ok := last[path] if !ok { newdir := make(map[string]interface{}) last[path] = newdir last = newdir } else { last = directory.(map[string]interface{}) } } } if !dirsonly && base != "" { last[base] = []interface{}{ time.Unix(f.Modified, 0), f.Size(), } } return true }) return output }
func (m *Model) internalScanFolderSubs(folder string, subs []string) error { for i, sub := range subs { sub = osutil.NativeFilename(sub) if p := filepath.Clean(filepath.Join(folder, sub)); !strings.HasPrefix(p, folder) { return errors.New("invalid subpath") } subs[i] = sub } m.fmut.Lock() fs := m.folderFiles[folder] folderCfg := m.folderCfgs[folder] ignores := m.folderIgnores[folder] runner, ok := m.folderRunners[folder] m.fmut.Unlock() // Folders are added to folderRunners only when they are started. We can't // scan them before they have started, so that's what we need to check for // here. if !ok { return errors.New("no such folder") } _ = ignores.Load(filepath.Join(folderCfg.Path(), ".stignore")) // Ignore error, there might not be an .stignore // Required to make sure that we start indexing at a directory we're already // aware off. var unifySubs []string nextSub: for _, sub := range subs { for sub != "" { if _, ok = fs.Get(protocol.LocalDeviceID, sub); ok { break } sub = filepath.Dir(sub) if sub == "." || sub == string(filepath.Separator) { sub = "" } } for _, us := range unifySubs { if strings.HasPrefix(sub, us) { continue nextSub } } unifySubs = append(unifySubs, sub) } subs = unifySubs w := &scanner.Walker{ Dir: folderCfg.Path(), Subs: subs, Matcher: ignores, BlockSize: protocol.BlockSize, TempNamer: defTempNamer, TempLifetime: time.Duration(m.cfg.Options().KeepTemporariesH) * time.Hour, CurrentFiler: cFiler{m, folder}, MtimeRepo: db.NewVirtualMtimeRepo(m.db, folderCfg.ID), IgnorePerms: folderCfg.IgnorePerms, AutoNormalize: folderCfg.AutoNormalize, Hashers: m.numHashers(folder), ShortID: m.shortID, } runner.setState(FolderScanning) fchan, err := w.Walk() if err != nil { // The error we get here is likely an OS level error, which might not be // as readable as our health check errors. Check if we can get a health // check error first, and use that if it's available. if ferr := m.CheckFolderHealth(folder); ferr != nil { err = ferr } runner.setError(err) return err } batchSizeFiles := 100 batchSizeBlocks := 2048 // about 256 MB batch := make([]protocol.FileInfo, 0, batchSizeFiles) blocksHandled := 0 for f := range fchan { if len(batch) == batchSizeFiles || blocksHandled > batchSizeBlocks { if err := m.CheckFolderHealth(folder); err != nil { l.Infof("Stopping folder %s mid-scan due to folder error: %s", folder, err) return err } m.updateLocals(folder, batch) batch = batch[:0] blocksHandled = 0 } batch = append(batch, f) blocksHandled += len(f.Blocks) } if err := m.CheckFolderHealth(folder); err != nil { l.Infof("Stopping folder %s mid-scan due to folder error: %s", folder, err) return err } else if len(batch) > 0 { m.updateLocals(folder, batch) } batch = batch[:0] // TODO: We should limit the Have scanning to start at sub seenPrefix := false var iterError error fs.WithHaveTruncated(protocol.LocalDeviceID, func(fi db.FileIntf) bool { f := fi.(db.FileInfoTruncated) hasPrefix := len(subs) == 0 for _, sub := range subs { if strings.HasPrefix(f.Name, sub) { hasPrefix = true break } } // Return true so that we keep iterating, until we get to the part // of the tree we are interested in. Then return false so we stop // iterating when we've passed the end of the subtree. if !hasPrefix { return !seenPrefix } seenPrefix = true if !f.IsDeleted() { if f.IsInvalid() { return true } if len(batch) == batchSizeFiles { if err := m.CheckFolderHealth(folder); err != nil { iterError = err return false } m.updateLocals(folder, batch) batch = batch[:0] } if ignores.Match(f.Name) || symlinkInvalid(folder, f) { // File has been ignored or an unsupported symlink. Set invalid bit. if debug { l.Debugln("setting invalid bit on ignored", f) } nf := protocol.FileInfo{ Name: f.Name, Flags: f.Flags | protocol.FlagInvalid, Modified: f.Modified, Version: f.Version, // The file is still the same, so don't bump version } batch = append(batch, nf) } else if _, err := osutil.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil { // File has been deleted. // We don't specifically verify that the error is // os.IsNotExist because there is a corner case when a // directory is suddenly transformed into a file. When that // happens, files that were in the directory (that is now a // file) are deleted but will return a confusing error ("not a // directory") when we try to Lstat() them. nf := protocol.FileInfo{ Name: f.Name, Flags: f.Flags | protocol.FlagDeleted, Modified: f.Modified, Version: f.Version.Update(m.shortID), } batch = append(batch, nf) } } return true }) if iterError != nil { l.Infof("Stopping folder %s mid-scan due to folder error: %s", folder, iterError) return iterError } if err := m.CheckFolderHealth(folder); err != nil { l.Infof("Stopping folder %s mid-scan due to folder error: %s", folder, err) return err } else if len(batch) > 0 { m.updateLocals(folder, batch) } runner.setState(FolderIdle) return nil }
func Create(source, target string, flags uint32) error { return os.Symlink(osutil.NativeFilename(target), source) }