mirror of
https://github.com/drakkan/sftpgo.git
synced 2026-10-20 00:16:38 +00:00
Confine all local filesystem access to an os.Root, with path resoultion enforced by the underlying OS. Behavior changes an upgrading deployment may notice: - temp_path is removed and ignored if still configured. Its purpose was to stage files (atomic uploads, scratch and streaming data) outside the user's storage and rename them into place, and that is fundamentally incompatible with the confinement: an os.Root cannot rename a file in from outside the root, so every staged upload would degrade to a full copy-then-delete even when temp_path is on the same filesystem, defeating the very atomicity the option existed to provide. It was not a great idea before this change either: a temp_path on a different device already turned the final rename into a silent full copy. Staged and scratch files (the atomic upload temporary, the CryptFs download pipe backing file) now live in the storage root itself, on the same filesystem as the data, so the final rename stays atomic. - Cross-folder / cross-root renames are no longer atomic. A rename spanning the home and a local virtual folder, or two local folders, falls back to copy-then-delete: slower for large data even on the same device, and a midway failure can leave a partial tree at the destination. Symlinks and other non-regular entries inside a moved directory are skipped, not copied, and the rename still reports success. File mode bits are not preserved; regular files keep their modification time, directories do not. - Absolute symlinks created outside SFTPGo, or by versions predating this change, are no longer traversable and Readlink rejects them with permission denied, even when the target is inside the root: os.Root refuses an absolute link target regardless of where it points. Symlinks created through SFTPGo are stored relative to the link directory and keep working. - Execute-only home directories (e.g. mode 0111) are unusable: os.OpenRoot requires read permission, so every operation fails with permission denied until the directory is made readable, where previously only the listing failed. Since the root open is retried on demand, fixing the permissions (or creating a missing home externally) makes the same connection work again without reconnecting. Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
45 lines
1.2 KiB
Go
45 lines
1.2 KiB
Go
// Copyright (C) 2019 Nicola Murino
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published
|
|
// by the Free Software Foundation, version 3.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
//go:build linux
|
|
|
|
package vfs
|
|
|
|
import (
|
|
"os"
|
|
|
|
"github.com/pkg/sftp"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func getStatFS(f *os.File, _ string) (*sftp.StatVFS, error) {
|
|
stat := unix.Statfs_t{}
|
|
err := unix.Fstatfs(int(f.Fd()), &stat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &sftp.StatVFS{
|
|
Bsize: uint64(stat.Bsize),
|
|
Frsize: uint64(stat.Frsize),
|
|
Blocks: stat.Blocks,
|
|
Bfree: stat.Bfree,
|
|
Bavail: stat.Bavail,
|
|
Files: stat.Files,
|
|
Ffree: stat.Ffree,
|
|
Favail: stat.Ffree, // not sure how to calculate Favail
|
|
Flag: uint64(stat.Flags),
|
|
Namemax: uint64(stat.Namelen),
|
|
}, nil
|
|
}
|