don't execute fs check if the user has recent activity

The check could be expensive with some backends and is generally
only required the first time that a user logs in

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2022-02-24 16:11:35 +01:00
parent 670018f05e
commit f5a0559be6
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
16 changed files with 355 additions and 156 deletions

View file

@ -881,6 +881,39 @@ func TestGetTLSVersion(t *testing.T) {
assert.Equal(t, uint16(tls.VersionTLS13), tlsVer)
}
func TestCleanPath(t *testing.T) {
assert.Equal(t, "/", util.CleanPath("/"))
assert.Equal(t, "/", util.CleanPath("."))
assert.Equal(t, "/", util.CleanPath("/."))
assert.Equal(t, "/", util.CleanPath("/a/.."))
assert.Equal(t, "/a", util.CleanPath("/a/"))
assert.Equal(t, "/a", util.CleanPath("a/"))
// filepath.ToSlash does not touch \ as char on unix systems
// so os.PathSeparator is used for windows compatible tests
bslash := string(os.PathSeparator)
assert.Equal(t, "/", util.CleanPath(bslash))
assert.Equal(t, "/", util.CleanPath(bslash+bslash))
assert.Equal(t, "/a", util.CleanPath(bslash+"a"+bslash))
assert.Equal(t, "/a", util.CleanPath("a"+bslash))
assert.Equal(t, "/a/b/c", util.CleanPath(bslash+"a"+bslash+bslash+"b"+bslash+bslash+"c"+bslash))
assert.Equal(t, "/C:/a", util.CleanPath("C:"+bslash+"a"))
}
func TestUserRecentActivity(t *testing.T) {
u := dataprovider.User{}
res := u.HasRecentActivity()
assert.False(t, res)
u.LastLogin = util.GetTimeAsMsSinceEpoch(time.Now())
res = u.HasRecentActivity()
assert.True(t, res)
u.LastLogin = util.GetTimeAsMsSinceEpoch(time.Now().Add(1 * time.Minute))
res = u.HasRecentActivity()
assert.False(t, res)
u.LastLogin = util.GetTimeAsMsSinceEpoch(time.Now().Add(1 * time.Second))
res = u.HasRecentActivity()
assert.True(t, res)
}
func BenchmarkBcryptHashing(b *testing.B) {
bcryptPassword := "bcryptpassword"
for i := 0; i < b.N; i++ {

View file

@ -837,13 +837,16 @@ func TestTruncateQuotaLimits(t *testing.T) {
// cleanup
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
user.UsedQuotaFiles = 0
user.UsedQuotaSize = 0
_, err = httpdtest.UpdateQuotaUsage(user, "reset", http.StatusOK)
assert.NoError(t, err)
user.QuotaSize = 0
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
if user.Username == defaultUsername {
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.QuotaSize = 0
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
}

View file

@ -1122,9 +1122,7 @@ func UpdateAPIKeyLastUse(apiKey *APIKey) error {
// UpdateLastLogin updates the last login field for the given SFTPGo user
func UpdateLastLogin(user *User) {
lastLogin := util.GetTimeFromMsecSinceEpoch(user.LastLogin)
diff := -time.Until(lastLogin)
if diff < 0 || diff > lastLoginMinDelay {
if !isLastActivityRecent(user.LastLogin) {
err := provider.updateLastLogin(user.Username)
if err == nil {
webDAVUsersCache.updateLastLogin(user.Username)
@ -1134,9 +1132,7 @@ func UpdateLastLogin(user *User) {
// UpdateAdminLastLogin updates the last login field for the given SFTPGo admin
func UpdateAdminLastLogin(admin *Admin) {
lastLogin := util.GetTimeFromMsecSinceEpoch(admin.LastLogin)
diff := -time.Until(lastLogin)
if diff < 0 || diff > lastLoginMinDelay {
if !isLastActivityRecent(admin.LastLogin) {
provider.updateAdminLastLogin(admin.Username) //nolint:errcheck
}
}
@ -3375,3 +3371,12 @@ func getUserAndJSONForHook(username string) (User, []byte, error) {
func providerLog(level logger.LogLevel, format string, v ...interface{}) {
logger.Log(level, logSender, "", format, v...)
}
func isLastActivityRecent(lastActivity int64) bool {
lastActivityTime := util.GetTimeFromMsecSinceEpoch(lastActivity)
diff := -time.Until(lastActivityTime)
if diff < -10*time.Second {
return false
}
return diff < lastLoginMinDelay
}

View file

@ -161,12 +161,51 @@ func (u *User) getRootFs(connectionID string) (fs vfs.Fs, err error) {
}
}
func (u *User) checkDirWithParents(virtualDirPath, connectionID string) error {
dirs := util.GetDirsForVirtualPath(virtualDirPath)
for idx := len(dirs) - 1; idx >= 0; idx-- {
vPath := dirs[idx]
if vPath == "/" {
continue
}
fs, err := u.GetFilesystemForPath(vPath, connectionID)
if err != nil {
return fmt.Errorf("unable to get fs for path %#v: %w", vPath, err)
}
if fs.HasVirtualFolders() {
continue
}
fsPath, err := fs.ResolvePath(vPath)
if err != nil {
return fmt.Errorf("unable to resolve path %#v: %w", vPath, err)
}
_, err = fs.Stat(fsPath)
if err == nil {
continue
}
if fs.IsNotExist(err) {
err = fs.Mkdir(fsPath)
if err != nil {
return err
}
vfs.SetPathPermissions(fs, fsPath, u.GetUID(), u.GetGID())
} else {
return fmt.Errorf("unable to stat path %#v: %w", vPath, err)
}
}
return nil
}
// CheckFsRoot check the root directory for the main fs and the virtual folders.
// It returns an error if the main filesystem cannot be created
func (u *User) CheckFsRoot(connectionID string) error {
if u.Filters.DisableFsChecks {
return nil
}
if isLastActivityRecent(u.LastLogin) {
return nil
}
fs, err := u.GetFilesystemForPath("/", connectionID)
if err != nil {
logger.Warn(logSender, connectionID, "could not create main filesystem for user %#v err: %v", u.Username, err)
@ -180,15 +219,9 @@ func (u *User) CheckFsRoot(connectionID string) error {
fs.CheckRootPath(u.Username, u.GetUID(), u.GetGID())
}
// now check intermediary folders
fs, err = u.GetFilesystemForPath(path.Dir(v.VirtualPath), connectionID)
if err == nil && !fs.HasVirtualFolders() {
fsPath, err := fs.ResolvePath(v.VirtualPath)
if err != nil {
continue
}
err = fs.MkdirAll(fsPath, u.GetUID(), u.GetGID())
logger.Debug(logSender, connectionID, "create intermediary dir to %#v, path %#v, err: %v",
v.VirtualPath, fsPath, err)
err = u.checkDirWithParents(path.Dir(v.VirtualPath), connectionID)
if err != nil {
logger.Warn(logSender, connectionID, "could not create intermediary dir to %#v, err: %v", v.VirtualPath, err)
}
}
return nil
@ -1071,6 +1104,11 @@ func (u *User) GetHomeDir() string {
return filepath.Clean(u.HomeDir)
}
// HasRecentActivity returns true if the last user login is recent and so we can skip some expensive checks
func (u *User) HasRecentActivity() bool {
return isLastActivityRecent(u.LastLogin)
}
// HasQuotaRestrictions returns true if there are any disk quota restrictions
func (u *User) HasQuotaRestrictions() bool {
return u.QuotaFiles > 0 || u.QuotaSize > 0

View file

@ -1433,6 +1433,13 @@ func TestResume(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -1579,8 +1586,14 @@ func TestQuotaLimits(t *testing.T) {
assert.NoError(t, err)
user.QuotaFiles = 0
user.QuotaSize = 0
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.QuotaSize = 0
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
@ -1631,9 +1644,14 @@ func TestUploadMaxSize(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
user.Filters.MaxUploadFileSize = 65536000
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.Filters.MaxUploadFileSize = 65536000
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
@ -1823,12 +1841,17 @@ func TestRename(t *testing.T) {
err = os.Remove(testFilePath)
assert.NoError(t, err)
if user.Username == defaultUsername {
user.Permissions = make(map[string][]string)
user.Permissions["/"] = allPerms
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Permissions = make(map[string][]string)
user.Permissions["/"] = allPerms
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
@ -1885,6 +1908,13 @@ func TestSymlink(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
err = os.Remove(testFilePath)
@ -1935,6 +1965,13 @@ func TestStat(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -2284,6 +2321,13 @@ func TestChtimes(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -2366,6 +2410,13 @@ func TestChmod(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -2523,6 +2574,13 @@ func TestHASH(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -2577,6 +2635,13 @@ func TestCombine(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}

18
go.mod
View file

@ -8,7 +8,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
github.com/aws/aws-sdk-go v1.43.2
github.com/aws/aws-sdk-go v1.43.5
github.com/cockroachdb/cockroach-go/v2 v2.2.8
github.com/coreos/go-oidc/v3 v3.1.0
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001
@ -26,8 +26,8 @@ require (
github.com/hashicorp/go-plugin v1.4.3
github.com/hashicorp/go-retryablehttp v0.7.0
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
github.com/klauspost/compress v1.14.3
github.com/lestrrat-go/jwx v1.2.18
github.com/klauspost/compress v1.14.4
github.com/lestrrat-go/jwx v1.2.19
github.com/lib/pq v1.10.4
github.com/lithammer/shortuuid/v3 v3.0.7
github.com/mattn/go-sqlite3 v1.14.11
@ -57,16 +57,16 @@ require (
gocloud.dev v0.24.0
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
golang.org/x/sys v0.0.0-20220209214540-3681064d5158
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
google.golang.org/api v0.69.0
google.golang.org/api v0.70.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
)
require (
cloud.google.com/go v0.100.2 // indirect
cloud.google.com/go/compute v1.3.0 // indirect
cloud.google.com/go/compute v1.5.0 // indirect
cloud.google.com/go/iam v0.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v0.9.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@ -98,7 +98,7 @@ require (
github.com/lestrrat-go/iter v1.0.1 // indirect
github.com/lestrrat-go/option v1.0.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
@ -128,7 +128,7 @@ require (
golang.org/x/tools v0.1.9 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c // indirect
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf // indirect
google.golang.org/grpc v1.44.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect

30
go.sum
View file

@ -47,8 +47,9 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/compute v1.2.0/go.mod h1:xlogom/6gr8RJGBe7nT2eGsQYAFUbbv8dbC29qE3Xmw=
cloud.google.com/go/compute v1.3.0 h1:mPL/MzDDYHsh5tHRS9mhmhWlcgClCrCa6ApQCU6wnHI=
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiLM=
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.5.0/go.mod h1:c4nNYR1qdq7eaZ+jSc5fonrQN2k3M7sWATcYTiakjEo=
@ -143,8 +144,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go v1.43.2 h1:T6LuKCNu8CYXXDn3xJoldh8FbdvuVH7C9aSuLNrlht0=
github.com/aws/aws-sdk-go v1.43.2/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc=
github.com/aws/aws-sdk-go v1.43.5 h1:N7arnx54E4QyW69c45UW5o8j2DCSjzpoxzJW3yU6OSo=
github.com/aws/aws-sdk-go v1.43.5/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY=
github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY=
@ -513,8 +514,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.14.3 h1:DQv1WP+iS4srNjibdnHtqu8JNWCDMluj5NzPnFJsnvk=
github.com/klauspost/compress v1.14.3/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4=
github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.11 h1:i2lw1Pm7Yi/4O6XCSyJWqEHI2MDw2FzUK6o/D21xn2A=
github.com/klauspost/cpuid/v2 v2.0.11/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
@ -543,8 +544,8 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++
github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A=
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
github.com/lestrrat-go/jwx v1.2.6/go.mod h1:tJuGuAI3LC71IicTx82Mz1n3w9woAs2bYJZpkjJQ5aU=
github.com/lestrrat-go/jwx v1.2.18 h1:RV4hcTRUlPVYUnGqATKXEojoOsLexoU8Na4KheVzxQ8=
github.com/lestrrat-go/jwx v1.2.18/go.mod h1:bWTBO7IHHVMtNunM8so9MT8wD+euEY1PzGEyCnuI2qM=
github.com/lestrrat-go/jwx v1.2.19 h1:qxxLmAXNwZpTTvjc4PH21nT7I4wPK6lVv3lVNcZPnUk=
github.com/lestrrat-go/jwx v1.2.19/go.mod h1:bWTBO7IHHVMtNunM8so9MT8wD+euEY1PzGEyCnuI2qM=
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@ -560,8 +561,9 @@ github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaK
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@ -854,8 +856,9 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -953,8 +956,9 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 h1:BXxu8t6QN0G1uff4bzZzSkpsax8+ALqTGUtz08QrV00=
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -1087,8 +1091,9 @@ google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tD
google.golang.org/api v0.64.0/go.mod h1:931CdxA8Rm4t6zqTFGSsgwbAEZ2+GMYurbndwSimebM=
google.golang.org/api v0.66.0/go.mod h1:I1dmXYpX7HGwz/ejRxwQp2qj5bFAz93HiCU1C1oYd9M=
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
google.golang.org/api v0.69.0 h1:yHW5s2SFyDapr/43kYtIQmoaaFVW4baLMLwqV4auj2A=
google.golang.org/api v0.69.0/go.mod h1:boanBiw+h5c3s+tBPgEzLDRHfFLWV0qXxRHz3ws7C80=
google.golang.org/api v0.70.0 h1:67zQnAE0T2rB0A3CwLSas0K+SbVzSxP+zTLkQLexeiw=
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -1178,8 +1183,9 @@ google.golang.org/genproto v0.0.0-20220201184016-50beb8ab5c44/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220211171837-173942840c17/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220216160803-4663080d8bc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c h1:TU4rFa5APdKTq0s6B7WTsH6Xmx0Knj86s6Biz56mErE=
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf h1:SVYXkUz2yZS9FWb2Gm8ivSlbNQzL2Z/NpPKE3RG2jWk=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=

View file

@ -5,7 +5,6 @@ import (
"fmt"
"net/http"
"strings"
"time"
"github.com/go-chi/jwtauth/v5"
"github.com/lestrrat-go/jwx/jwt"
@ -406,15 +405,11 @@ func authenticateUserWithAPIKey(username, keyID string, tokenAuth *jwtauth.JWTAu
updateLoginMetrics(&user, dataprovider.LoginMethodPassword, ipAddr, err)
return err
}
lastLogin := util.GetTimeFromMsecSinceEpoch(user.LastLogin)
diff := -time.Until(lastLogin)
if diff < 0 || diff > 10*time.Minute {
defer user.CloseFs() //nolint:errcheck
err = user.CheckFsRoot(connectionID)
if err != nil {
updateLoginMetrics(&user, dataprovider.LoginMethodPassword, ipAddr, common.ErrInternalFailure)
return common.ErrInternalFailure
}
defer user.CloseFs() //nolint:errcheck
err = user.CheckFsRoot(connectionID)
if err != nil {
updateLoginMetrics(&user, dataprovider.LoginMethodPassword, ipAddr, common.ErrInternalFailure)
return common.ErrInternalFailure
}
c := jwtTokenClaims{
Username: user.Username,

View file

@ -762,6 +762,13 @@ func TestOpenReadWritePerm(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -1063,6 +1070,13 @@ func TestUploadResume(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -1290,6 +1304,13 @@ func TestStat(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -1340,6 +1361,13 @@ func TestStatChownChmod(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -1384,11 +1412,13 @@ func TestSFTPFsLoginWrongFingerprint(t *testing.T) {
sftpUser.FsConfig.SFTPConfig.Fingerprints = []string{"wrong"}
_, _, err = httpdtest.UpdateUser(sftpUser, http.StatusOK, "")
assert.NoError(t, err)
_, _, err = getSftpClient(sftpUser, usePubKey)
assert.Error(t, err)
_, err = runSSHCommand("md5sum", sftpUser, usePubKey)
assert.Error(t, err)
conn, client, err = getSftpClient(sftpUser, usePubKey)
if assert.NoError(t, err) {
defer conn.Close()
defer client.Close()
err = checkBasicSFTP(client)
assert.Error(t, err)
}
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
assert.NoError(t, err)
@ -1438,6 +1468,13 @@ func TestChtimes(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -3816,13 +3853,14 @@ func TestQuotaFileReplace(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
user.UsedQuotaFiles = 0
user.UsedQuotaSize = 0
_, err = httpdtest.UpdateQuotaUsage(user, "reset", http.StatusOK)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
user.QuotaSize = 0
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
@ -3914,10 +3952,13 @@ func TestQuotaRename(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
user.UsedQuotaFiles = 0
user.UsedQuotaSize = 0
_, err = httpdtest.UpdateQuotaUsage(user, "reset", http.StatusOK)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -4078,10 +4119,13 @@ func TestQuotaLimits(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
user.UsedQuotaFiles = 0
user.UsedQuotaSize = 0
_, err = httpdtest.UpdateQuotaUsage(user, "reset", http.StatusOK)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
err = os.Remove(testFilePath)
@ -5078,13 +5122,14 @@ func TestTruncateQuotaLimits(t *testing.T) {
// cleanup
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
user.UsedQuotaFiles = 0
user.UsedQuotaSize = 0
_, err = httpdtest.UpdateQuotaUsage(user, "reset", http.StatusOK)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
user.QuotaSize = 0
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
}
@ -7323,10 +7368,15 @@ func TestRootDirCommands(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
user.Permissions = make(map[string][]string)
user.Permissions["/"] = []string{dataprovider.PermAny}
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
@ -8900,6 +8950,13 @@ func TestSCPBasicHandling(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
@ -8975,6 +9032,13 @@ func TestSCPUploadFileOverwrite(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
err = os.Remove(testFilePath)
@ -9043,6 +9107,13 @@ func TestSCPRecursive(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}

View file

@ -353,11 +353,6 @@ func (fs *AzureBlobFs) Mkdir(name string) error {
return w.Close()
}
// MkdirAll does nothing, we don't have folder
func (*AzureBlobFs) MkdirAll(name string, uid int, gid int) error {
return nil
}
// Symlink creates source as a symbolic link to target.
func (*AzureBlobFs) Symlink(source, target string) error {
return ErrVfsUnsupported

View file

@ -317,11 +317,6 @@ func (fs *GCSFs) Mkdir(name string) error {
return w.Close()
}
// MkdirAll does nothing, we don't have folder
func (*GCSFs) MkdirAll(name string, uid int, gid int) error {
return nil
}
// Symlink creates source as a symbolic link to target.
func (*GCSFs) Symlink(source, target string) error {
return ErrVfsUnsupported

View file

@ -128,13 +128,6 @@ func (*OsFs) Mkdir(name string) error {
return os.Mkdir(name, os.ModePerm)
}
// MkdirAll creates a directory named path, along with any necessary parents,
// and returns nil, or else returns an error.
// If path is already a directory, MkdirAll does nothing and returns nil.
func (fs *OsFs) MkdirAll(name string, uid int, gid int) error {
return fs.createMissingDirs(name, uid, gid)
}
// Symlink creates source as a symbolic link to target.
func (*OsFs) Symlink(source, target string) error {
return os.Symlink(source, target)
@ -418,23 +411,6 @@ func (fs *OsFs) isSubDir(sub string) error {
return nil
}
func (fs *OsFs) createMissingDirs(filePath string, uid, gid int) error {
dirsToCreate, err := fs.findNonexistentDirs(filePath)
if err != nil {
return err
}
last := len(dirsToCreate) - 1
for i := range dirsToCreate {
d := dirsToCreate[last-i]
if err := os.Mkdir(d, os.ModePerm); err != nil {
fsLog(fs, logger.LevelError, "error creating missing dir: %#v", d)
return err
}
SetPathPermissions(fs, d, uid, gid)
}
return nil
}
// GetMimeType returns the content type
func (fs *OsFs) GetMimeType(name string) (string, error) {
f, err := os.OpenFile(name, os.O_RDONLY, 0)

View file

@ -90,22 +90,7 @@ func NewS3Fs(connectionID, localTempDir, mountPath string, config S3FsConfig) (F
if fs.config.ForcePathStyle {
awsConfig.S3ForcePathStyle = aws.Bool(true)
}
if fs.config.UploadPartSize == 0 {
fs.config.UploadPartSize = s3manager.DefaultUploadPartSize
} else {
fs.config.UploadPartSize *= 1024 * 1024
}
if fs.config.UploadConcurrency == 0 {
fs.config.UploadConcurrency = s3manager.DefaultUploadConcurrency
}
if fs.config.DownloadPartSize == 0 {
fs.config.DownloadPartSize = s3manager.DefaultDownloadPartSize
} else {
fs.config.DownloadPartSize *= 1024 * 1024
}
if fs.config.DownloadConcurrency == 0 {
fs.config.DownloadConcurrency = s3manager.DefaultDownloadConcurrency
}
fs.setConfigDefaults()
sessOpts := session.Options{
Config: *awsConfig,
@ -392,11 +377,6 @@ func (fs *S3Fs) Mkdir(name string) error {
return w.Close()
}
// MkdirAll does nothing, we don't have folder
func (*S3Fs) MkdirAll(name string, uid int, gid int) error {
return nil
}
// Symlink creates source as a symbolic link to target.
func (*S3Fs) Symlink(source, target string) error {
return ErrVfsUnsupported
@ -742,6 +722,29 @@ func (fs *S3Fs) checkIfBucketExists() error {
return err
}
func (fs *S3Fs) setConfigDefaults() {
if fs.config.UploadPartSize == 0 {
fs.config.UploadPartSize = s3manager.DefaultUploadPartSize
} else {
if fs.config.UploadPartSize < 1024*1024 {
fs.config.UploadPartSize *= 1024 * 1024
}
}
if fs.config.UploadConcurrency == 0 {
fs.config.UploadConcurrency = s3manager.DefaultUploadConcurrency
}
if fs.config.DownloadPartSize == 0 {
fs.config.DownloadPartSize = s3manager.DefaultDownloadPartSize
} else {
if fs.config.DownloadPartSize < 1024*1024 {
fs.config.DownloadPartSize *= 1024 * 1024
}
}
if fs.config.DownloadConcurrency == 0 {
fs.config.DownloadConcurrency = s3manager.DefaultDownloadConcurrency
}
}
func (fs *S3Fs) hasContents(name string) (bool, error) {
prefix := ""
if name != "/" && name != "." {

View file

@ -349,16 +349,6 @@ func (fs *SFTPFs) Mkdir(name string) error {
return fs.sftpClient.Mkdir(name)
}
// MkdirAll creates a directory named path, along with any necessary parents,
// and returns nil, or else returns an error.
// If path is already a directory, MkdirAll does nothing and returns nil.
func (fs *SFTPFs) MkdirAll(name string, uid int, gid int) error {
if err := fs.checkConnection(); err != nil {
return err
}
return fs.sftpClient.MkdirAll(name)
}
// Symlink creates source as a symbolic link to target.
func (fs *SFTPFs) Symlink(source, target string) error {
if err := fs.checkConnection(); err != nil {
@ -459,7 +449,10 @@ func (fs *SFTPFs) CheckRootPath(username string, uid int, gid int) bool {
if fs.config.Prefix == "/" {
return true
}
if err := fs.MkdirAll(fs.config.Prefix, uid, gid); err != nil {
if err := fs.checkConnection(); err != nil {
return false
}
if err := fs.sftpClient.MkdirAll(fs.config.Prefix); err != nil {
fsLog(fs, logger.LevelDebug, "error creating root directory %#v for user %#v: %v", fs.config.Prefix, username, err)
return false
}

View file

@ -73,7 +73,6 @@ type Fs interface {
Rename(source, target string) error
Remove(name string, isDir bool) error
Mkdir(name string) error
MkdirAll(name string, uid int, gid int) error
Symlink(source, target string) error
Chown(name string, uid int, gid int) error
Chmod(name string, mode os.FileMode) error

View file

@ -576,6 +576,13 @@ func TestBasicHandling(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
@ -1484,10 +1491,15 @@ func TestQuotaLimits(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
user.QuotaFiles = 0
user.QuotaSize = 0
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
@ -1581,9 +1593,14 @@ func TestUploadMaxSize(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
user.Filters.MaxUploadFileSize = 65536000
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Filters.MaxUploadFileSize = 65536000
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
@ -2195,9 +2212,14 @@ func TestMiscCommands(t *testing.T) {
if user.Username == defaultUsername {
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
user.QuotaFiles = 0
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
user.Password = defaultPassword
user.ID = 0
user.CreatedAt = 0
user.QuotaFiles = 0
_, resp, err := httpdtest.AddUser(user, http.StatusCreated)
assert.NoError(t, err, string(resp))
}
}
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)