mirror of
https://github.com/syncthing/syncthing.git
synced 2024-11-15 18:08:45 -07:00
all: Remove symlink support on Windows, SymlinksEnabled config
After this change, - Symlinks on Windows are always unsupported. Sorry. - Symlinks are always enabled on other platforms. They are just a small file like anything else. There is no need to special case them. If you don't want to sync some symlinks, ignore them. - The protocol doesn't differentiate between different "types" of symlinks. If that distinction ever does become relevant the individual devices can figure it out by looking at the destination when they create the link. It's backwards compatible in that all the old symlink types are still understood to be symlinks, and the new SYMLINK type is equivalent to the old SYMLINK_UNKNOWN which was always a valid way to do it. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3962 LGTM: AudriusButkevicius
This commit is contained in:
parent
9fda9642d3
commit
c4ba580cbb
@ -15,8 +15,6 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -104,7 +102,7 @@ func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan er
|
||||
mode: os.ModeSymlink,
|
||||
}
|
||||
|
||||
tgt, _, err := symlinks.Read(path)
|
||||
tgt, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -43,7 +43,6 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/sha256"
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
"github.com/syncthing/syncthing/lib/tlsutil"
|
||||
"github.com/syncthing/syncthing/lib/upgrade"
|
||||
"github.com/syncthing/syncthing/lib/weakhash"
|
||||
@ -679,10 +678,6 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
||||
|
||||
opts := cfg.Options()
|
||||
|
||||
if !opts.SymlinksEnabled {
|
||||
symlinks.Supported = false
|
||||
}
|
||||
|
||||
if opts.WeakHashSelectionMethod == config.WeakHashAuto {
|
||||
if perfWithoutWeakHash*0.8 > perfWithWeakHash {
|
||||
l.Infof("Weak hash disabled, as it has an unacceptable performance impact.")
|
||||
@ -751,6 +746,10 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
||||
// have been incorrectly ignore filtered.
|
||||
ldb.DropDeltaIndexIDs()
|
||||
}
|
||||
if cfg.RawCopy().OriginalVersion < 19 {
|
||||
// Converts old symlink types to new in the entire database.
|
||||
ldb.ConvertSymlinkTypes()
|
||||
}
|
||||
|
||||
m := model.NewModel(cfg, myID, myDeviceName(cfg), "syncthing", Version, ldb, protectedFiles)
|
||||
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
|
||||
const (
|
||||
OldestHandledVersion = 10
|
||||
CurrentVersion = 18
|
||||
CurrentVersion = 19
|
||||
MaxRescanIntervalS = 365 * 24 * 60 * 60
|
||||
)
|
||||
|
||||
@ -261,6 +261,9 @@ func (cfg *Configuration) clean() error {
|
||||
if cfg.Version == 17 {
|
||||
convertV17V18(cfg)
|
||||
}
|
||||
if cfg.Version == 18 {
|
||||
convertV18V19(cfg)
|
||||
}
|
||||
|
||||
// Build a list of available devices
|
||||
existingDevices := make(map[protocol.DeviceID]bool)
|
||||
@ -314,6 +317,11 @@ func (cfg *Configuration) clean() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertV18V19(cfg *Configuration) {
|
||||
// Triggers a database tweak
|
||||
cfg.Version = 19
|
||||
}
|
||||
|
||||
func convertV17V18(cfg *Configuration) {
|
||||
// Do channel selection for existing users. Those who have auto upgrades
|
||||
// and usage reporting on default to the candidate channel. Others get
|
||||
|
@ -54,7 +54,6 @@ func TestDefaultValues(t *testing.T) {
|
||||
KeepTemporariesH: 24,
|
||||
CacheIgnoredFiles: false,
|
||||
ProgressUpdateIntervalS: 5,
|
||||
SymlinksEnabled: true,
|
||||
LimitBandwidthInLan: false,
|
||||
MinHomeDiskFreePct: 1,
|
||||
URURL: "https://data.syncthing.net/newdata",
|
||||
@ -191,7 +190,6 @@ func TestOverriddenValues(t *testing.T) {
|
||||
KeepTemporariesH: 48,
|
||||
CacheIgnoredFiles: true,
|
||||
ProgressUpdateIntervalS: 10,
|
||||
SymlinksEnabled: false,
|
||||
LimitBandwidthInLan: true,
|
||||
MinHomeDiskFreePct: 5.2,
|
||||
URURL: "https://localhost/newdata",
|
||||
|
@ -122,7 +122,6 @@ type OptionsConfiguration struct {
|
||||
KeepTemporariesH int `xml:"keepTemporariesH" json:"keepTemporariesH" default:"24"` // 0 for off
|
||||
CacheIgnoredFiles bool `xml:"cacheIgnoredFiles" json:"cacheIgnoredFiles" default:"false"`
|
||||
ProgressUpdateIntervalS int `xml:"progressUpdateIntervalS" json:"progressUpdateIntervalS" default:"5"`
|
||||
SymlinksEnabled bool `xml:"symlinksEnabled" json:"symlinksEnabled" default:"true"`
|
||||
LimitBandwidthInLan bool `xml:"limitBandwidthInLan" json:"limitBandwidthInLan" default:"false"`
|
||||
MinHomeDiskFreePct float64 `xml:"minHomeDiskFreePct" json:"minHomeDiskFreePct" default:"1"`
|
||||
ReleasesURL string `xml:"releasesURL" json:"releasesURL" default:"https://upgrades.syncthing.net/meta.json"`
|
||||
|
15
lib/config/testdata/v19.xml
vendored
Normal file
15
lib/config/testdata/v19.xml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<configuration version="19">
|
||||
<folder id="test" path="testdata" type="readonly" ignorePerms="false" rescanIntervalS="600" autoNormalize="true">
|
||||
<device id="AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR"></device>
|
||||
<device id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2"></device>
|
||||
<minDiskFreePct>1</minDiskFreePct>
|
||||
<maxConflicts>-1</maxConflicts>
|
||||
<fsync>true</fsync>
|
||||
</folder>
|
||||
<device id="AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR" name="node one" compression="metadata">
|
||||
<address>tcp://a</address>
|
||||
</device>
|
||||
<device id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2" name="node two" compression="metadata">
|
||||
<address>tcp://b</address>
|
||||
</device>
|
||||
</configuration>
|
@ -618,6 +618,41 @@ func (db *Instance) checkGlobals(folder []byte, globalSize *sizeTracker) {
|
||||
l.Debugf("db check completed for %q", folder)
|
||||
}
|
||||
|
||||
// ConvertSymlinkTypes should be run once only on an old database. It
|
||||
// changes SYMLINK_FILE and SYMLINK_DIRECTORY types to the current SYMLINK
|
||||
// type (previously SYMLINK_UNKNOWN). It does this for all devices, both
|
||||
// local and remote, and does not reset delta indexes. It shouldn't really
|
||||
// matter what the symlink type is, but this cleans it up for a possible
|
||||
// future when SYMLINK_FILE and SYMLINK_DIRECTORY are no longer understood.
|
||||
func (db *Instance) ConvertSymlinkTypes() {
|
||||
t := db.newReadWriteTransaction()
|
||||
defer t.close()
|
||||
|
||||
dbi := t.NewIterator(util.BytesPrefix([]byte{KeyTypeDevice}), nil)
|
||||
defer dbi.Release()
|
||||
|
||||
conv := 0
|
||||
for dbi.Next() {
|
||||
var f protocol.FileInfo
|
||||
if err := f.Unmarshal(dbi.Value()); err != nil {
|
||||
// probably can't happen
|
||||
continue
|
||||
}
|
||||
if f.Type == protocol.FileInfoTypeDeprecatedSymlinkDirectory || f.Type == protocol.FileInfoTypeDeprecatedSymlinkFile {
|
||||
f.Type = protocol.FileInfoTypeSymlink
|
||||
bs, err := f.Marshal()
|
||||
if err != nil {
|
||||
panic("can't happen: " + err.Error())
|
||||
}
|
||||
t.Put(dbi.Key(), bs)
|
||||
t.checkFlush()
|
||||
conv++
|
||||
}
|
||||
}
|
||||
|
||||
l.Infof("Updated symlink type for %d index entries", conv)
|
||||
}
|
||||
|
||||
// deviceKey returns a byte slice encoding the following information:
|
||||
// keyTypeDevice (1 byte)
|
||||
// folder (4 bytes)
|
||||
|
@ -35,7 +35,7 @@ func (f FileInfoTruncated) IsDirectory() bool {
|
||||
|
||||
func (f FileInfoTruncated) IsSymlink() bool {
|
||||
switch f.Type {
|
||||
case protocol.FileInfoTypeSymlinkDirectory, protocol.FileInfoTypeSymlinkFile, protocol.FileInfoTypeSymlinkUnknown:
|
||||
case protocol.FileInfoTypeSymlink, protocol.FileInfoTypeDeprecatedSymlinkDirectory, protocol.FileInfoTypeDeprecatedSymlinkFile:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
@ -20,24 +20,10 @@ func (BasicFilesystem) SymlinksSupported() bool {
|
||||
return symlinksSupported
|
||||
}
|
||||
|
||||
func (BasicFilesystem) CreateSymlink(name, target string, _ LinkTargetType) error {
|
||||
func (BasicFilesystem) CreateSymlink(name, target string) error {
|
||||
return os.Symlink(target, name)
|
||||
}
|
||||
|
||||
func (BasicFilesystem) ChangeSymlinkType(_ string, _ LinkTargetType) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (BasicFilesystem) ReadSymlink(path string) (string, LinkTargetType, error) {
|
||||
tt := LinkTargetUnknown
|
||||
if stat, err := os.Stat(path); err == nil {
|
||||
if stat.IsDir() {
|
||||
tt = LinkTargetDirectory
|
||||
} else {
|
||||
tt = LinkTargetFile
|
||||
}
|
||||
}
|
||||
|
||||
path, err := os.Readlink(path)
|
||||
return path, tt, err
|
||||
func (BasicFilesystem) ReadSymlink(path string) (string, error) {
|
||||
return os.Readlink(path)
|
||||
}
|
||||
|
@ -8,188 +8,20 @@
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
import "errors"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
var errNotSupported = errors.New("symlinks not supported")
|
||||
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
win32FsctlGetReparsePoint = 0x900a8
|
||||
win32FileFlagOpenReparsePoint = 0x00200000
|
||||
win32SymbolicLinkFlagDirectory = 0x1
|
||||
)
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procDeviceIoControl = modkernel32.NewProc("DeviceIoControl")
|
||||
procCreateSymbolicLink = modkernel32.NewProc("CreateSymbolicLinkW")
|
||||
symlinksSupported = false
|
||||
)
|
||||
|
||||
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.
|
||||
symlinksSupported = false
|
||||
}
|
||||
}()
|
||||
|
||||
// Needs administrator privileges.
|
||||
// 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 := DefaultFilesystem.CreateSymlink(path, base, LinkTargetDirectory)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stat, err := osutil.Lstat(path)
|
||||
if err != nil || stat.Mode()&os.ModeSymlink == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
target, tt, err := DefaultFilesystem.ReadSymlink(path)
|
||||
if err != nil || osutil.NativeFilename(target) != base || tt != LinkTargetDirectory {
|
||||
return
|
||||
}
|
||||
symlinksSupported = true
|
||||
}
|
||||
|
||||
func DisableSymlinks() {
|
||||
symlinksSupported = false
|
||||
}
|
||||
func DisableSymlinks() {}
|
||||
|
||||
func (BasicFilesystem) SymlinksSupported() bool {
|
||||
return symlinksSupported
|
||||
return false
|
||||
}
|
||||
|
||||
func (BasicFilesystem) ReadSymlink(path string) (string, LinkTargetType, error) {
|
||||
ptr, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return "", LinkTargetUnknown, err
|
||||
}
|
||||
handle, err := syscall.CreateFile(ptr, 0, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS|win32FileFlagOpenReparsePoint, 0)
|
||||
if err != nil || handle == syscall.InvalidHandle {
|
||||
return "", LinkTargetUnknown, err
|
||||
}
|
||||
defer syscall.Close(handle)
|
||||
var ret uint16
|
||||
var data reparseData
|
||||
|
||||
r1, _, err := syscall.Syscall9(procDeviceIoControl.Addr(), 8, uintptr(handle), win32FsctlGetReparsePoint, 0, 0, uintptr(unsafe.Pointer(&data)), unsafe.Sizeof(data), uintptr(unsafe.Pointer(&ret)), 0, 0)
|
||||
if r1 == 0 {
|
||||
return "", LinkTargetUnknown, err
|
||||
}
|
||||
|
||||
tt := LinkTargetUnknown
|
||||
if attr, err := syscall.GetFileAttributes(ptr); err == nil {
|
||||
if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
tt = LinkTargetDirectory
|
||||
} else {
|
||||
tt = LinkTargetFile
|
||||
}
|
||||
}
|
||||
|
||||
return osutil.NormalizedFilename(data.printName()), tt, nil
|
||||
func (BasicFilesystem) ReadSymlink(path string) (string, error) {
|
||||
return "", errNotSupported
|
||||
}
|
||||
|
||||
func (BasicFilesystem) CreateSymlink(path, target string, tt LinkTargetType) error {
|
||||
srcp, err := syscall.UTF16PtrFromString(path)
|
||||
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 tt == LinkTargetUnknown {
|
||||
path := target
|
||||
if !filepath.IsAbs(target) {
|
||||
path = filepath.Join(filepath.Dir(path), target)
|
||||
}
|
||||
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && stat.IsDir() {
|
||||
mode = win32SymbolicLinkFlagDirectory
|
||||
}
|
||||
} else if tt == LinkTargetDirectory {
|
||||
mode = win32SymbolicLinkFlagDirectory
|
||||
}
|
||||
|
||||
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 (fs BasicFilesystem) ChangeSymlinkType(path string, tt LinkTargetType) error {
|
||||
target, existingTargetType, err := fs.ReadSymlink(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If it's the same type, nothing to do.
|
||||
if tt == existingTargetType {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the actual type is unknown, but the new type is file, nothing to do
|
||||
if existingTargetType == LinkTargetUnknown && tt != LinkTargetDirectory {
|
||||
return nil
|
||||
}
|
||||
return osutil.InWritableDir(func(path string) error {
|
||||
// It should be a symlink as well hence no need to change permissions on
|
||||
// the file.
|
||||
os.Remove(path)
|
||||
return fs.CreateSymlink(path, target, tt)
|
||||
}, path)
|
||||
}
|
||||
|
||||
type reparseData struct {
|
||||
reparseTag uint32
|
||||
reparseDataLength uint16
|
||||
reserved uint16
|
||||
substitueNameOffset uint16
|
||||
substitueNameLength uint16
|
||||
printNameOffset uint16
|
||||
printNameLength uint16
|
||||
flags uint32
|
||||
// substituteName - 264 widechars max = 528 bytes
|
||||
// printName - 260 widechars max = 520 bytes
|
||||
// = 1048 bytes total
|
||||
buffer [1048 / 2]uint16
|
||||
}
|
||||
|
||||
func (r *reparseData) printName() string {
|
||||
// offset and length are in bytes but we're indexing a []uint16
|
||||
offset := r.printNameOffset / 2
|
||||
length := r.printNameLength / 2
|
||||
return string(utf16.Decode(r.buffer[offset : offset+length]))
|
||||
}
|
||||
|
||||
func (r *reparseData) substituteName() string {
|
||||
// offset and length are in bytes but we're indexing a []uint16
|
||||
offset := r.substitueNameOffset / 2
|
||||
length := r.substitueNameLength / 2
|
||||
return string(utf16.Decode(r.buffer[offset : offset+length]))
|
||||
func (BasicFilesystem) CreateSymlink(path, target string) error {
|
||||
return errNotSupported
|
||||
}
|
||||
|
@ -12,26 +12,17 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type LinkTargetType int
|
||||
|
||||
const (
|
||||
LinkTargetFile LinkTargetType = iota
|
||||
LinkTargetDirectory
|
||||
LinkTargetUnknown
|
||||
)
|
||||
|
||||
// The Filesystem interface abstracts access to the file system.
|
||||
type Filesystem interface {
|
||||
ChangeSymlinkType(name string, tt LinkTargetType) error
|
||||
Chmod(name string, mode FileMode) error
|
||||
Chtimes(name string, atime time.Time, mtime time.Time) error
|
||||
Create(name string) (File, error)
|
||||
CreateSymlink(name, target string, tt LinkTargetType) error
|
||||
CreateSymlink(name, target string) error
|
||||
DirNames(name string) ([]string, error)
|
||||
Lstat(name string) (FileInfo, error)
|
||||
Mkdir(name string, perm FileMode) error
|
||||
Open(name string) (File, error)
|
||||
ReadSymlink(name string) (string, LinkTargetType, error)
|
||||
ReadSymlink(name string) (string, error)
|
||||
Remove(name string) error
|
||||
Rename(oldname, newname string) error
|
||||
Stat(name string) (FileInfo, error)
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
stdsync "sync"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
@ -33,7 +32,6 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/scanner"
|
||||
"github.com/syncthing/syncthing/lib/stats"
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
"github.com/syncthing/syncthing/lib/upgrade"
|
||||
"github.com/syncthing/syncthing/lib/versioner"
|
||||
@ -105,7 +103,6 @@ type Model struct {
|
||||
type folderFactory func(*Model, config.FolderConfiguration, versioner.Versioner, *fs.MtimeFS) service
|
||||
|
||||
var (
|
||||
symlinkWarning = stdsync.Once{}
|
||||
folderFactories = make(map[config.FolderType]folderFactory, 0)
|
||||
)
|
||||
|
||||
@ -116,7 +113,6 @@ var (
|
||||
errFolderMarkerMissing = errors.New("folder marker missing")
|
||||
errHomeDiskNoSpace = errors.New("home disk has insufficient free space")
|
||||
errFolderNoSpace = errors.New("folder has insufficient free space")
|
||||
errUnsupportedSymlink = errors.New("symlink not supported")
|
||||
errInvalidFilename = errors.New("filename is invalid")
|
||||
errDeviceUnknown = errors.New("unknown device")
|
||||
errDevicePaused = errors.New("device is paused")
|
||||
@ -1879,9 +1875,8 @@ func (m *Model) internalScanFolderSubdirs(folder string, subDirs []string) error
|
||||
}
|
||||
|
||||
switch {
|
||||
case !f.IsInvalid() && (ignores.Match(f.Name).IsIgnored() || symlinkInvalid(folder, f)):
|
||||
// File was valid at last pass but has been ignored or is an
|
||||
// unsupported symlink. Set invalid bit.
|
||||
case !f.IsInvalid() && ignores.Match(f.Name).IsIgnored():
|
||||
// File was valid at last pass but has been ignored. Set invalid bit.
|
||||
l.Debugln("setting invalid bit on ignored", f)
|
||||
nf := protocol.FileInfo{
|
||||
Name: f.Name,
|
||||
@ -2490,26 +2485,6 @@ func mapDeviceConfigs(devices []config.DeviceConfiguration) map[protocol.DeviceI
|
||||
return m
|
||||
}
|
||||
|
||||
func symlinkInvalid(folder string, fi db.FileIntf) bool {
|
||||
if !symlinks.Supported && fi.IsSymlink() && !fi.IsInvalid() && !fi.IsDeleted() {
|
||||
symlinkWarning.Do(func() {
|
||||
l.Warnln("Symlinks are disabled, unsupported or require Administrator privileges. This might cause your folder to appear out of sync.")
|
||||
})
|
||||
|
||||
// Need to type switch for the concrete type to be able to access fields...
|
||||
var name string
|
||||
switch fi := fi.(type) {
|
||||
case protocol.FileInfo:
|
||||
name = fi.Name
|
||||
case db.FileInfoTruncated:
|
||||
name = fi.Name
|
||||
}
|
||||
l.Infoln("Unsupported symlink", name, "in folder", folder)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Skips `skip` elements and retrieves up to `get` elements from a given slice.
|
||||
// Returns the resulting slice, plus how much elements are left to skip or
|
||||
// copy to satisfy the values which were provided, given the slice is not
|
||||
|
@ -359,7 +359,7 @@ func TestSendDownloadProgressMessages(t *testing.T) {
|
||||
file: protocol.FileInfo{
|
||||
Name: "state6",
|
||||
Version: v1,
|
||||
Type: protocol.FileInfoTypeSymlinkUnknown,
|
||||
Type: protocol.FileInfoTypeSymlink,
|
||||
},
|
||||
mut: sync.NewRWMutex(),
|
||||
available: []int32{1, 2, 3},
|
||||
|
@ -89,7 +89,7 @@ func TestSymlinkTraversalRead(t *testing.T) {
|
||||
|
||||
// Send an update for the symlink, wait for it to sync and be reported back.
|
||||
contents := []byte("..")
|
||||
fc.addFile("symlink", 0644, protocol.FileInfoTypeSymlinkDirectory, contents)
|
||||
fc.addFile("symlink", 0644, protocol.FileInfoTypeSymlink, contents)
|
||||
fc.sendIndexUpdate()
|
||||
<-done
|
||||
|
||||
@ -142,7 +142,7 @@ func TestSymlinkTraversalWrite(t *testing.T) {
|
||||
|
||||
// Send an update for the symlink, wait for it to sync and be reported back.
|
||||
contents := []byte("..")
|
||||
fc.addFile("symlink", 0644, protocol.FileInfoTypeSymlinkDirectory, contents)
|
||||
fc.addFile("symlink", 0644, protocol.FileInfoTypeSymlink, contents)
|
||||
fc.sendIndexUpdate()
|
||||
<-done
|
||||
|
||||
@ -152,7 +152,7 @@ func TestSymlinkTraversalWrite(t *testing.T) {
|
||||
contents = []byte("testdata testdata\n")
|
||||
fc.addFile("symlink/testfile", 0644, protocol.FileInfoTypeFile, contents)
|
||||
fc.addFile("symlink/testdir", 0644, protocol.FileInfoTypeDirectory, contents)
|
||||
fc.addFile("symlink/testsyml", 0644, protocol.FileInfoTypeSymlinkFile, contents)
|
||||
fc.addFile("symlink/testsyml", 0644, protocol.FileInfoTypeSymlink, contents)
|
||||
fc.sendIndexUpdate()
|
||||
|
||||
select {
|
||||
@ -191,7 +191,7 @@ func TestRequestCreateTmpSymlink(t *testing.T) {
|
||||
fc.mut.Unlock()
|
||||
|
||||
// Send an update for the test file, wait for it to sync and be reported back.
|
||||
fc.addFile(".syncthing.testlink.tmp", 0644, protocol.FileInfoTypeSymlinkDirectory, []byte(".."))
|
||||
fc.addFile(".syncthing.testlink.tmp", 0644, protocol.FileInfoTypeSymlink, []byte(".."))
|
||||
fc.sendIndexUpdate()
|
||||
|
||||
select {
|
||||
|
@ -25,7 +25,6 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/scanner"
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
"github.com/syncthing/syncthing/lib/versioner"
|
||||
"github.com/syncthing/syncthing/lib/weakhash"
|
||||
@ -741,15 +740,10 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
tt := symlinks.TargetFile
|
||||
if file.IsDirectory() {
|
||||
tt = symlinks.TargetDirectory
|
||||
}
|
||||
|
||||
// We declare a function that acts on only the path name, so
|
||||
// we can pass it to InWritableDir.
|
||||
createLink := func(path string) error {
|
||||
return symlinks.Create(path, file.SymlinkTarget, tt)
|
||||
return os.Symlink(file.SymlinkTarget, path)
|
||||
}
|
||||
|
||||
if err = osutil.InWritableDir(createLink, realName); err == nil {
|
||||
@ -1765,9 +1759,6 @@ func fileValid(file db.FileIntf) error {
|
||||
// We don't care about file validity if we're not supposed to have it
|
||||
return nil
|
||||
|
||||
case !symlinks.Supported && file.IsSymlink():
|
||||
return errUnsupportedSymlink
|
||||
|
||||
case runtime.GOOS == "windows" && windowsInvalidFilename(file.FileName()):
|
||||
return errInvalidFilename
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package osutil_test
|
||||
|
||||
import (
|
||||
@ -11,19 +13,13 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
)
|
||||
|
||||
func TestTraversesSymlink(t *testing.T) {
|
||||
if !symlinks.Supported {
|
||||
t.Skip("pointless test")
|
||||
return
|
||||
}
|
||||
|
||||
os.RemoveAll("testdata")
|
||||
defer os.RemoveAll("testdata")
|
||||
os.MkdirAll("testdata/a/b/c", 0755)
|
||||
symlinks.Create("testdata/a/l", "b", symlinks.TargetDirectory)
|
||||
os.Symlink("b", "testdata/a/l")
|
||||
|
||||
// a/l -> b, so a/l/c should resolve by normal stat
|
||||
info, err := osutil.Lstat("testdata/a/l/c")
|
||||
|
@ -134,11 +134,11 @@ func (Compression) EnumDescriptor() ([]byte, []int) { return fileDescriptorBep,
|
||||
type FileInfoType int32
|
||||
|
||||
const (
|
||||
FileInfoTypeFile FileInfoType = 0
|
||||
FileInfoTypeDirectory FileInfoType = 1
|
||||
FileInfoTypeSymlinkFile FileInfoType = 2
|
||||
FileInfoTypeSymlinkDirectory FileInfoType = 3
|
||||
FileInfoTypeSymlinkUnknown FileInfoType = 4
|
||||
FileInfoTypeFile FileInfoType = 0
|
||||
FileInfoTypeDirectory FileInfoType = 1
|
||||
FileInfoTypeDeprecatedSymlinkFile FileInfoType = 2
|
||||
FileInfoTypeDeprecatedSymlinkDirectory FileInfoType = 3
|
||||
FileInfoTypeSymlink FileInfoType = 4
|
||||
)
|
||||
|
||||
var FileInfoType_name = map[int32]string{
|
||||
@ -146,14 +146,14 @@ var FileInfoType_name = map[int32]string{
|
||||
1: "DIRECTORY",
|
||||
2: "SYMLINK_FILE",
|
||||
3: "SYMLINK_DIRECTORY",
|
||||
4: "SYMLINK_UNKNOWN",
|
||||
4: "SYMLINK",
|
||||
}
|
||||
var FileInfoType_value = map[string]int32{
|
||||
"FILE": 0,
|
||||
"DIRECTORY": 1,
|
||||
"SYMLINK_FILE": 2,
|
||||
"SYMLINK_DIRECTORY": 3,
|
||||
"SYMLINK_UNKNOWN": 4,
|
||||
"SYMLINK": 4,
|
||||
}
|
||||
|
||||
func (x FileInfoType) String() string {
|
||||
@ -4165,114 +4165,114 @@ var (
|
||||
func init() { proto.RegisterFile("bep.proto", fileDescriptorBep) }
|
||||
|
||||
var fileDescriptorBep = []byte{
|
||||
// 1732 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcd, 0x73, 0xdb, 0xc6,
|
||||
0x15, 0xe7, 0x07, 0xf8, 0xf5, 0x48, 0xc9, 0xd0, 0xda, 0x56, 0x50, 0x58, 0xa1, 0x10, 0x26, 0x6e,
|
||||
0x14, 0x4d, 0xe3, 0xb8, 0x71, 0xda, 0xcc, 0x74, 0xda, 0xce, 0xf0, 0x03, 0x92, 0x39, 0xa1, 0x41,
|
||||
0x76, 0x49, 0xd9, 0x75, 0x0e, 0xc5, 0x80, 0xc4, 0x92, 0xc2, 0x08, 0xc4, 0xb2, 0x00, 0x28, 0x9b,
|
||||
0xfd, 0x13, 0xf8, 0x17, 0xf4, 0xc2, 0x99, 0xcc, 0xf4, 0xd4, 0x7b, 0xff, 0x08, 0x1f, 0x33, 0x3d,
|
||||
0xf4, 0xd0, 0x83, 0xa7, 0x51, 0x2f, 0x3d, 0xf6, 0xd2, 0x53, 0x2f, 0x9d, 0xdd, 0x05, 0x48, 0x50,
|
||||
0x92, 0x3b, 0x39, 0xe4, 0x84, 0x7d, 0xef, 0xfd, 0xf6, 0xed, 0xbe, 0x8f, 0xdf, 0x5b, 0x40, 0x69,
|
||||
0x48, 0x66, 0x8f, 0x66, 0x3e, 0x0d, 0x29, 0x2a, 0xf2, 0xcf, 0x88, 0xba, 0xea, 0xa7, 0x13, 0x27,
|
||||
0x3c, 0x9f, 0x0f, 0x1f, 0x8d, 0xe8, 0xf4, 0xb3, 0x09, 0x9d, 0xd0, 0xcf, 0xb8, 0x65, 0x38, 0x1f,
|
||||
0x73, 0x89, 0x0b, 0x7c, 0x25, 0x36, 0xd6, 0x66, 0x90, 0x7b, 0x4a, 0x5c, 0x97, 0xa2, 0x43, 0x28,
|
||||
0xdb, 0xe4, 0xd2, 0x19, 0x11, 0xd3, 0xb3, 0xa6, 0x44, 0x49, 0x6b, 0xe9, 0xa3, 0x12, 0x06, 0xa1,
|
||||
0x32, 0xac, 0x29, 0x61, 0x80, 0x91, 0xeb, 0x10, 0x2f, 0x14, 0x80, 0x8c, 0x00, 0x08, 0x15, 0x07,
|
||||
0x3c, 0x84, 0xdd, 0x08, 0x70, 0x49, 0xfc, 0xc0, 0xa1, 0x9e, 0x92, 0xe5, 0x98, 0x1d, 0xa1, 0x7d,
|
||||
0x2e, 0x94, 0xb5, 0x00, 0xf2, 0x4f, 0x89, 0x65, 0x13, 0x1f, 0x7d, 0x02, 0x52, 0xb8, 0x98, 0x89,
|
||||
0xb3, 0x76, 0x3f, 0xbf, 0xff, 0x28, 0x8e, 0xe1, 0xd1, 0x33, 0x12, 0x04, 0xd6, 0x84, 0x0c, 0x16,
|
||||
0x33, 0x82, 0x39, 0x04, 0xfd, 0x1a, 0xca, 0x23, 0x3a, 0x9d, 0xf9, 0x24, 0xe0, 0x8e, 0x33, 0x7c,
|
||||
0xc7, 0xc1, 0x8d, 0x1d, 0xcd, 0x0d, 0x06, 0x27, 0x37, 0xd4, 0xea, 0xb0, 0xd3, 0x74, 0xe7, 0x41,
|
||||
0x48, 0xfc, 0x26, 0xf5, 0xc6, 0xce, 0x04, 0x3d, 0x86, 0xc2, 0x98, 0xba, 0x36, 0xf1, 0x03, 0x25,
|
||||
0xad, 0x65, 0x8f, 0xca, 0x9f, 0xcb, 0x1b, 0x67, 0x27, 0xdc, 0xd0, 0x90, 0xde, 0xbc, 0x3d, 0x4c,
|
||||
0xe1, 0x18, 0x56, 0xfb, 0x53, 0x06, 0xf2, 0xc2, 0x82, 0xf6, 0x21, 0xe3, 0xd8, 0x22, 0x45, 0x8d,
|
||||
0xfc, 0xd5, 0xdb, 0xc3, 0x4c, 0xbb, 0x85, 0x33, 0x8e, 0x8d, 0xee, 0x41, 0xce, 0xb5, 0x86, 0xc4,
|
||||
0x8d, 0x92, 0x23, 0x04, 0xf4, 0x00, 0x4a, 0x3e, 0xb1, 0x6c, 0x93, 0x7a, 0xee, 0x82, 0xa7, 0xa4,
|
||||
0x88, 0x8b, 0x4c, 0xd1, 0xf5, 0xdc, 0x05, 0xfa, 0x14, 0x90, 0x33, 0xf1, 0xa8, 0x4f, 0xcc, 0x19,
|
||||
0xf1, 0xa7, 0x0e, 0xbf, 0x6d, 0xa0, 0x48, 0x1c, 0xb5, 0x27, 0x2c, 0xbd, 0x8d, 0x01, 0x7d, 0x08,
|
||||
0x3b, 0x11, 0xdc, 0x26, 0x2e, 0x09, 0x89, 0x92, 0xe3, 0xc8, 0x8a, 0x50, 0xb6, 0xb8, 0x0e, 0x3d,
|
||||
0x86, 0x7b, 0xb6, 0x13, 0x58, 0x43, 0x97, 0x98, 0x21, 0x99, 0xce, 0x4c, 0xc7, 0xb3, 0xc9, 0x6b,
|
||||
0x12, 0x28, 0x79, 0x8e, 0x45, 0x91, 0x6d, 0x40, 0xa6, 0xb3, 0xb6, 0xb0, 0xa0, 0x7d, 0xc8, 0xcf,
|
||||
0xac, 0x79, 0x40, 0x6c, 0xa5, 0xc0, 0x31, 0x91, 0xc4, 0xb2, 0x24, 0x3a, 0x20, 0x50, 0xe4, 0xeb,
|
||||
0x59, 0x6a, 0x71, 0x43, 0x9c, 0xa5, 0x08, 0x56, 0xfb, 0x77, 0x06, 0xf2, 0xc2, 0x82, 0x7e, 0xbc,
|
||||
0xce, 0x52, 0xa5, 0xb1, 0xcf, 0x50, 0x7f, 0x7f, 0x7b, 0x58, 0x14, 0xb6, 0x76, 0x2b, 0x91, 0x35,
|
||||
0x04, 0x52, 0xa2, 0xa3, 0xf8, 0x1a, 0x1d, 0x40, 0xc9, 0xb2, 0x6d, 0x56, 0x3d, 0x12, 0x28, 0x59,
|
||||
0x2d, 0x7b, 0x54, 0xc2, 0x1b, 0x05, 0xfa, 0x72, 0xbb, 0x1b, 0xa4, 0xeb, 0xfd, 0xf3, 0xae, 0x36,
|
||||
0x60, 0xa5, 0x18, 0x11, 0x3f, 0xea, 0xe0, 0x1c, 0x3f, 0xaf, 0xc8, 0x14, 0xbc, 0x7f, 0x3f, 0x80,
|
||||
0xca, 0xd4, 0x7a, 0x6d, 0x06, 0xe4, 0xf7, 0x73, 0xe2, 0x8d, 0x08, 0x4f, 0x57, 0x16, 0x97, 0xa7,
|
||||
0xd6, 0xeb, 0x7e, 0xa4, 0x42, 0x55, 0x00, 0xc7, 0x0b, 0x7d, 0x6a, 0xcf, 0x47, 0xc4, 0x8f, 0x72,
|
||||
0x95, 0xd0, 0xa0, 0x9f, 0x41, 0x91, 0x27, 0xdb, 0x74, 0x6c, 0xa5, 0xa8, 0xa5, 0x8f, 0xa4, 0x86,
|
||||
0x1a, 0x05, 0x5e, 0xe0, 0xa9, 0xe6, 0x71, 0xc7, 0x4b, 0x5c, 0xe0, 0xd8, 0xb6, 0x8d, 0x7e, 0x09,
|
||||
0x6a, 0x70, 0xe1, 0xb0, 0x42, 0x09, 0x4f, 0xa1, 0x43, 0x3d, 0xd3, 0x27, 0x53, 0x7a, 0x69, 0xb9,
|
||||
0x81, 0x52, 0xe2, 0xc7, 0x28, 0x0c, 0xd1, 0x4e, 0x00, 0x70, 0x64, 0xaf, 0x75, 0x21, 0xc7, 0x3d,
|
||||
0xb2, 0x2a, 0x8a, 0x66, 0x8d, 0xd8, 0x1b, 0x49, 0xe8, 0x11, 0xe4, 0xc6, 0x8e, 0x4b, 0x02, 0x25,
|
||||
0xc3, 0x6b, 0x88, 0x12, 0x9d, 0xee, 0xb8, 0xa4, 0xed, 0x8d, 0x69, 0x54, 0x45, 0x01, 0xab, 0x9d,
|
||||
0x41, 0x99, 0x3b, 0x3c, 0x9b, 0xd9, 0x56, 0x48, 0x7e, 0x30, 0xb7, 0xff, 0xcd, 0x42, 0x31, 0xb6,
|
||||
0xac, 0x8b, 0x9e, 0x4e, 0x14, 0xfd, 0x38, 0x9a, 0x07, 0x82, 0xdd, 0xfb, 0x37, 0xfd, 0x25, 0x06,
|
||||
0x02, 0x02, 0x29, 0x70, 0xfe, 0x40, 0x38, 0x9f, 0xb2, 0x98, 0xaf, 0x91, 0x06, 0xe5, 0xeb, 0x24,
|
||||
0xda, 0xc1, 0x49, 0x15, 0x7a, 0x1f, 0x60, 0x4a, 0x6d, 0x67, 0xec, 0x10, 0xdb, 0x0c, 0x78, 0x03,
|
||||
0x64, 0x71, 0x29, 0xd6, 0xf4, 0x91, 0xc2, 0xda, 0x9d, 0x51, 0xc8, 0x8e, 0xb8, 0x12, 0x8b, 0xcc,
|
||||
0xe2, 0x78, 0x97, 0x96, 0xeb, 0xc4, 0x0c, 0x89, 0x45, 0x36, 0xf5, 0x3c, 0xba, 0x45, 0xde, 0x22,
|
||||
0x07, 0xec, 0x78, 0x34, 0x49, 0xdc, 0xc7, 0x50, 0x88, 0xa7, 0x22, 0xab, 0xe7, 0x16, 0x93, 0x9e,
|
||||
0x93, 0x51, 0x48, 0xd7, 0xf3, 0x26, 0x82, 0x21, 0x15, 0x8a, 0xeb, 0x56, 0x04, 0x7e, 0xd3, 0xb5,
|
||||
0xcc, 0x66, 0xf1, 0x3a, 0x0e, 0x2f, 0x50, 0xca, 0x5a, 0xfa, 0x28, 0x87, 0xd7, 0xa1, 0x19, 0xec,
|
||||
0xb8, 0x0d, 0x60, 0xb8, 0x50, 0x2a, 0xbc, 0x17, 0xef, 0xc4, 0xbd, 0xd8, 0x3f, 0xa7, 0x7e, 0xd8,
|
||||
0x6e, 0x6d, 0x76, 0x34, 0x16, 0xe8, 0xa7, 0x90, 0x6f, 0xb8, 0x74, 0x74, 0x11, 0x33, 0xfd, 0xee,
|
||||
0xe6, 0x7e, 0x5c, 0x9f, 0xa8, 0x67, 0x04, 0x64, 0xa1, 0x07, 0x8b, 0xa9, 0xeb, 0x78, 0x17, 0x66,
|
||||
0x68, 0xf9, 0x13, 0x12, 0x2a, 0x7b, 0x62, 0xe0, 0x47, 0xda, 0x01, 0x57, 0xfe, 0x42, 0xfa, 0xe3,
|
||||
0x37, 0x87, 0xa9, 0x9a, 0x07, 0xa5, 0xb5, 0x1f, 0xd6, 0x52, 0x74, 0x3c, 0x0e, 0x48, 0xc8, 0xeb,
|
||||
0x9f, 0xc5, 0x91, 0xb4, 0xae, 0x6a, 0x86, 0x07, 0x24, 0xaa, 0x8a, 0x40, 0x3a, 0xb7, 0x82, 0x73,
|
||||
0x5e, 0xe9, 0x0a, 0xe6, 0x6b, 0xc6, 0xe3, 0x57, 0xc4, 0xba, 0x30, 0xb9, 0x41, 0xd4, 0xb9, 0xc8,
|
||||
0x14, 0x4f, 0xad, 0xe0, 0x3c, 0x3a, 0xef, 0x57, 0x90, 0x17, 0x79, 0x45, 0x4f, 0xa0, 0x38, 0xa2,
|
||||
0x73, 0x2f, 0xdc, 0xcc, 0xfa, 0xbd, 0xe4, 0xa8, 0xe0, 0x96, 0x28, 0xb2, 0x35, 0xb0, 0x76, 0x02,
|
||||
0x85, 0xc8, 0x84, 0x1e, 0xae, 0xe7, 0x98, 0xd4, 0xb8, 0x7f, 0x2d, 0x85, 0xdb, 0xc3, 0xff, 0xd2,
|
||||
0x72, 0xe7, 0xe2, 0xf2, 0x12, 0x16, 0x42, 0xed, 0x2f, 0x69, 0x28, 0x60, 0x56, 0xb6, 0x20, 0x4c,
|
||||
0x3c, 0x1b, 0xb9, 0xad, 0x67, 0x63, 0x43, 0xb0, 0xcc, 0x16, 0xc1, 0x62, 0x8e, 0x64, 0x13, 0x1c,
|
||||
0xd9, 0x64, 0x4e, 0xba, 0x35, 0x73, 0xb9, 0x5b, 0x32, 0x97, 0x4f, 0x64, 0xee, 0x21, 0xec, 0x8e,
|
||||
0x7d, 0x3a, 0xe5, 0x0f, 0x03, 0xf5, 0x2d, 0x7f, 0x11, 0xf5, 0xf3, 0x0e, 0xd3, 0x0e, 0x62, 0x65,
|
||||
0xcd, 0x84, 0x22, 0x26, 0xc1, 0x8c, 0x7a, 0x01, 0x79, 0xe7, 0xb5, 0x11, 0x48, 0xb6, 0x15, 0x5a,
|
||||
0xfc, 0xd2, 0x15, 0xcc, 0xd7, 0xe8, 0x63, 0x90, 0x46, 0xd4, 0x16, 0x57, 0xde, 0x4d, 0xf6, 0x90,
|
||||
0xee, 0xfb, 0xd4, 0x6f, 0x52, 0x9b, 0x60, 0x0e, 0xa8, 0xcd, 0x40, 0x6e, 0xd1, 0x57, 0x9e, 0x4b,
|
||||
0x2d, 0xbb, 0xe7, 0xd3, 0x09, 0x1b, 0xd0, 0xef, 0x1c, 0x34, 0x2d, 0x28, 0xcc, 0xf9, 0x28, 0x8a,
|
||||
0x47, 0xcd, 0x47, 0xdb, 0xa3, 0xe1, 0xba, 0x23, 0x31, 0xb7, 0x62, 0x3e, 0x45, 0x5b, 0x6b, 0x7f,
|
||||
0x4b, 0x83, 0xfa, 0x6e, 0x34, 0x6a, 0x43, 0x59, 0x20, 0xcd, 0xc4, 0x3f, 0xc9, 0xd1, 0xf7, 0x39,
|
||||
0x88, 0x4f, 0x25, 0x98, 0xaf, 0xd7, 0xb7, 0x3e, 0x68, 0x09, 0xfe, 0x67, 0xbf, 0x1f, 0xff, 0x3f,
|
||||
0x86, 0x9d, 0x21, 0x23, 0xcc, 0xfa, 0xf9, 0x96, 0xb4, 0xec, 0x51, 0xae, 0x91, 0x91, 0x53, 0xb8,
|
||||
0x32, 0x14, 0x4c, 0xe2, 0xfa, 0x5a, 0x1e, 0xa4, 0x9e, 0xe3, 0x4d, 0x6a, 0x87, 0x90, 0x6b, 0xba,
|
||||
0x94, 0x17, 0x2c, 0xef, 0x13, 0x2b, 0xa0, 0x5e, 0x9c, 0x47, 0x21, 0x1d, 0xff, 0x35, 0x03, 0xe5,
|
||||
0xc4, 0xaf, 0x15, 0x7a, 0x0c, 0xbb, 0xcd, 0xce, 0x59, 0x7f, 0xa0, 0x63, 0xb3, 0xd9, 0x35, 0x4e,
|
||||
0xda, 0xa7, 0x72, 0x4a, 0x3d, 0x58, 0xae, 0x34, 0x65, 0xba, 0x01, 0x6d, 0xff, 0x35, 0x1d, 0x42,
|
||||
0xae, 0x6d, 0xb4, 0xf4, 0xdf, 0xca, 0x69, 0xf5, 0xde, 0x72, 0xa5, 0xc9, 0x09, 0xa0, 0x78, 0x82,
|
||||
0x7e, 0x02, 0x15, 0x0e, 0x30, 0xcf, 0x7a, 0xad, 0xfa, 0x40, 0x97, 0x33, 0xaa, 0xba, 0x5c, 0x69,
|
||||
0xfb, 0xd7, 0x71, 0x51, 0xce, 0x3f, 0x84, 0x02, 0xd6, 0x7f, 0x73, 0xa6, 0xf7, 0x07, 0x72, 0x56,
|
||||
0xdd, 0x5f, 0xae, 0x34, 0x94, 0x00, 0xc6, 0xac, 0x79, 0x08, 0x45, 0xac, 0xf7, 0x7b, 0x5d, 0xa3,
|
||||
0xaf, 0xcb, 0x92, 0xfa, 0xde, 0x72, 0xa5, 0xdd, 0xdd, 0x42, 0x45, 0x5d, 0xfa, 0x73, 0xd8, 0x6b,
|
||||
0x75, 0x5f, 0x18, 0x9d, 0x6e, 0xbd, 0x65, 0xf6, 0x70, 0xf7, 0x14, 0xeb, 0xfd, 0xbe, 0x9c, 0x53,
|
||||
0x0f, 0x97, 0x2b, 0xed, 0x41, 0x02, 0x7f, 0xa3, 0xe9, 0xde, 0x07, 0xa9, 0xd7, 0x36, 0x4e, 0xe5,
|
||||
0xbc, 0x7a, 0x77, 0xb9, 0xd2, 0xee, 0x24, 0xa0, 0x2c, 0xa9, 0x2c, 0xe2, 0x66, 0xa7, 0xdb, 0xd7,
|
||||
0xe5, 0xc2, 0x8d, 0x88, 0x79, 0xb2, 0x8f, 0x7f, 0x07, 0xe8, 0xe6, 0xcf, 0x27, 0xfa, 0x08, 0x24,
|
||||
0xa3, 0x6b, 0xe8, 0x72, 0x4a, 0xc4, 0x7f, 0x13, 0x61, 0x50, 0x8f, 0xa0, 0x1a, 0x64, 0x3b, 0x5f,
|
||||
0x7f, 0x21, 0xa7, 0xd5, 0x1f, 0x2d, 0x57, 0xda, 0xfd, 0x9b, 0xa0, 0xce, 0xd7, 0x5f, 0x1c, 0x53,
|
||||
0x28, 0x27, 0x1d, 0xd7, 0xa0, 0xf8, 0x4c, 0x1f, 0xd4, 0x5b, 0xf5, 0x41, 0x5d, 0x4e, 0x89, 0x2b,
|
||||
0xc5, 0xe6, 0x67, 0x24, 0xb4, 0x38, 0x09, 0x0f, 0x20, 0x67, 0xe8, 0xcf, 0x75, 0x2c, 0xa7, 0xd5,
|
||||
0xbd, 0xe5, 0x4a, 0xdb, 0x89, 0x01, 0x06, 0xb9, 0x24, 0x3e, 0xaa, 0x42, 0xbe, 0xde, 0x79, 0x51,
|
||||
0x7f, 0xd9, 0x97, 0x33, 0x2a, 0x5a, 0xae, 0xb4, 0xdd, 0xd8, 0x5c, 0x77, 0x5f, 0x59, 0x8b, 0xe0,
|
||||
0xf8, 0x3f, 0x69, 0xa8, 0x24, 0x1f, 0x5c, 0x54, 0x05, 0xe9, 0xa4, 0xdd, 0xd1, 0xe3, 0xe3, 0x92,
|
||||
0x36, 0xb6, 0x46, 0x47, 0x50, 0x6a, 0xb5, 0xb1, 0xde, 0x1c, 0x74, 0xf1, 0xcb, 0x38, 0x96, 0x24,
|
||||
0xa8, 0xe5, 0xf8, 0xbc, 0xc1, 0xd9, 0xcf, 0x6e, 0xa5, 0xff, 0xf2, 0x59, 0xa7, 0x6d, 0x7c, 0x65,
|
||||
0x72, 0x8f, 0x19, 0xf5, 0xc1, 0x72, 0xa5, 0xbd, 0x97, 0x04, 0xf7, 0xc5, 0xd3, 0xc1, 0x1d, 0x7f,
|
||||
0x09, 0x7b, 0x31, 0x7c, 0x73, 0x40, 0x56, 0xd5, 0x96, 0x2b, 0xed, 0xe0, 0x96, 0x3d, 0x9b, 0x73,
|
||||
0x9e, 0xc0, 0x9d, 0x78, 0xe3, 0x99, 0xf1, 0x95, 0xd1, 0x7d, 0x61, 0xc8, 0x92, 0x5a, 0x5d, 0xae,
|
||||
0x34, 0xf5, 0x96, 0x6d, 0x67, 0xde, 0x85, 0x47, 0x5f, 0x79, 0xc7, 0x7f, 0x4e, 0x43, 0x69, 0x3d,
|
||||
0xa5, 0x58, 0x9e, 0x8d, 0xae, 0xa9, 0x63, 0xdc, 0xc5, 0x71, 0xe0, 0x6b, 0xa3, 0x41, 0xf9, 0x12,
|
||||
0x7d, 0x00, 0x85, 0x53, 0xdd, 0xd0, 0x71, 0xbb, 0x19, 0xf3, 0x61, 0x0d, 0x39, 0x25, 0x1e, 0xf1,
|
||||
0x9d, 0x11, 0xfa, 0x04, 0x2a, 0x46, 0xd7, 0xec, 0x9f, 0x35, 0x9f, 0xc6, 0x11, 0xf3, 0x06, 0x4e,
|
||||
0xb8, 0xea, 0xcf, 0x47, 0xe7, 0x3c, 0xda, 0x63, 0x46, 0x9d, 0xe7, 0xf5, 0x4e, 0xbb, 0x25, 0xa0,
|
||||
0x59, 0x55, 0x59, 0xae, 0xb4, 0x7b, 0x6b, 0x68, 0x5b, 0xfc, 0x70, 0x30, 0xec, 0xb1, 0x0d, 0xd5,
|
||||
0xff, 0x3f, 0x8f, 0x90, 0x06, 0xf9, 0x7a, 0xaf, 0xa7, 0x1b, 0xad, 0xf8, 0xf6, 0x1b, 0x5b, 0x7d,
|
||||
0x36, 0x23, 0x9e, 0xcd, 0x10, 0x27, 0x5d, 0x7c, 0xaa, 0x0f, 0xe2, 0xcb, 0x6f, 0x10, 0x27, 0x94,
|
||||
0x3d, 0xdc, 0x8d, 0x83, 0x37, 0xdf, 0x55, 0x53, 0xdf, 0x7e, 0x57, 0x4d, 0xbd, 0xb9, 0xaa, 0xa6,
|
||||
0xbf, 0xbd, 0xaa, 0xa6, 0xff, 0x71, 0x55, 0x4d, 0xfd, 0xeb, 0xaa, 0x9a, 0xfe, 0xe6, 0x9f, 0xd5,
|
||||
0xf4, 0x30, 0xcf, 0xe7, 0xd7, 0x93, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xeb, 0xb3, 0x61, 0xe0,
|
||||
0x86, 0x0e, 0x00, 0x00,
|
||||
// 1729 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x8f, 0xdb, 0xc6,
|
||||
0x15, 0x17, 0x25, 0xea, 0xdf, 0x93, 0x76, 0xc3, 0x1d, 0xdb, 0x5b, 0x96, 0xd9, 0x68, 0x69, 0xc5,
|
||||
0x8e, 0x37, 0x8b, 0x64, 0xe3, 0x26, 0x69, 0x8b, 0x16, 0x6d, 0x01, 0xfd, 0xe1, 0xae, 0x85, 0xca,
|
||||
0x94, 0x3a, 0xd2, 0x3a, 0x75, 0x0e, 0x25, 0x28, 0x71, 0xa4, 0x25, 0x4c, 0x71, 0x54, 0x92, 0x5a,
|
||||
0x5b, 0xfd, 0x08, 0xfa, 0x04, 0xbd, 0x08, 0x08, 0xd0, 0x53, 0xef, 0xfd, 0x10, 0x3e, 0x06, 0x3d,
|
||||
0xf4, 0xd0, 0x83, 0xd1, 0x6c, 0x2f, 0x3d, 0xf6, 0x5e, 0xa0, 0x28, 0x38, 0x43, 0x52, 0xd4, 0xae,
|
||||
0x37, 0xf0, 0xa1, 0x27, 0xce, 0xbc, 0xf7, 0x9b, 0x3f, 0xef, 0xf7, 0xde, 0xef, 0x0d, 0xa1, 0x3c,
|
||||
0x22, 0xf3, 0x93, 0xb9, 0x47, 0x03, 0x8a, 0x4a, 0xec, 0x33, 0xa6, 0x8e, 0xf2, 0xe9, 0xd4, 0x0e,
|
||||
0x2e, 0x16, 0xa3, 0x93, 0x31, 0x9d, 0x7d, 0x36, 0xa5, 0x53, 0xfa, 0x19, 0xf3, 0x8c, 0x16, 0x13,
|
||||
0x36, 0x63, 0x13, 0x36, 0xe2, 0x0b, 0xeb, 0x73, 0xc8, 0x3f, 0x21, 0x8e, 0x43, 0xd1, 0x21, 0x54,
|
||||
0x2c, 0x72, 0x69, 0x8f, 0x89, 0xe1, 0x9a, 0x33, 0x22, 0x0b, 0xaa, 0x70, 0x54, 0xc6, 0xc0, 0x4d,
|
||||
0xba, 0x39, 0x23, 0x21, 0x60, 0xec, 0xd8, 0xc4, 0x0d, 0x38, 0x20, 0xcb, 0x01, 0xdc, 0xc4, 0x00,
|
||||
0x0f, 0x61, 0x37, 0x02, 0x5c, 0x12, 0xcf, 0xb7, 0xa9, 0x2b, 0xe7, 0x18, 0x66, 0x87, 0x5b, 0x9f,
|
||||
0x71, 0x63, 0xdd, 0x87, 0xc2, 0x13, 0x62, 0x5a, 0xc4, 0x43, 0x1f, 0x83, 0x18, 0x2c, 0xe7, 0xfc,
|
||||
0xac, 0xdd, 0xcf, 0xef, 0x9d, 0xc4, 0x31, 0x9c, 0x3c, 0x25, 0xbe, 0x6f, 0x4e, 0xc9, 0x70, 0x39,
|
||||
0x27, 0x98, 0x41, 0xd0, 0xaf, 0xa0, 0x32, 0xa6, 0xb3, 0xb9, 0x47, 0x7c, 0xb6, 0x71, 0x96, 0xad,
|
||||
0x38, 0xb8, 0xb1, 0xa2, 0xb5, 0xc1, 0xe0, 0xf4, 0x82, 0x7a, 0x03, 0x76, 0x5a, 0xce, 0xc2, 0x0f,
|
||||
0x88, 0xd7, 0xa2, 0xee, 0xc4, 0x9e, 0xa2, 0xc7, 0x50, 0x9c, 0x50, 0xc7, 0x22, 0x9e, 0x2f, 0x0b,
|
||||
0x6a, 0xee, 0xa8, 0xf2, 0xb9, 0xb4, 0xd9, 0xec, 0x94, 0x39, 0x9a, 0xe2, 0xeb, 0x37, 0x87, 0x19,
|
||||
0x1c, 0xc3, 0xea, 0x7f, 0xca, 0x42, 0x81, 0x7b, 0xd0, 0x3e, 0x64, 0x6d, 0x8b, 0x53, 0xd4, 0x2c,
|
||||
0x5c, 0xbd, 0x39, 0xcc, 0x76, 0xda, 0x38, 0x6b, 0x5b, 0xe8, 0x2e, 0xe4, 0x1d, 0x73, 0x44, 0x9c,
|
||||
0x88, 0x1c, 0x3e, 0x41, 0xef, 0x43, 0xd9, 0x23, 0xa6, 0x65, 0x50, 0xd7, 0x59, 0x32, 0x4a, 0x4a,
|
||||
0xb8, 0x14, 0x1a, 0x7a, 0xae, 0xb3, 0x44, 0x9f, 0x02, 0xb2, 0xa7, 0x2e, 0xf5, 0x88, 0x31, 0x27,
|
||||
0xde, 0xcc, 0x66, 0xb7, 0xf5, 0x65, 0x91, 0xa1, 0xf6, 0xb8, 0xa7, 0xbf, 0x71, 0xa0, 0x0f, 0x61,
|
||||
0x27, 0x82, 0x5b, 0xc4, 0x21, 0x01, 0x91, 0xf3, 0x0c, 0x59, 0xe5, 0xc6, 0x36, 0xb3, 0xa1, 0xc7,
|
||||
0x70, 0xd7, 0xb2, 0x7d, 0x73, 0xe4, 0x10, 0x23, 0x20, 0xb3, 0xb9, 0x61, 0xbb, 0x16, 0x79, 0x45,
|
||||
0x7c, 0xb9, 0xc0, 0xb0, 0x28, 0xf2, 0x0d, 0xc9, 0x6c, 0xde, 0xe1, 0x1e, 0xb4, 0x0f, 0x85, 0xb9,
|
||||
0xb9, 0xf0, 0x89, 0x25, 0x17, 0x19, 0x26, 0x9a, 0x85, 0x2c, 0xf1, 0x0a, 0xf0, 0x65, 0xe9, 0x3a,
|
||||
0x4b, 0x6d, 0xe6, 0x88, 0x59, 0x8a, 0x60, 0xf5, 0x7f, 0x67, 0xa1, 0xc0, 0x3d, 0xe8, 0xa3, 0x84,
|
||||
0xa5, 0x6a, 0x73, 0x3f, 0x44, 0xfd, 0xfd, 0xcd, 0x61, 0x89, 0xfb, 0x3a, 0xed, 0x14, 0x6b, 0x08,
|
||||
0xc4, 0x54, 0x45, 0xb1, 0x31, 0x3a, 0x80, 0xb2, 0x69, 0x59, 0x61, 0xf6, 0x88, 0x2f, 0xe7, 0xd4,
|
||||
0xdc, 0x51, 0x19, 0x6f, 0x0c, 0xe8, 0xa7, 0xdb, 0xd5, 0x20, 0x5e, 0xaf, 0x9f, 0xdb, 0xca, 0x20,
|
||||
0x4c, 0xc5, 0x98, 0x78, 0x51, 0x05, 0xe7, 0xd9, 0x79, 0xa5, 0xd0, 0xc0, 0xea, 0xf7, 0x3e, 0x54,
|
||||
0x67, 0xe6, 0x2b, 0xc3, 0x27, 0xbf, 0x5f, 0x10, 0x77, 0x4c, 0x18, 0x5d, 0x39, 0x5c, 0x99, 0x99,
|
||||
0xaf, 0x06, 0x91, 0x09, 0xd5, 0x00, 0x6c, 0x37, 0xf0, 0xa8, 0xb5, 0x18, 0x13, 0x2f, 0xe2, 0x2a,
|
||||
0x65, 0x41, 0x3f, 0x86, 0x12, 0x23, 0xdb, 0xb0, 0x2d, 0xb9, 0xa4, 0x0a, 0x47, 0x62, 0x53, 0x89,
|
||||
0x02, 0x2f, 0x32, 0xaa, 0x59, 0xdc, 0xf1, 0x10, 0x17, 0x19, 0xb6, 0x63, 0xa1, 0x5f, 0x80, 0xe2,
|
||||
0xbf, 0xb0, 0xc3, 0x44, 0xf1, 0x9d, 0x02, 0x9b, 0xba, 0x86, 0x47, 0x66, 0xf4, 0xd2, 0x74, 0x7c,
|
||||
0xb9, 0xcc, 0x8e, 0x91, 0x43, 0x44, 0x27, 0x05, 0xc0, 0x91, 0xbf, 0xde, 0x83, 0x3c, 0xdb, 0x31,
|
||||
0xcc, 0x22, 0x2f, 0xd6, 0x48, 0xbd, 0xd1, 0x0c, 0x9d, 0x40, 0x7e, 0x62, 0x3b, 0xc4, 0x97, 0xb3,
|
||||
0x2c, 0x87, 0x28, 0x55, 0xe9, 0xb6, 0x43, 0x3a, 0xee, 0x84, 0x46, 0x59, 0xe4, 0xb0, 0xfa, 0x39,
|
||||
0x54, 0xd8, 0x86, 0xe7, 0x73, 0xcb, 0x0c, 0xc8, 0xff, 0x6d, 0xdb, 0xff, 0xe4, 0xa0, 0x14, 0x7b,
|
||||
0x92, 0xa4, 0x0b, 0xa9, 0xa4, 0x1f, 0x47, 0xfd, 0x80, 0xab, 0x7b, 0xff, 0xe6, 0x7e, 0xa9, 0x86,
|
||||
0x80, 0x40, 0xf4, 0xed, 0x3f, 0x10, 0xa6, 0xa7, 0x1c, 0x66, 0x63, 0xa4, 0x42, 0xe5, 0xba, 0x88,
|
||||
0x76, 0x70, 0xda, 0x84, 0x3e, 0x00, 0x98, 0x51, 0xcb, 0x9e, 0xd8, 0xc4, 0x32, 0x7c, 0x56, 0x00,
|
||||
0x39, 0x5c, 0x8e, 0x2d, 0x03, 0x24, 0x87, 0xe5, 0x1e, 0x4a, 0xc8, 0x8a, 0xb4, 0x12, 0x4f, 0x43,
|
||||
0x8f, 0xed, 0x5e, 0x9a, 0x8e, 0x1d, 0x2b, 0x24, 0x9e, 0x86, 0x5d, 0xcf, 0xa5, 0x5b, 0xe2, 0x2d,
|
||||
0x31, 0xc0, 0x8e, 0x4b, 0xd3, 0xc2, 0x7d, 0x0c, 0xc5, 0xb8, 0x2b, 0x86, 0xf9, 0xdc, 0x52, 0xd2,
|
||||
0x33, 0x32, 0x0e, 0x68, 0xd2, 0x6f, 0x22, 0x18, 0x52, 0xa0, 0x94, 0x94, 0x22, 0xb0, 0x9b, 0x26,
|
||||
0xf3, 0xb0, 0x17, 0x27, 0x71, 0xb8, 0xbe, 0x5c, 0x51, 0x85, 0xa3, 0x3c, 0x4e, 0x42, 0xd3, 0xc3,
|
||||
0xe3, 0x36, 0x80, 0xd1, 0x52, 0xae, 0xb2, 0x5a, 0x7c, 0x2f, 0xae, 0xc5, 0xc1, 0x05, 0xf5, 0x82,
|
||||
0x4e, 0x7b, 0xb3, 0xa2, 0xb9, 0x44, 0x3f, 0x82, 0x42, 0xd3, 0xa1, 0xe3, 0x17, 0xb1, 0xd2, 0xef,
|
||||
0x6c, 0xee, 0xc7, 0xec, 0xa9, 0x7c, 0x46, 0xc0, 0x30, 0x74, 0x7f, 0x39, 0x73, 0x6c, 0xf7, 0x85,
|
||||
0x11, 0x98, 0xde, 0x94, 0x04, 0xf2, 0x1e, 0x6f, 0xf8, 0x91, 0x75, 0xc8, 0x8c, 0x3f, 0x17, 0xff,
|
||||
0xf8, 0xcd, 0x61, 0xa6, 0xee, 0x42, 0x39, 0xd9, 0x27, 0x2c, 0x29, 0x3a, 0x99, 0xf8, 0x24, 0x60,
|
||||
0xf9, 0xcf, 0xe1, 0x68, 0x96, 0x64, 0x35, 0xcb, 0x02, 0xe2, 0x59, 0x45, 0x20, 0x5e, 0x98, 0xfe,
|
||||
0x05, 0xcb, 0x74, 0x15, 0xb3, 0x71, 0xa8, 0xe3, 0x97, 0xc4, 0x7c, 0x61, 0x30, 0x07, 0xcf, 0x73,
|
||||
0x29, 0x34, 0x3c, 0x31, 0xfd, 0x8b, 0xe8, 0xbc, 0x5f, 0x42, 0x81, 0xf3, 0x8a, 0xbe, 0x80, 0xd2,
|
||||
0x98, 0x2e, 0xdc, 0x60, 0xd3, 0xeb, 0xf7, 0xd2, 0xad, 0x82, 0x79, 0xa2, 0xc8, 0x12, 0x60, 0xfd,
|
||||
0x14, 0x8a, 0x91, 0x0b, 0x3d, 0x4c, 0xfa, 0x98, 0xd8, 0xbc, 0x77, 0x8d, 0xc2, 0xed, 0xe6, 0x7f,
|
||||
0x69, 0x3a, 0x0b, 0x7e, 0x79, 0x11, 0xf3, 0x49, 0xfd, 0x2f, 0x02, 0x14, 0x71, 0x98, 0x36, 0x3f,
|
||||
0x48, 0x3d, 0x1b, 0xf9, 0xad, 0x67, 0x63, 0x23, 0xb0, 0xec, 0x96, 0xc0, 0x62, 0x8d, 0xe4, 0x52,
|
||||
0x1a, 0xd9, 0x30, 0x27, 0xbe, 0x95, 0xb9, 0xfc, 0x5b, 0x98, 0x2b, 0xa4, 0x98, 0x7b, 0x08, 0xbb,
|
||||
0x13, 0x8f, 0xce, 0xd8, 0xc3, 0x40, 0x3d, 0xd3, 0x5b, 0x46, 0xf5, 0xbc, 0x13, 0x5a, 0x87, 0xb1,
|
||||
0xb1, 0x6e, 0x40, 0x09, 0x13, 0x7f, 0x4e, 0x5d, 0x9f, 0xdc, 0x7a, 0x6d, 0x04, 0xa2, 0x65, 0x06,
|
||||
0x26, 0xbb, 0x74, 0x15, 0xb3, 0x31, 0x7a, 0x04, 0xe2, 0x98, 0x5a, 0xfc, 0xca, 0xbb, 0xe9, 0x1a,
|
||||
0xd2, 0x3c, 0x8f, 0x7a, 0x2d, 0x6a, 0x11, 0xcc, 0x00, 0xf5, 0x39, 0x48, 0x6d, 0xfa, 0xd2, 0x75,
|
||||
0xa8, 0x69, 0xf5, 0x3d, 0x3a, 0x0d, 0x1b, 0xf4, 0xad, 0x8d, 0xa6, 0x0d, 0xc5, 0x05, 0x6b, 0x45,
|
||||
0x71, 0xab, 0x79, 0xb0, 0xdd, 0x1a, 0xae, 0x6f, 0xc4, 0xfb, 0x56, 0xac, 0xa7, 0x68, 0x69, 0xfd,
|
||||
0x6f, 0x02, 0x28, 0xb7, 0xa3, 0x51, 0x07, 0x2a, 0x1c, 0x69, 0xa4, 0xfe, 0x49, 0x8e, 0xde, 0xe5,
|
||||
0x20, 0xd6, 0x95, 0x60, 0x91, 0x8c, 0xdf, 0xfa, 0xa0, 0xa5, 0xf4, 0x9f, 0x7b, 0x37, 0xfd, 0x3f,
|
||||
0x82, 0x9d, 0x51, 0x28, 0x98, 0xe4, 0xf9, 0x16, 0xd5, 0xdc, 0x51, 0xbe, 0x99, 0x95, 0x32, 0xb8,
|
||||
0x3a, 0xe2, 0x4a, 0x62, 0xf6, 0x7a, 0x01, 0xc4, 0xbe, 0xed, 0x4e, 0xeb, 0x87, 0x90, 0x6f, 0x39,
|
||||
0x94, 0x25, 0xac, 0xe0, 0x11, 0xd3, 0xa7, 0x6e, 0xcc, 0x23, 0x9f, 0x1d, 0xff, 0x35, 0x0b, 0x95,
|
||||
0xd4, 0xaf, 0x15, 0x7a, 0x0c, 0xbb, 0xad, 0xee, 0xf9, 0x60, 0xa8, 0x61, 0xa3, 0xd5, 0xd3, 0x4f,
|
||||
0x3b, 0x67, 0x52, 0x46, 0x39, 0x58, 0xad, 0x55, 0x79, 0xb6, 0x01, 0x6d, 0xff, 0x35, 0x1d, 0x42,
|
||||
0xbe, 0xa3, 0xb7, 0xb5, 0xdf, 0x4a, 0x82, 0x72, 0x77, 0xb5, 0x56, 0xa5, 0x14, 0x90, 0x3f, 0x41,
|
||||
0x9f, 0x40, 0x95, 0x01, 0x8c, 0xf3, 0x7e, 0xbb, 0x31, 0xd4, 0xa4, 0xac, 0xa2, 0xac, 0xd6, 0xea,
|
||||
0xfe, 0x75, 0x5c, 0xc4, 0xf9, 0x87, 0x50, 0xc4, 0xda, 0x6f, 0xce, 0xb5, 0xc1, 0x50, 0xca, 0x29,
|
||||
0xfb, 0xab, 0xb5, 0x8a, 0x52, 0xc0, 0x58, 0x35, 0x0f, 0xa1, 0x84, 0xb5, 0x41, 0xbf, 0xa7, 0x0f,
|
||||
0x34, 0x49, 0x54, 0x7e, 0xb0, 0x5a, 0xab, 0x77, 0xb6, 0x50, 0x51, 0x95, 0xfe, 0x04, 0xf6, 0xda,
|
||||
0xbd, 0xaf, 0xf4, 0x6e, 0xaf, 0xd1, 0x36, 0xfa, 0xb8, 0x77, 0x86, 0xb5, 0xc1, 0x40, 0xca, 0x2b,
|
||||
0x87, 0xab, 0xb5, 0xfa, 0x7e, 0x0a, 0x7f, 0xa3, 0xe8, 0x3e, 0x00, 0xb1, 0xdf, 0xd1, 0xcf, 0xa4,
|
||||
0x82, 0x72, 0x67, 0xb5, 0x56, 0xdf, 0x4b, 0x41, 0x43, 0x52, 0xc3, 0x88, 0x5b, 0xdd, 0xde, 0x40,
|
||||
0x93, 0x8a, 0x37, 0x22, 0x66, 0x64, 0x1f, 0xff, 0x0e, 0xd0, 0xcd, 0x9f, 0x4f, 0xf4, 0x00, 0x44,
|
||||
0xbd, 0xa7, 0x6b, 0x52, 0x86, 0xc7, 0x7f, 0x13, 0xa1, 0x53, 0x97, 0xa0, 0x3a, 0xe4, 0xba, 0x5f,
|
||||
0x7f, 0x29, 0x09, 0xca, 0x0f, 0x57, 0x6b, 0xf5, 0xde, 0x4d, 0x50, 0xf7, 0xeb, 0x2f, 0x8f, 0x29,
|
||||
0x54, 0xd2, 0x1b, 0xd7, 0xa1, 0xf4, 0x54, 0x1b, 0x36, 0xda, 0x8d, 0x61, 0x43, 0xca, 0xf0, 0x2b,
|
||||
0xc5, 0xee, 0xa7, 0x24, 0x30, 0x99, 0x08, 0x0f, 0x20, 0xaf, 0x6b, 0xcf, 0x34, 0x2c, 0x09, 0xca,
|
||||
0xde, 0x6a, 0xad, 0xee, 0xc4, 0x00, 0x9d, 0x5c, 0x12, 0x0f, 0xd5, 0xa0, 0xd0, 0xe8, 0x7e, 0xd5,
|
||||
0x78, 0x3e, 0x90, 0xb2, 0x0a, 0x5a, 0xad, 0xd5, 0xdd, 0xd8, 0xdd, 0x70, 0x5e, 0x9a, 0x4b, 0xff,
|
||||
0xf8, 0xbf, 0x02, 0x54, 0xd3, 0x0f, 0x2e, 0xaa, 0x81, 0x78, 0xda, 0xe9, 0x6a, 0xf1, 0x71, 0x69,
|
||||
0x5f, 0x38, 0x46, 0x47, 0x50, 0x6e, 0x77, 0xb0, 0xd6, 0x1a, 0xf6, 0xf0, 0xf3, 0x38, 0x96, 0x34,
|
||||
0xa8, 0x6d, 0x7b, 0xac, 0xc0, 0x97, 0xe8, 0x67, 0x50, 0x1d, 0x3c, 0x7f, 0xda, 0xed, 0xe8, 0xbf,
|
||||
0x36, 0xd8, 0x8e, 0x59, 0xe5, 0xd1, 0x6a, 0xad, 0xde, 0xdf, 0x02, 0x93, 0xb9, 0x47, 0xc6, 0x66,
|
||||
0x40, 0xac, 0x01, 0x7f, 0x44, 0x42, 0x67, 0x49, 0x40, 0x2d, 0xd8, 0x8b, 0x97, 0x6e, 0x0e, 0xcb,
|
||||
0x29, 0x9f, 0xac, 0xd6, 0xea, 0x47, 0xdf, 0xbb, 0x3e, 0x39, 0xbd, 0x24, 0xa0, 0x07, 0x50, 0x8c,
|
||||
0x36, 0x89, 0x2b, 0x29, 0xbd, 0x34, 0x5a, 0x70, 0xfc, 0x67, 0x01, 0xca, 0x49, 0xbb, 0x0a, 0x09,
|
||||
0xd7, 0x7b, 0x86, 0x86, 0x71, 0x0f, 0xc7, 0x0c, 0x24, 0x4e, 0x9d, 0xb2, 0x21, 0xba, 0x0f, 0xc5,
|
||||
0x33, 0x4d, 0xd7, 0x70, 0xa7, 0x15, 0x0b, 0x23, 0x81, 0x9c, 0x11, 0x97, 0x78, 0xf6, 0x18, 0x7d,
|
||||
0x0c, 0x55, 0xbd, 0x67, 0x0c, 0xce, 0x5b, 0x4f, 0xe2, 0xd0, 0xd9, 0xf9, 0xa9, 0xad, 0x06, 0x8b,
|
||||
0xf1, 0x05, 0xe3, 0xf3, 0x38, 0xd4, 0xd0, 0xb3, 0x46, 0xb7, 0xd3, 0xe6, 0xd0, 0x9c, 0x22, 0xaf,
|
||||
0xd6, 0xea, 0xdd, 0x04, 0xda, 0xe1, 0x7f, 0x1e, 0x21, 0xf6, 0xd8, 0x82, 0xda, 0xf7, 0x37, 0x26,
|
||||
0xa4, 0x42, 0xa1, 0xd1, 0xef, 0x6b, 0x7a, 0x3b, 0xbe, 0xfd, 0xc6, 0xd7, 0x98, 0xcf, 0x89, 0x6b,
|
||||
0x85, 0x88, 0xd3, 0x1e, 0x3e, 0xd3, 0x86, 0xf1, 0xe5, 0x37, 0x88, 0x53, 0x1a, 0xbe, 0xe0, 0xcd,
|
||||
0x83, 0xd7, 0xdf, 0xd5, 0x32, 0xdf, 0x7e, 0x57, 0xcb, 0xbc, 0xbe, 0xaa, 0x09, 0xdf, 0x5e, 0xd5,
|
||||
0x84, 0x7f, 0x5c, 0xd5, 0x32, 0xff, 0xba, 0xaa, 0x09, 0xdf, 0xfc, 0xb3, 0x26, 0x8c, 0x0a, 0xac,
|
||||
0x91, 0x7d, 0xf1, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xad, 0x8a, 0xef, 0x7f, 0x8f, 0x0e, 0x00,
|
||||
0x00,
|
||||
}
|
||||
|
@ -113,9 +113,9 @@ message FileInfo {
|
||||
enum FileInfoType {
|
||||
FILE = 0 [(gogoproto.enumvalue_customname) = "FileInfoTypeFile"];
|
||||
DIRECTORY = 1 [(gogoproto.enumvalue_customname) = "FileInfoTypeDirectory"];
|
||||
SYMLINK_FILE = 2 [(gogoproto.enumvalue_customname) = "FileInfoTypeSymlinkFile"];
|
||||
SYMLINK_DIRECTORY = 3 [(gogoproto.enumvalue_customname) = "FileInfoTypeSymlinkDirectory"];
|
||||
SYMLINK_UNKNOWN = 4 [(gogoproto.enumvalue_customname) = "FileInfoTypeSymlinkUnknown"];
|
||||
SYMLINK_FILE = 2 [(gogoproto.enumvalue_customname) = "FileInfoTypeDeprecatedSymlinkFile", deprecated = true];
|
||||
SYMLINK_DIRECTORY = 3 [(gogoproto.enumvalue_customname) = "FileInfoTypeDeprecatedSymlinkDirectory", deprecated = true];
|
||||
SYMLINK = 4 [(gogoproto.enumvalue_customname) = "FileInfoTypeSymlink"];
|
||||
}
|
||||
|
||||
message BlockInfo {
|
||||
|
@ -37,7 +37,7 @@ func (f FileInfo) String() string {
|
||||
case FileInfoTypeFile:
|
||||
return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, NoPermissions:%v, Blocks:%v}",
|
||||
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.Invalid, f.NoPermissions, f.Blocks)
|
||||
case FileInfoTypeSymlinkDirectory, FileInfoTypeSymlinkFile, FileInfoTypeSymlinkUnknown:
|
||||
case FileInfoTypeSymlink, FileInfoTypeDeprecatedSymlinkDirectory, FileInfoTypeDeprecatedSymlinkFile:
|
||||
return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, NoPermissions:%v, SymlinkTarget:%q}",
|
||||
f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.Invalid, f.NoPermissions, f.SymlinkTarget)
|
||||
default:
|
||||
@ -59,7 +59,7 @@ func (f FileInfo) IsDirectory() bool {
|
||||
|
||||
func (f FileInfo) IsSymlink() bool {
|
||||
switch f.Type {
|
||||
case FileInfoTypeSymlinkDirectory, FileInfoTypeSymlinkFile, FileInfoTypeSymlinkUnknown:
|
||||
case FileInfoTypeSymlink, FileInfoTypeDeprecatedSymlinkDirectory, FileInfoTypeDeprecatedSymlinkFile:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/ignore"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
@ -374,19 +373,12 @@ func (w *walker) walkDir(relPath string, info os.FileInfo, dchan chan protocol.F
|
||||
// walkSymlink returns nil or an error, if the error is of the nature that
|
||||
// it should stop the entire walk.
|
||||
func (w *walker) walkSymlink(absPath, relPath string, dchan chan protocol.FileInfo) 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 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)
|
||||
target, err := os.Readlink(absPath)
|
||||
if err != nil {
|
||||
l.Debugln("readlink error:", absPath, err)
|
||||
return nil
|
||||
@ -400,13 +392,13 @@ func (w *walker) walkSymlink(absPath, relPath string, dchan chan protocol.FileIn
|
||||
// - the symlink type (file/dir) was the same
|
||||
// - the target was the same
|
||||
cf, ok := w.CurrentFiler.CurrentFile(relPath)
|
||||
if ok && !cf.IsDeleted() && cf.IsSymlink() && !cf.IsInvalid() && SymlinkTypeEqual(targetType, cf) && cf.SymlinkTarget == target {
|
||||
if ok && !cf.IsDeleted() && cf.IsSymlink() && !cf.IsInvalid() && cf.SymlinkTarget == target {
|
||||
return nil
|
||||
}
|
||||
|
||||
f := protocol.FileInfo{
|
||||
Name: relPath,
|
||||
Type: SymlinkType(targetType),
|
||||
Type: protocol.FileInfoTypeSymlink,
|
||||
Version: cf.Version.Update(w.ShortID),
|
||||
NoPermissions: true, // Symlinks don't have permissions of their own
|
||||
SymlinkTarget: target,
|
||||
@ -489,38 +481,6 @@ func PermsEqual(a, b uint32) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func SymlinkTypeEqual(disk symlinks.TargetType, f protocol.FileInfo) bool {
|
||||
// If the target is missing, Unix never knows what type of symlink it is
|
||||
// and Windows always knows even if there is no target. Which means that
|
||||
// without this special check a Unix node would be fighting with a Windows
|
||||
// node about whether or not the target is known. Basically, if you don't
|
||||
// know and someone else knows, just accept it. The fact that you don't
|
||||
// know means you are on Unix, and on Unix you don't really care what the
|
||||
// target type is. The moment you do know, and if something doesn't match,
|
||||
// that will propagate through the cluster.
|
||||
switch disk {
|
||||
case symlinks.TargetUnknown:
|
||||
return true
|
||||
case symlinks.TargetDirectory:
|
||||
return f.Type == protocol.FileInfoTypeSymlinkDirectory
|
||||
case symlinks.TargetFile:
|
||||
return f.Type == protocol.FileInfoTypeSymlinkFile
|
||||
}
|
||||
panic("unknown symlink TargetType")
|
||||
}
|
||||
|
||||
func SymlinkType(t symlinks.TargetType) protocol.FileInfoType {
|
||||
switch t {
|
||||
case symlinks.TargetFile:
|
||||
return protocol.FileInfoTypeSymlinkFile
|
||||
case symlinks.TargetDirectory:
|
||||
return protocol.FileInfoTypeSymlinkDirectory
|
||||
case symlinks.TargetUnknown:
|
||||
return protocol.FileInfoTypeSymlinkUnknown
|
||||
}
|
||||
panic("unknown symlink TargetType")
|
||||
}
|
||||
|
||||
// A byteCounter gets bytes added to it via Update() and then provides the
|
||||
// Total() and one minute moving average Rate() in bytes per second.
|
||||
type byteCounter struct {
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/ignore"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
@ -282,7 +281,7 @@ func TestIssue1507(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWalkSymlink(t *testing.T) {
|
||||
if !symlinks.Supported {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("skipping unsupported symlink test")
|
||||
return
|
||||
}
|
||||
@ -293,7 +292,7 @@ func TestWalkSymlink(t *testing.T) {
|
||||
defer os.RemoveAll("_symlinks")
|
||||
|
||||
os.Mkdir("_symlinks", 0755)
|
||||
symlinks.Create("_symlinks/link", "destination", symlinks.TargetUnknown)
|
||||
os.Symlink("destination", "_symlinks/link")
|
||||
|
||||
// Scan it
|
||||
|
||||
@ -383,34 +382,6 @@ func (l testfileList) String() string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func TestSymlinkTypeEqual(t *testing.T) {
|
||||
testcases := []struct {
|
||||
onDiskType symlinks.TargetType
|
||||
fiType protocol.FileInfoType
|
||||
equal bool
|
||||
}{
|
||||
// File is only equal to file
|
||||
{symlinks.TargetFile, protocol.FileInfoTypeSymlinkFile, true},
|
||||
{symlinks.TargetFile, protocol.FileInfoTypeSymlinkDirectory, false},
|
||||
{symlinks.TargetFile, protocol.FileInfoTypeSymlinkUnknown, false},
|
||||
// Directory is only equal to directory
|
||||
{symlinks.TargetDirectory, protocol.FileInfoTypeSymlinkFile, false},
|
||||
{symlinks.TargetDirectory, protocol.FileInfoTypeSymlinkDirectory, true},
|
||||
{symlinks.TargetDirectory, protocol.FileInfoTypeSymlinkUnknown, false},
|
||||
// Unknown is equal to anything
|
||||
{symlinks.TargetUnknown, protocol.FileInfoTypeSymlinkFile, true},
|
||||
{symlinks.TargetUnknown, protocol.FileInfoTypeSymlinkDirectory, true},
|
||||
{symlinks.TargetUnknown, protocol.FileInfoTypeSymlinkUnknown, true},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
res := SymlinkTypeEqual(tc.onDiskType, protocol.FileInfo{Type: tc.fiType})
|
||||
if res != tc.equal {
|
||||
t.Errorf("Incorrect result %v for %v, %v", res, tc.onDiskType, tc.fiType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var initOnce sync.Once
|
||||
|
||||
const (
|
||||
|
@ -1,10 +0,0 @@
|
||||
// Copyright (C) 2016 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// The existence of this file means we get 0% test coverage rather than no
|
||||
// test coverage at all. Remove when implementing an actual test.
|
||||
|
||||
package symlinks
|
@ -1,37 +0,0 @@
|
||||
// Copyright (C) 2014 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package symlinks
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
)
|
||||
|
||||
var (
|
||||
Supported = true
|
||||
)
|
||||
|
||||
func Read(path string) (string, TargetType, error) {
|
||||
tt := TargetUnknown
|
||||
if stat, err := os.Stat(path); err == nil {
|
||||
if stat.IsDir() {
|
||||
tt = TargetDirectory
|
||||
} else {
|
||||
tt = TargetFile
|
||||
}
|
||||
}
|
||||
path, err := os.Readlink(path)
|
||||
|
||||
return osutil.NormalizedFilename(path), tt, err
|
||||
}
|
||||
|
||||
func Create(source, target string, tt TargetType) error {
|
||||
return os.Symlink(osutil.NativeFilename(target), source)
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
// Copyright (C) 2014 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build windows
|
||||
|
||||
package symlinks
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
Win32FsctlGetReparsePoint = 0x900a8
|
||||
Win32FileFlagOpenReparsePoint = 0x00200000
|
||||
Win32FileAttributeReparsePoint = 0x400
|
||||
Win32IOReparseTagSymlink = 0xA000000C
|
||||
Win32SymbolicLinkFlagDirectory = 0x1
|
||||
)
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procDeviceIoControl = modkernel32.NewProc("DeviceIoControl")
|
||||
procCreateSymbolicLink = modkernel32.NewProc("CreateSymbolicLinkW")
|
||||
|
||||
Supported = false
|
||||
)
|
||||
|
||||
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 privileges.
|
||||
// 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, TargetDirectory)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stat, err := osutil.Lstat(path)
|
||||
if err != nil || stat.Mode()&os.ModeSymlink == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
target, tt, err := Read(path)
|
||||
if err != nil || osutil.NativeFilename(target) != base || tt != TargetDirectory {
|
||||
return
|
||||
}
|
||||
Supported = true
|
||||
}
|
||||
|
||||
type reparseData struct {
|
||||
reparseTag uint32
|
||||
reparseDataLength uint16
|
||||
reserved uint16
|
||||
substitueNameOffset uint16
|
||||
substitueNameLength uint16
|
||||
printNameOffset uint16
|
||||
printNameLength uint16
|
||||
flags uint32
|
||||
// substituteName - 264 widechars max = 528 bytes
|
||||
// printName - 260 widechars max = 520 bytes
|
||||
// = 1048 bytes total
|
||||
buffer [1048]uint16
|
||||
}
|
||||
|
||||
func (r *reparseData) PrintName() string {
|
||||
// No clue why the offset and length is doubled...
|
||||
offset := r.printNameOffset / 2
|
||||
length := r.printNameLength / 2
|
||||
return string(utf16.Decode(r.buffer[offset : offset+length]))
|
||||
}
|
||||
|
||||
func (r *reparseData) SubstituteName() string {
|
||||
// No clue why the offset and length is doubled...
|
||||
offset := r.substitueNameOffset / 2
|
||||
length := r.substitueNameLength / 2
|
||||
return string(utf16.Decode(r.buffer[offset : offset+length]))
|
||||
}
|
||||
|
||||
func Read(path string) (string, TargetType, error) {
|
||||
ptr, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return "", TargetUnknown, err
|
||||
}
|
||||
handle, err := syscall.CreateFile(ptr, 0, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS|Win32FileFlagOpenReparsePoint, 0)
|
||||
if err != nil || handle == syscall.InvalidHandle {
|
||||
return "", TargetUnknown, err
|
||||
}
|
||||
defer syscall.Close(handle)
|
||||
var ret uint16
|
||||
var data reparseData
|
||||
|
||||
r1, _, err := syscall.Syscall9(procDeviceIoControl.Addr(), 8, uintptr(handle), Win32FsctlGetReparsePoint, 0, 0, uintptr(unsafe.Pointer(&data)), unsafe.Sizeof(data), uintptr(unsafe.Pointer(&ret)), 0, 0)
|
||||
if r1 == 0 {
|
||||
return "", TargetUnknown, err
|
||||
}
|
||||
|
||||
tt := TargetUnknown
|
||||
if attr, err := syscall.GetFileAttributes(ptr); err == nil {
|
||||
if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
tt = TargetDirectory
|
||||
} else {
|
||||
tt = TargetFile
|
||||
}
|
||||
}
|
||||
|
||||
return osutil.NormalizedFilename(data.PrintName()), tt, nil
|
||||
}
|
||||
|
||||
func Create(source, target string, tt TargetType) 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 tt == TargetUnknown {
|
||||
path := target
|
||||
if !filepath.IsAbs(target) {
|
||||
path = filepath.Join(filepath.Dir(source), target)
|
||||
}
|
||||
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && stat.IsDir() {
|
||||
mode = Win32SymbolicLinkFlagDirectory
|
||||
}
|
||||
} else if tt == TargetDirectory {
|
||||
mode = Win32SymbolicLinkFlagDirectory
|
||||
}
|
||||
|
||||
r0, _, err := syscall.Syscall(procCreateSymbolicLink.Addr(), 3, uintptr(unsafe.Pointer(srcp)), uintptr(unsafe.Pointer(trgp)), uintptr(mode))
|
||||
if r0 == 1 {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
// Copyright (C) 2015 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package symlinks
|
||||
|
||||
type TargetType int
|
||||
|
||||
const (
|
||||
TargetFile TargetType = iota
|
||||
TargetDirectory
|
||||
TargetUnknown
|
||||
)
|
@ -13,8 +13,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
)
|
||||
|
||||
func TestIgnores(t *testing.T) {
|
||||
@ -55,7 +53,7 @@ func TestIgnores(t *testing.T) {
|
||||
syms = []string{"s1", "s2", "s3", "s4", "s11", "s12", "s13", "s14"}
|
||||
for _, sym := range syms {
|
||||
p := filepath.Join("s1", sym)
|
||||
symlinks.Create(p, p, 0)
|
||||
os.Symlink(p, p)
|
||||
}
|
||||
all = append(all, syms...)
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/rc"
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
)
|
||||
|
||||
func TestSymlinks(t *testing.T) {
|
||||
@ -107,7 +106,7 @@ func testSymlinks(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fd.Close()
|
||||
err = symlinks.Create("s1/fileLink", "file", 0)
|
||||
err = os.Symlink("file", "s1/fileLink")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -118,35 +117,35 @@ func testSymlinks(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = symlinks.Create("s1/dirLink", "dir", 0)
|
||||
err = os.Symlink("dir", "s1/dirLink")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// A link to something in the repo that does not exist
|
||||
|
||||
err = symlinks.Create("s1/noneLink", "does/not/exist", 0)
|
||||
err = os.Symlink("does/not/exist", "s1/noneLink")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// A link we will replace with a file later
|
||||
|
||||
err = symlinks.Create("s1/repFileLink", "does/not/exist", 0)
|
||||
err = os.Symlink("does/not/exist", "s1/repFileLink")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// A link we will replace with a directory later
|
||||
|
||||
err = symlinks.Create("s1/repDirLink", "does/not/exist", 0)
|
||||
err = os.Symlink("does/not/exist", "s1/repDirLink")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// A link we will remove later
|
||||
|
||||
err = symlinks.Create("s1/removeLink", "does/not/exist", 0)
|
||||
err = os.Symlink("does/not/exist", "s1/removeLink")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -183,7 +182,7 @@ func testSymlinks(t *testing.T) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = symlinks.Create("s1/dirLink", "file", 0)
|
||||
err = os.Symlink("file", "s1/dirLink")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -219,7 +218,7 @@ func testSymlinks(t *testing.T) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = symlinks.Create("s1/fileToReplace", "somewhere/non/existent", 0)
|
||||
err = os.Symlink("somewhere/non/existent", "s1/fileToReplace")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -230,7 +229,7 @@ func testSymlinks(t *testing.T) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = symlinks.Create("s1/dirToReplace", "somewhere/non/existent", 0)
|
||||
err = os.Symlink("somewhere/non/existent", "s1/dirToReplace")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ import (
|
||||
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/rc"
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -436,7 +435,7 @@ func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan er
|
||||
mode: os.ModeSymlink,
|
||||
}
|
||||
|
||||
tgt, _, err := symlinks.Read(path)
|
||||
tgt, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user