Skip to content

Commit

Permalink
Merge branch 'main' into runtimeos
Browse files Browse the repository at this point in the history
* main:
  all: Support syncing ownership (fixes syncthing#1329) (syncthing#8434)
  gui, man, authors: Update docs, translations, and contributors
  • Loading branch information
calmh committed Jul 26, 2022
2 parents 17028a9 + adce6fa commit b7862b0
Show file tree
Hide file tree
Showing 36 changed files with 1,885 additions and 492 deletions.
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -52,7 +52,7 @@ require (
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/mod v0.5.1 // indirect
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b
golang.org/x/text v0.3.7
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
golang.org/x/tools v0.1.7
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Expand Up @@ -603,8 +603,9 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
2 changes: 1 addition & 1 deletion gui/default/assets/lang/lang-sv.json
Expand Up @@ -316,7 +316,7 @@
"Show detailed listener status": "Visa detaljerad lyssnarstatus",
"Show diff with previous version": "Visa skillnad med tidigare version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Visas istället för enhets-ID i klusterstatus. Kommer att annonseras på andra enheter som ett valfritt standardnamn.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Visas istället för enhets-ID i klusterstatus. Uppdateras till namnet som enheten annonserar om den lämnas tom.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Visas istället för enhets-ID i klusterstatusen. Kommer att uppdateras till det namn som enheten annonserar som om det lämnas tomt.",
"Shutdown": "Stäng av",
"Shutdown Complete": "Avstängning klar",
"Simple": "Enkel",
Expand Down
5 changes: 1 addition & 4 deletions lib/config/config.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

296 changes: 167 additions & 129 deletions lib/config/folderconfiguration.pb.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions lib/db/db_test.go
Expand Up @@ -212,7 +212,7 @@ func TestUpdate0to3(t *testing.T) {
t.Error("Unexpected additional file via sequence", f.FileName())
return true
}
if e := haveUpdate0to3[protocol.LocalDeviceID][0]; f.IsEquivalentOptional(e, 0, true, true, 0) {
if e := haveUpdate0to3[protocol.LocalDeviceID][0]; f.IsEquivalentOptional(e, protocol.FileInfoComparison{IgnorePerms: true, IgnoreBlocks: true}) {
found = true
} else {
t.Errorf("Wrong file via sequence, got %v, expected %v", f, e)
Expand Down Expand Up @@ -281,7 +281,7 @@ func TestUpdate0to3(t *testing.T) {
}
f := fi.(protocol.FileInfo)
delete(need, f.Name)
if !f.IsEquivalentOptional(e, 0, true, true, 0) {
if !f.IsEquivalentOptional(e, protocol.FileInfoComparison{IgnorePerms: true, IgnoreBlocks: true}) {
t.Errorf("Wrong needed file, got %v, expected %v", f, e)
}
}
Expand Down
256 changes: 152 additions & 104 deletions lib/db/structs.pb.go

Large diffs are not rendered by default.

8 changes: 0 additions & 8 deletions lib/fs/basicfs.go
Expand Up @@ -129,14 +129,6 @@ func (f *BasicFilesystem) Chmod(name string, mode FileMode) error {
return os.Chmod(name, os.FileMode(mode))
}

func (f *BasicFilesystem) Lchown(name string, uid, gid int) error {
name, err := f.rooted(name)
if err != nil {
return err
}
return os.Lchown(name, uid, gid)
}

func (f *BasicFilesystem) Chtimes(name string, atime time.Time, mtime time.Time) error {
name, err := f.rooted(name)
if err != nil {
Expand Down
18 changes: 18 additions & 0 deletions lib/fs/basicfs_platformdata_unix.go
@@ -0,0 +1,18 @@
// Copyright (C) 2022 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 https://mozilla.org/MPL/2.0/.

//go:build !windows
// +build !windows

package fs

import (
"github.com/syncthing/syncthing/lib/protocol"
)

func (f *BasicFilesystem) PlatformData(name string) (protocol.PlatformData, error) {
return unixPlatformData(f, name)
}
69 changes: 69 additions & 0 deletions lib/fs/basicfs_platformdata_windows.go
@@ -0,0 +1,69 @@
// Copyright (C) 2022 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 https://mozilla.org/MPL/2.0/.

package fs

import (
"fmt"
"os/user"

"github.com/syncthing/syncthing/lib/protocol"
"golang.org/x/sys/windows"
)

func (f *BasicFilesystem) PlatformData(name string) (protocol.PlatformData, error) {
rootedName, err := f.rooted(name)
if err != nil {
return protocol.PlatformData{}, fmt.Errorf("rooted for %s: %w", name, err)
}
hdl, err := openReadOnlyWithBackupSemantics(rootedName)
if err != nil {
return protocol.PlatformData{}, fmt.Errorf("open %s: %w", rootedName, err)
}
defer windows.Close(hdl)

// GetSecurityInfo returns an owner SID.
sd, err := windows.GetSecurityInfo(hdl, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION)
if err != nil {
return protocol.PlatformData{}, fmt.Errorf("get security info for %s: %w", rootedName, err)
}
owner, _, err := sd.Owner()
if err != nil {
return protocol.PlatformData{}, fmt.Errorf("get owner for %s: %w", rootedName, err)
}

// The owner SID might represent a user or a group. We try to look it up
// as both, and set the appropriate fields in the OS data.
pd := &protocol.WindowsData{}
if us, err := user.LookupId(owner.String()); err == nil {
pd.OwnerName = us.Username
} else if gr, err := user.LookupGroupId(owner.String()); err == nil {
pd.OwnerName = gr.Name
pd.OwnerIsGroup = true
} else {
l.Debugf("Failed to resolve owner for %s: %v", rootedName, err)
}

return protocol.PlatformData{Windows: pd}, nil
}

func openReadOnlyWithBackupSemantics(path string) (fd windows.Handle, err error) {
// This is windows.Open but simplified to read-only only, and adding
// FILE_FLAG_BACKUP_SEMANTICS which is required to open directories.
if len(path) == 0 {
return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND
}
pathp, err := windows.UTF16PtrFromString(path)
if err != nil {
return windows.InvalidHandle, err
}
var access uint32 = windows.GENERIC_READ
var sharemode uint32 = windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE
var sa *windows.SecurityAttributes
var createmode uint32 = windows.OPEN_EXISTING
var attrs uint32 = windows.FILE_ATTRIBUTE_READONLY | windows.FILE_FLAG_BACKUP_SEMANTICS
return windows.CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
}
3 changes: 2 additions & 1 deletion lib/fs/basicfs_test.go
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -84,7 +85,7 @@ func TestChownFile(t *testing.T) {
newUID := 1000 + rand.Intn(30000)
newGID := 1000 + rand.Intn(30000)

if err := fs.Lchown("file", newUID, newGID); err != nil {
if err := fs.Lchown("file", strconv.Itoa(newUID), strconv.Itoa(newGID)); err != nil {
t.Error("Unexpected error:", err)
}

Expand Down
17 changes: 17 additions & 0 deletions lib/fs/basicfs_unix.go
Expand Up @@ -12,6 +12,7 @@ package fs
import (
"os"
"path/filepath"
"strconv"
"strings"
)

Expand Down Expand Up @@ -57,6 +58,22 @@ func (f *BasicFilesystem) Roots() ([]string, error) {
return []string{"/"}, nil
}

func (f *BasicFilesystem) Lchown(name, uid, gid string) error {
name, err := f.rooted(name)
if err != nil {
return err
}
nuid, err := strconv.Atoi(uid)
if err != nil {
return err
}
ngid, err := strconv.Atoi(gid)
if err != nil {
return err
}
return os.Lchown(name, nuid, ngid)
}

// unrootedChecked returns the path relative to the folder root (same as
// unrooted) or an error if the given path is not a subpath and handles the
// special case when the given path is the folder root without a trailing
Expand Down
38 changes: 38 additions & 0 deletions lib/fs/basicfs_windows.go
Expand Up @@ -17,6 +17,8 @@ import (
"strings"
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

var errNotSupported = errors.New("symlinks not supported")
Expand Down Expand Up @@ -152,6 +154,42 @@ func (f *BasicFilesystem) Roots() ([]string, error) {
return drives, nil
}

func (f *BasicFilesystem) Lchown(name, uid, gid string) error {
name, err := f.rooted(name)
if err != nil {
return err
}

hdl, err := windows.Open(name, windows.O_WRONLY, 0)
if err != nil {
return err
}
defer windows.Close(hdl)

// Depending on whether we got an uid or a gid, we need to set the
// appropriate flag and parse the corresponding SID. The one we're not
// setting remains nil, which is what we want in the call to
// SetSecurityInfo.

var si windows.SECURITY_INFORMATION
var ownerSID, groupSID *syscall.SID
if uid != "" {
ownerSID, err = syscall.StringToSid(uid)
if err == nil {
si |= windows.OWNER_SECURITY_INFORMATION
}
} else if gid != "" {
groupSID, err = syscall.StringToSid(uid)
if err == nil {
si |= windows.GROUP_SECURITY_INFORMATION
}
} else {
return errors.New("neither uid nor gid specified")
}

return windows.SetSecurityInfo(hdl, windows.SE_FILE_OBJECT, si, (*windows.SID)(ownerSID), (*windows.SID)(groupSID), nil, nil)
}

// unrootedChecked returns the path relative to the folder root (same as
// unrooted) or an error if the given path is not a subpath and handles the
// special case when the given path is the folder root without a trailing
Expand Down
2 changes: 1 addition & 1 deletion lib/fs/casefs.go
Expand Up @@ -153,7 +153,7 @@ func (f *caseFilesystem) Chmod(name string, mode FileMode) error {
return f.Filesystem.Chmod(name, mode)
}

func (f *caseFilesystem) Lchown(name string, uid, gid int) error {
func (f *caseFilesystem) Lchown(name, uid, gid string) error {
if err := f.checkCase(name); err != nil {
return err
}
Expand Down
7 changes: 6 additions & 1 deletion lib/fs/errorfs.go
Expand Up @@ -9,6 +9,8 @@ package fs
import (
"context"
"time"

"github.com/syncthing/syncthing/lib/protocol"
)

type errorFilesystem struct {
Expand All @@ -18,7 +20,7 @@ type errorFilesystem struct {
}

func (fs *errorFilesystem) Chmod(name string, mode FileMode) error { return fs.err }
func (fs *errorFilesystem) Lchown(name string, uid, gid int) error { return fs.err }
func (fs *errorFilesystem) Lchown(name, uid, gid string) error { return fs.err }
func (fs *errorFilesystem) Chtimes(name string, atime time.Time, mtime time.Time) error {
return fs.err
}
Expand Down Expand Up @@ -52,6 +54,9 @@ func (fs *errorFilesystem) SameFile(fi1, fi2 FileInfo) bool { return false }
func (fs *errorFilesystem) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
return nil, nil, fs.err
}
func (fs *errorFilesystem) PlatformData(name string) (protocol.PlatformData, error) {
return protocol.PlatformData{}, fs.err
}

func (fs *errorFilesystem) underlying() (Filesystem, bool) {
return nil, false
Expand Down
31 changes: 18 additions & 13 deletions lib/fs/fakefs.go
Expand Up @@ -21,6 +21,8 @@ import (
"sync"
"testing"
"time"

"github.com/syncthing/syncthing/lib/protocol"
)

// see readShortAt()
Expand All @@ -29,19 +31,19 @@ const randomBlockShift = 14 // 128k
// fakeFS is a fake filesystem for testing and benchmarking. It has the
// following properties:
//
// - File metadata is kept in RAM. Specifically, we remember which files and
// directories exist, their dates, permissions and sizes. Symlinks are
// not supported.
// - File metadata is kept in RAM. Specifically, we remember which files and
// directories exist, their dates, permissions and sizes. Symlinks are
// not supported.
//
// - File contents are generated pseudorandomly with just the file name as
// seed. Writes are discarded, other than having the effect of increasing
// the file size. If you only write data that you've read from a file with
// the same name on a different fakeFS, you'll never know the difference...
// - File contents are generated pseudorandomly with just the file name as
// seed. Writes are discarded, other than having the effect of increasing
// the file size. If you only write data that you've read from a file with
// the same name on a different fakeFS, you'll never know the difference...
//
// - We totally ignore permissions - pretend you are root.
//
// - The root path can contain URL query-style parameters that pre populate
// the filesystem at creation with a certain amount of random data:
// - The root path can contain URL query-style parameters that pre populate
// the filesystem at creation with a certain amount of random data:
//
// files=n to generate n random files (default 0)
// maxsize=n to generate files up to a total of n MiB (default 0)
Expand All @@ -51,7 +53,6 @@ const randomBlockShift = 14 // 128k
// latency=d to set the amount of time each "disk" operation takes, where d is time.ParseDuration format
//
// - Two fakeFS:s pointing at the same root path see the same files.
//
type fakeFS struct {
counters fakeFSCounters
uri string
Expand Down Expand Up @@ -220,7 +221,7 @@ func (fs *fakeFS) Chmod(name string, mode FileMode) error {
return nil
}

func (fs *fakeFS) Lchown(name string, uid, gid int) error {
func (fs *fakeFS) Lchown(name, uid, gid string) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Lchown++
Expand All @@ -229,8 +230,8 @@ func (fs *fakeFS) Lchown(name string, uid, gid int) error {
if entry == nil {
return os.ErrNotExist
}
entry.uid = uid
entry.gid = gid
entry.uid, _ = strconv.Atoi(uid)
entry.gid, _ = strconv.Atoi(gid)
return nil
}

Expand Down Expand Up @@ -656,6 +657,10 @@ func (fs *fakeFS) SameFile(fi1, fi2 FileInfo) bool {
return ok && fi1.ModTime().Equal(fi2.ModTime()) && fi1.Mode() == fi2.Mode() && fi1.IsDir() == fi2.IsDir() && fi1.IsRegular() == fi2.IsRegular() && fi1.IsSymlink() == fi2.IsSymlink() && fi1.Owner() == fi2.Owner() && fi1.Group() == fi2.Group()
}

func (fs *fakeFS) PlatformData(name string) (protocol.PlatformData, error) {
return unixPlatformData(fs, name)
}

func (fs *fakeFS) underlying() (Filesystem, bool) {
return nil, false
}
Expand Down
2 changes: 1 addition & 1 deletion lib/fs/fakefs_test.go
Expand Up @@ -121,7 +121,7 @@ func TestFakeFS(t *testing.T) {
}

// Chown
if err := fs.Lchown("dira", 1234, 5678); err != nil {
if err := fs.Lchown("dira", "1234", "5678"); err != nil {
t.Fatal(err)
}
if info, err := fs.Lstat("dira"); err != nil {
Expand Down

0 comments on commit b7862b0

Please sign in to comment.