-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
os: NTFS deduped file changed from regular to irregular #63429
Comments
I don't think that's accurate. A symlink would have the |
POSIX defines a “regular file” as “a randomly accessible sequence of bytes, with no further structure imposed by the system.” Under that definition (which I think is the one the Go Reparse points by definition do have “further structure imposed by the system” — namely, they have reparse tags and are handled by file system filters associated with those reparse tags. And some kinds of reparse points, such as On the other hand, I would expect that at least some kinds of reparse points do represent “a randomly accessible sequence of bytes” from the perspective of an application. If we can explicitly enumerate which reparse points have that property, perhaps it would be ok to treat them as regular files? But they do still have “further structure imposed by the system”, so maybe not — I'm really not sure. In the particular case of (CC @golang/windows) |
Change https://go.dev/cl/536655 mentions this issue: |
It is probably expecting too much from A good compromise could be to provide an easy way to access the reparse tag that is currently unexported in package fs
type ReparseTagFileInfo interface {
FileInfo
ReparseTag() uint32
} package os
func (fs *fileStat) ReparseTag() uint32 {
return fs._ReparseTag
} Which would be used like this: var fi, err = os.Lstat(name)
if fi.Mode()&fs.ModeIrregular {
if fix, ok := fi.(fs.ReparseTagFileInfo); ok {
switch fi.ReparseTag() {
case syscall.IO_REPARSE_TAG_DEDUP:
// custom logic
}
}
} |
That would also be useful for us indeed - we currently do just what you say, we use syscalls on every irregular file to detect reparse points to treat them like regular ones. |
I agree with the Boost developers that turning on NTFS deduplication should not make ordinary files look "irregular". People will turn this on to save some disk space, and then Go programs that use .IsRegular() to avoid opening printer devices and such will stop processing their files. That's not right for a portable os layer. Whatever we decide for #61893, I think we should special-case the dedup reparse points and report them as regular files. If the behavior is new in Go 1.21 (wasn't present in Go 1.20), I think we should probably treat this as a critical bug fix and backport it. |
Change https://go.dev/cl/537915 mentions this issue: |
@imsodin, I have a proposed patch in https://go.dev/cl/537915 but I can't easily test it. (I don't have a Windows Server instance with deduplication enabled.) If you can build that commit from source (https://go.dev/doc/install/source) and confirm that it fixes the problem for you or your users, I will request a backport to Go 1.21. (Note that Gerrit has a “Download patch” option in the |
@bcmills Your CL is equivalent to the workaround we added after upgrading to go1.21 and encountering the issue, and the user already confirmed it did fix the issue for them: syncthing/syncthing@9553365. |
I agree that the approach is equivalent — mostly I want to rule out bugs in my implementation of it. 😅 But I'll go ahead and start the backport process based on that information. |
@gopherbot, please backport to Go 1.21. This was a behavior change in Go 1.21 impacting users of the |
Backport issue(s) opened: #63764 (for 1.21). Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://go.dev/wiki/MinorReleases. |
Change https://go.dev/cl/538218 mentions this issue: |
…lar in Stat and Lstat Prior to CL 460595, Lstat reported most reparse points as regular files. However, reparse points can in general implement unusual behaviors (consider IO_REPARSE_TAG_AF_UNIX or IO_REPARSE_TAG_LX_CHR), and Windows allows arbitrary user-defined reparse points, so in general we must not assume that an unrecognized reparse tag represents a regular file; in CL 460595, we began marking them as irregular. As it turns out, the Data Deduplication service on Windows Server runs an Optimization job that turns regular files into reparse files with the tag IO_REPARSE_TAG_DEDUP. Those files still behave more-or-less like regular files, in that they have well-defined sizes and support random-access reads and writes, so most programs can treat them as regular files without difficulty. However, they are still reparse files: as a result, on servers with the Data Deduplication service enabled, files could arbitrarily change from “regular” to “irregular” without explicit user intervention. Since dedup files are converted in the background and otherwise behave like regular files, this change adds a special case to report DEDUP reparse points as regular. Fixes #63764. Updates #63429. No test because to my knowledge we don't have any Windows builders that have the deduplication service enabled, nor do we have a way to reliably guarantee the existence of an IO_REPARSE_TAG_DEDUP file. (In theory we could add a builder with the service enabled on a specific volume, write a test that encodes knowledge of that volume, and use the GO_BUILDER_NAME environment variable to run that test only on the specially-configured builders. However, I don't currently have the bandwidth to reconfigure the builders in this way, and given the simplicity of the change I think it is unlikely to regress accidentally.) Change-Id: I649e7ef0b67e3939a980339ce7ec6a20b31b23a1 Cq-Include-Trybots: luci.golang.try:go1.21-windows-amd64-longtest Reviewed-on: https://go-review.googlesource.com/c/go/+/538218 Reviewed-by: Alex Brainman <alex.brainman@gmail.com> Reviewed-by: David Chase <drchase@google.com> Auto-Submit: Heschi Kreinick <heschi@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Change https://go.dev/cl/565136 mentions this issue: |
This CL changes the behavior of os.Lstat to stop setting the os.ModeSymlink type mode bit for mount points on Windows. As a result, filepath.EvalSymlinks no longer evaluates mount points, which was the cause of many inconsistencies and bugs. Additionally, os.Lstat starts setting the os.ModeIrregular type mode bit for all reparse tags on Windows, except for those that are explicitly supported by the os package, which, since this CL, doesn't include mount points. This helps to identify files that need special handling outside of the os package. This behavior is controlled by the `winsymlink` GODEBUG setting. For Go 1.23, it defaults to `winsymlink=1`. Previous versions default to `winsymlink=0`. Fixes #39786 Fixes #40176 Fixes #61893 Updates #63703 Updates #40180 Updates #63429 Cq-Include-Trybots: luci.golang.try:gotip-windows-amd64-longtest,gotip-windows-arm64 Change-Id: I2e7372ab8862f5062667d30db6958d972bce5407 Reviewed-on: https://go-review.googlesource.com/c/go/+/565136 Reviewed-by: Bryan Mills <bcmills@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Knyszek <mknyszek@google.com>
This issue is a followup to a golang-nuts discussion: https://groups.google.com/g/golang-nuts/c/YtBDdIgxazY/m/-X1Gc0xWBwAJ
@qmuntal asked me to file an issue there.
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
I didn't try, but given the underlying issue (see below) it's unlikely to have changed - I don't have a system to repro.
What operating system and processor architecture are you using (
go env
)?Same here, not my system where the repro happened: A windows system with an ntfs filesystem with deduplication is needed
What did you do?
os.Lstat
on the deduplicated file.What did you expect to see?
In go <=1.20 the result of
os.Lstat
gave.IsRegular() == true
, which is expected.What did you see instead?
In go1.21 it's false and it's considered a symlink.
This broke syncing of those files in syncthing, where this issue was originally reported: syncthing/syncthing#9120
Quoting @qmuntal from golang-nuts:
Also given go <=1.20 already had the desired behaviour (deduped file was considered regular), I'd personally consider this a regression and would appreciated if a fix could be backported to go1.21.
The text was updated successfully, but these errors were encountered: