Files
Nicola Murino 661d77f47b vfs: migrate OsFs/CryptFs to os.Root and remove temp_path
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>
2026-07-06 13:08:12 +02:00

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
}