Пример #1
0
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")
		}
	}
}
Пример #2
0
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
}
Пример #3
0
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
}
Пример #4
0
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
}
Пример #5
0
// 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
}
Пример #6
0
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
}
Пример #7
0
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
}
Пример #8
0
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
}
Пример #9
0
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
}
Пример #10
0
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
}
Пример #11
0
func Create(source, target string, flags uint32) error {
	return os.Symlink(osutil.NativeFilename(target), source)
}