Go: 建议:操作系统:在 Windows 上创建/打开/打开文件()设置 FILE_SHARE_DELETE

创建于 2019-05-16  ·  194评论  ·  资料来源: golang/go

在 Linux 和 MacOS 上我们可以这样写; 在 Windows 上,它因“共享冲突”而失败:

path := "delete-after-open"
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
if err != nil { ... }
err = os.Remove(path)            // or os.Rename(path, path+"2")
if err != nil { ... }
fd.Close()

如果您在 Windows 上开发并部署到 Linux 等,并且您的代码依赖于 os.Rename() 和 .Remove() 的 _undocumented_ GOOS=windows 行为,则它已损坏并且可能易受攻击。 请注意,包“os”有 _15 次提及_其他 Windows 特定的行为。

要解决此问题,请使用 https://golang.org/src/syscall/syscall_windows.go#L272 上的syscall.Open()
需要sharemode |= FILE_SHARE_DELETE

Microsoft建议将其设为默认值: https :
Rust将其https :
七年前, Mingw-w64将其
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858
六年前, Erlang将其设为默认值:erlang/
由于 MSVC 运行时的限制, Python无法采用它: https :

~因此 syscall.Open() 应该默认使用 file_share_delete ,并且 syscall 应该同时提供:
a) 一个全局开关来禁用它(对于任何依赖它缺失的现有应用程序),以及
b) 与 os.OpenFile() 一起使用的标志,以在特定文件句柄上禁用它。~

@rsc https://github.com/golang/go/issues/32088#issuecomment-532784947 之后更新:
a) os.Create/Open/OpenFile() 应始终在 Windows 上
b) Windows 上的 syscall.Open() 应该接受一个启用 file_share_delete 的标志,并且
c) Windows 上的系统调用应该为新标志导出一个常量。

os.Remove() 的文档还应注意,要在 Windows 上删除打开的文件后重用文件名,必须这样做: os.Rename(path, unique_path); os.Remove(unique_path)

赢取 API 文档
https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-deletefilea
https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea

如果有理由不这样做,则应记录在 os.Remove() 和 .Rename() 中。

抄送@alexbrainman
@gopherbot添加操作系统-Windows

FrozenDueToAge OS-Windows Proposal Proposal-FinalCommentPeriod release-blocker

最有用的评论

我将尝试在这里代表 Windows 团队的观点……我们宁愿 Go 始终设置 FILE_SHARE_DELETE,没有任何选项。 一般来说,为了更容易移植到/从 Windows,我们更喜欢与 Linux 一致的行为,我不明白为什么 Windows 用户或软件更喜欢默认的 !FILE_SHARE_DELETE 行为,这足以成为规则的例外。

一个有趣的注释,与@mattn的评论相反:在最新版本的 Windows 中,我们更新了 DeleteFile(在 NTFS 上)以执行“POSIX”删除,其中文件立即从命名空间中删除,而不是等待所有打开要关闭的文件的句柄。 它仍然尊重 FILE_SHARE_DELETE,但现在的行为更像 POSIX 取消链接。 此功能是为 WSL 添加的,并且默认情况下也被认为值得用于 Windows 软件。

换句话说,如果你在最新版本的 Windows 上运行 mattn 的测试程序和 del、dir 等序列,你会看到文件在删除文件后立即从命名空间中消失,而不是在测试程序退出后。 就像Linux一样。

因此,即使在 Windows 本身中,由于其 appcompat 风险要大得多,我们也在做一些小改动,以简化将非 Windows 软件移植到 Windows 的过程。 我真的鼓励 Go 也这样做。

所有194条评论

/cc @alexbrainman

syscall.Open() 在https://golang.org/src/syscall/syscall_windows.go#L272
应该使用sharemode := ... | FILE_SHARE_DELETE

为什么要呢?

亚历克斯

FILE_SHARE_DELETE 启用上面的代码示例,它适用于 MacOS 和 Linux,但在 Windows 上失败。 这也是必要的:

fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
defer fd.Close()
_, err = fd.Write(...)
err = fd.Sync()  // file now safe to share
err = os.Rename(path, path+"2")
_, err = fd.Read(...)

FILE_SHARE_DELETE 启用上面的代码示例,它适用于 MacOS 和 Linux,但在 Windows 上失败。

为什么我们不调整 MacOS 和 Linux 代码呢?

这也是必要的:

我不明白你想说什么。

亚历克斯

@networkimprov您必须在 Windows 上的 Close() 之后调用 Remove。

path := "delete-after-open"
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
if err != nil { panic(err) }
fd.Close()
err = os.Remove(path)            // or os.Rename(path, path+"2")
if err != nil { panic(err) }

@alexbrainman在进行可靠的文件 I/O(对于数据库)时,标准做法是创建一个具有临时名称的文件、写入、fsync 并重命名它。

默认情况下,在 Unix 上可以重命名或删除打开的文件。 未设置此功能的 Windows 标志似乎是一种疏忽。 我怀疑我们会说服 Go 团队改变它在 Linux 和 MacOS 上的工作方式:-)

@mattn请应用我描述的修复并尝试我发布的代码。

我怀疑我们会说服 Go 团队改变它在 Linux 和 MacOS 上的工作方式:-)

我现在的情况很好。

亚历克斯

在 Windows 中省略这种常见功能是否有理由?

您能否在 syscall_windows.go 中提供一个开关,以便我们可以在程序启动时选择 Unix 行为?

我可以添加新的 API 或标志。 但我反对改变当前的行为。 由于 FILE_SHARE_DELETE 与 Unix 行为不同。

#include <windows.h>
#include <stdio.h>

int
main(int argc, char* argv[]) {
  HANDLE h = CreateFile("test.txt",
      GENERIC_READ | GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
      NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  getchar();
  char buf[256] = {0};
  DWORD nread;
  printf("%d\n", ReadFile(h, buf, 256, &nread, NULL));
  printf("%s,%d\n", buf, nread);
  return 0;
}

在 Windows 上完成此代码,并尝试以test.exe 。 在此应用程序等待按键时,打开新的 cmd.exe,删除“test.txt”,如下所示。

image

该文件可以被删除,但在进程存在时仍保留在那里。 因此,此更改不会满足您的预期。

我意识到目录条目没有被删除,但文档说

随后调用 CreateFile 以打开文件失败并显示 ERROR_ACCESS_DENIED。

所以我不明白你的屏幕日志。

无论如何,像这样的开关会很好:

syscall.OpenFileShareDelete = true

我想提一下,我以前在工作中被这个咬过。

基本上我们有:

f, err := os.Open(...)
if err != nil { ... }
defer f.Close()

// lots of code

if err := os.Rename(...); err != nil { ... }

该代码在我们基于 unix 的 CI 平台上运行良好,但在 Windows 上却爆炸式增长。

假设没有任何奇怪的副作用,如果一切正常就好了

IIRC,我们过去对 GOOS=windows 和 plan9 的行为进行了一些调整,以更接近 Unix 语义。 如果语义足够接近,我不介意让这成为另一种情况。 但是, @mattn的评论表明该行为不够接近,因此可能不值得。

不过,我不想看到一些全局选项。 这似乎是一场调试噩梦。

@布拉菲茨
可能不会,请在此处参考我的评论
https://groups.google.com/forum/#!topic/golang -dev/R79TJAzsBfM
或者,如果您愿意,我可以在此处复制内容,因为还有一个可能的解决方案,尽管我不会将其实现为默认行为,因为这与 windows 程序的行为方式不一致,而是一种创建类似交叉的解决方法-os 经验。

@guybrand在 golang-dev 线程中写道:

我的程序正在编写一个名为“my.data”的文件,然后调用另一个程序并且不等待它结束......这个程序可以说上传这个文件需要 10 秒(让我们称这个另一个程序为“uploader”) .
...
当我的程序在 Windows 上调用 .Remove 时(FILE_SHARE_DELETE 已打开):
...
上传者会收到一个错误,告诉它文件已被删除(可能是 EOF)。

假设“上传程序”在“我的程序”调用 os.Remove() 之前打开文件,我认为您与 Win API 文档相矛盾:

_DeleteFile 在关闭时标记要删除的文件。 因此,在文件的最后一个句柄关闭之前,不会发生文件删除。 随后调用 CreateFile 以打开文件失败并显示 ERROR_ACCESS_DENIED._

重新“将 CreateFile 的 _open_osfhandle() 与 _fdopen 配对”,你能指出示例代码吗?

如果我们添加 FILE_SHARE_DELETE,很多程序员会误用它来模拟 Unix 行为。 在这种情况下,您可以为每个操作系统创建一个由构建约束分隔的函数。 它应该返回os.NewFile() ,在 Windows 上使用 FILE_SHARE_DELETE 。

为每个操作系统创建一个由构建约束分隔的函数...返回 os.NewFile(),在 Windows 上使用 FILE_SHARE_DELETE

为了保留 os.OpenFile() 的功能,这个计划似乎需要重新实现所有:

os.OpenFile()
openFileNolog()
打开文件()
打开目录()
新文件()
修复长路径()
系统调用.Open()
makeInheritSa()

@networkimprov

@guybrand在 golang-dev 线程中写道:

我的程序正在编写一个名为“my.data”的文件,然后调用另一个程序并且不等待它结束......这个程序可以说上传这个文件需要 10 秒(让我们称这个另一个程序为“uploader”) .
...
当我的程序在 Windows 上调用 .Remove 时(FILE_SHARE_DELETE 已打开):
...
上传者会收到一个错误,告诉它文件已被删除(可能是 EOF)。

假设“上传程序”在“我的程序”调用 os.Remove() 之前打开文件,您是否与 Win API 文档相矛盾?

DeleteFile 在关闭时标记要删除的文件。 因此,在文件的最后一个句柄关闭之前,不会发生文件删除。 随后调用 CreateFile 以打开文件失败并显示 ERROR_ACCESS_DENIED。

重新“将 CreateFile 的 _open_osfhandle() 与 _fdopen 配对”,你能指出示例代码吗?

我没有看到与 WIndows API 的矛盾,请指出看起来像矛盾的地方。

至于代码示例,我用谷歌搜索了一下,发现了这个:
http://blog.httrack.com/blog/2013/10/05/creating-deletable-and-movable-files-on-windows/


请注意 - 这只会在内部为您提供类似 nix 的行为,任何其他使用它的外部程序都会破坏它(因为它 99% 使用标准 Windows API

@networkimprov
如果你的意思是“这听起来像“dir my.data”会显示文件,据我所知,这是直到 windows 的行为.... XP 或 7(不记得是哪个)并且从那时起发生了变化(也许资源管理器只是隐藏它以某种方式) - 我可以在下次启动 Windows 环境时重新测试。(每隔几周发生一次......)

如果您的意思是“这听起来像其他具有文件句柄的进程仍然能够读取和写入文件”,我敢打赌,当您读取写入此类文件时,您确实会收到“EOF”样式错误- 但再次需要确认是 100% 积极的。

在任何一种情况下,我的观点都是(在这一点上——我在“native like”与“os-agnostic”点中采取“立场”)——即使你实施 _fdopen 风格的解决方案,你的服务和您与之协作的所有其他可执行文件,因此只能与其他 go 可执行文件(或直接使用 fd 的罕见服务)交互。

换句话说——你的应用程序将是“班上最聪明的孩子”——其他孩子都无法理解。
我能想到的两个例子:
输入
我的应用程序下载一个文件,防病毒软件识别它的 harfull 并删除/隔离(== 重命名),如果我使用 fd - 我的应用程序仍然可以用它做任何事情(这是 coll,但可能会结束遇到病毒了……)
输出
我的应用程序将文件通过管道传输到另一个(“上传器”之类的)服务,然后将其删除,我什至费心并在 go 中编写了一个测试器,以查看一切正常 - 并且测试通过了。
现在,我使用 filezilla,而不是我的 go 测试,我们传输,dropbx API 等等
它会失败/不会以与我的测试工作相同的方式运行...

您仍然认为将其更改为默认行为有意义吗?

在 Windows 中省略这种常见功能是否有理由?

我从来没有考虑过这个问题。 我不知道。

Go 文件在 Windows 上的工作方式与我生活中使用的所有其他开发人员工具一致。 如果 Go 文件能按照您的建议工作,我会感到惊讶。 我也怀疑,它会破坏许多现有的程序。

亚历克斯

我也怀疑,它会破坏许多现有的程序。

@alexbrainman我还建议使用开关来启用它,而不是更改默认值。

@bradfitz有 syscall.SocketDisableIPv6,这与调整 syscall.Open() 行为的标志并没有什么不同。

由于 syscall_windows*.go 状态
func Open(path string, mode int, perm uint32) (fd Handle, err error) {
……
共享模式:= uint32(FILE_SHARE_READ | FILE_SHARE_WRITE)

和 type_windows*.go 有
FILE_SHARE_DELETE = 0x00000004

一切准备就绪,唯一的问题是如何实现标志。
我不喜欢全局选项以及它的不明确性,虽然单个开发人员可能记得他设置了 os.DeleteUponLastHandleClosed = 1,但这对于长期或多个开发人员来说不是一个好习惯。
其他选项可以为标志设置特定的保留编号,例如:
fd,错误:= os.OpenFile(路径,os.O_RDWR|os.O_CREATE|os.DELETE_WHEN_FREED,0600)
而对于 env,DELETE_WHEN_FREED 甚至可以为 0。 除了窗户,

另一种选择是使用 perm 参数,Windows 不支持该参数,这可能会有点尴尬
fd,错误:= os.OpenFile(路径,os.O_RDWR|os.O_CREATE,777)
777 是保留的,所以我们需要一个 1777 或 -777 ro 来支持这两个系统
使可读 DELETE_WHEN_FREED | 777

我能想到的最后一个选项是 os.OpenDeletableFile(
哪个将 os.OpenFile on nix 和
转动
共享模式:= uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
在窗户上

以上所有解决方案都很容易实现,多一点时间来测试(跨操作系统),只需要一个投票者......

在不更改默认值或扩展 API 表面的情况下支持此行为的一种方法可能是在 Windows 上的os.OpenFileflag参数中接受syscall.FILE_SHARE_DELETE并将其折叠到计算的sharemode价值。 由于 Windows 上的syscall.O_* (以及因此os.O_* )标志无论如何都使用组合值,因此可以对其进行设计以避免与任何想要包含的 Windows 特定标志发生冲突。 幸运的是,他们已经避免了与syscall.FILE_SHARE_DELETE冲突。

无论如何,我强烈反对更改默认行为。 将FILE_SHARE_DELETE设为默认值将使您在无法确保无竞争文件系统遍历方面与 POSIX 系统处于相同位置。 让这个标志是可选的就是为什么 Windows 不需要openatrenameatreadlinkat等的等价物。

我还没有真正想过我们应该在这里做什么,但我想澄清一件事:

您能否在 syscall_windows.go 中提供一个开关,以便我们可以在程序启动时选择 Unix 行为?

我们不会这样做。 这将使单个程序无法使用期望不同行为的不同包。

@havoc-io
您的建议会创建容易出错的程序,因为 syscall.FILE_SHARE_DELETE 与 POSIX 标准行为不同。
请注意 syscall_windows*.go 的作者知道 FILE_SHARE_DELETE 标志(它在那里)并决定使用它不是首选行为。
为什么?

让我们拿 Liam 的代码,他
推迟 fd.Close()
删除/重命名
然后读/写

所以实际的排序顺序是
打开
标记为已删除
读/写(也可能是跨进程/跨例程读写)
关闭

当前的 Windows 行为提醒开发人员“这是错误的”,开发人员可能也会将删除推送到延迟
如果您添加 FILE_SHARE_DELETE,Windows 将允许您“标记为删除,即使文件已打开”,但同时运行的另一个进程/goroutine 将尝试在删除和关闭之间访问文件(这可能是此代码中较长的任务) 会失败
结果:而不是一致的“你可以做到”的行为 - 你会得到一个“有时 - 特别是当有很多并发用户时它会失败,我不知道为什么。
所以你没有解决问题,只是处理开发人员“只运行一次”的特定用例
我的投票当然是一致的行为......

这与 Posix 有何不同?
一旦标记为已删除,该文件就不再在文件表上,它仅作为 fd 存在,允许另一个例程/进程创建具有相同名称的文件。

我认为我们应该停止寻找“相同的解决方案”,因为我们不会在 go 中解决一些差异(区分大小写的文件名?:) 我们会让 Linux 5.2 做到这一点......)

总而言之,它看起来像是在寻找临时文件的解决方案,如果是,Windows 支持 GetTempFileName ,这可以被视为“使用然后丢弃”文件的标准解决方案。

另一方面,如果我们希望允许开发人员在 Windows 中延迟删除,这是可能的,请参阅我上面的建议,但开发人员必须对此负责,并理解意识 - 因此必须是具有良好名称约定的标志。

此功能的主要用途是在创建打开文件后重命名它:

fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
_, err = fd.Write(...)
err = fd.Sync()  // file now safe to share
err = os.Rename(path, shared_path)
// os.Close() at program exit

我不知道您为什么不添加标志 os.O_WRNDEL(Windows 重命名/删除;其他平台上的无操作),并记录该模式与 Unix 的差异; 删除的文件保留在其目录中,但无法打开,直到 os.Close()。 还提到在删除文件之前将文件移动到临时目录会提供更多类似 Unix 的行为。

@guybrand我想你可能误解了我的建议。 我不主张默认启用FILE_SHARE_DELETE - 事实上我反对它的原因是我的评论第二段中提到的原因。 我的建议是建立一种机制,允许用户选择加入FILE_SHARE_DELETE行为(基于每个文件),同时仍然使用os包的文件基础结构。 该提案提供了一种机制,无需扩展任何包的 API 表面。

我不知道您为什么不添加标志 os.O_WRNDEL (Windows 重命名/删除;其他平台上的无操作)

@networkimprov这基本上就是我的提议,除了定义一个新标志没有意义,因为已经存在一个完全有效的标志: syscall.FILE_SHARE_DELETE 。 唯一的实现更改是在 Windows 上监视syscall.Open此标志并添加检查以确保将来不会添加到syscall.O_* / os.O_*标志与此标志冲突。 然后可以更新os.OpenFile的文档以反映 Windows 上接受此标志。

@havoc-io 很抱歉造成误解,这是我从以下内容中提取的:
“ ...只接受 syscall.FILE_SHARE_DELETE ...进入计算的共享模式...”

添加 os.O_WRNDEL 与我的第二个建议相匹配,除了在“行为将是什么”方面对开发人员不够明确,也许是 os.WADRNDEL - Windows 允许延迟重命名/删除)。

@alexbrainman我还建议使用开关来启用它,而不是更改默认值

我没兴趣。 谢谢你。

亚历克斯

@guybrand打开文件的重命名不会被推迟,我检查过。

对不起,我又问你有什么建议? 删除打开文件? 重命名打开文件? 两个都?

我们三个人现在正在建议一个新标志,例如

fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE| os._WRNDEL, 0600)

fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE| syscall.FILE_SHARE_DELETE, 0600)

我们三个人现在正在建议一个新的旗帜

我认为标志是不合适的,尤其是阅读代码的人不容易解析的标志。 使用syscall.FILE_SHARE_DELETE会更加明确和清晰,立即向读者发出特定于平台的事情正在发生的信号。

这在 POSIX 平台上已经被允许。 许多特定于 POSIX(甚至特定于平台)的open标志未合并到os包中。 例如,添加类似os._DEVTONLY来支持 Darwin 的O_EVTONLY标志是没有意义的,因为您可以直接将syscall包中的标志传递给os.OpenFile ,例如:

os.OpenFile(..., os.O_RDONLY | syscall.O_EVTONLY, ...)

在这种情况下,特定于平台的标志将被传递给底层的open系统调用。

如果FILE_SHARE_DELETE行为确实是人们需要的东西,那么我认为答案就是让os.OpenFile能够类似地将标志线程化到 Windows 上的底层CreateFileW调用。 至少对于FILE_SHARE_DELETE

可以在此处找到示例实现。

@networkimprov

我们三个人现在正在建议一个新标志,例如

你对国旗有什么期待?

@guybrand打开文件的重命名不会被推迟,我检查过。

对,但两者都需要 FILE_SHARE_DELETE 才能在文件打开时被允许
如果您指的是我建议的术语,我们可以
os.WARNDFDEL - Windows 允许重命名和延迟删除有点太长,我自己会使用首字母缩略词 WINDOWS_ALLOW_OPENNED_FILE_RENAME_OR_DEFFERED_DELETE
是更好的国际海事组织。

@guybrand我真的看不到向os包添加一个具有复杂名称和不透明行为的新平台特定标志的价值,因为它的完美标志已经存在了几十年( FILE_SHARE_DELETE )。 使用syscall.FILE_SHARE_DELETE作为标志更加清晰和明确。 正如我上面提到的,有许多特定于 POSIX 和特定于平台的标志没有添加到os包中,但仍然被它接受。

syscall.FILE_SHARE_DELETE 仅适用于 Windows; 我们需要在其他地方无法使用的东西。 也许它应该出现在 pkg os 中,带有 WIN 或 WINDOWS 前缀?

@mattn请从@

我们需要在其他地方无法使用的东西。

@networkimprov该标志不需要存在于其他地方。您的 Windows 代码将如下所示:

import (
    "syscall"
    "os"
)

file, err := os.OpenFile(..., os.O_RDWR | syscall.FILE_SHARE_DELETE, ...)

是的,此代码需要在平台上进行门控,但您可能仍需要这样做,因为带有此标志的行为与 POSIX 系统的行为不匹配。或者,如果你想有在所有平台上相同的代码的方便,你可以定义自己的常数(一个值为syscall.FILE_SHARE_DELETE Windows和0在非Windows系统)和通它到os.OpenFile 。如果您想引入不属于os包的特定于平台的O_*标志(例如 Darwin 上的O_EVTONLYO_ASYNC在 Linux 上)。

当我编写代码时,我希望它是跨平台的, FILE_SHARE_DELETE 在 syscall_* 中不存在,只是 windows 之一(因此 anf 不会在其他系统上编译)。

我们可以使用 0x4 const,作为它的:
FILE_SHARE_DELETE = 0x00000004

除了丑,
netbsd_arm
O_NDELAY = 0x4
O_NONBLOCK = 0x4

所以使用 0x4 会在 netbsd_arm 中创建不同的代码。

因此,要么我们将 FILE_SHARE_DELETE 添加到所有平台,windows 的将是 0x4,其他的将是 0x0 并因此“丢弃”它,或者为此问题创建一个特定的标志。

无论如何我们必须改变 syscall_windows*
函数打开(
...
共享模式:= uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | isDeleteOn)
其中 isDeleteOn 将检查模式是否包含新标志

@guybrand

当我编写代码时,我希望它是跨平台的

我会提醒您,仅仅因为相同的os.OpenFile调用在多个平台上编译,并不意味着您的代码是跨平台的。 在 Windows 上使用FILE_SHARE_DELETE时需要考虑许多安全注意事项,并且 POSIX 和 Windows 系统之间文件生命周期的语义是如此不同,以至于很难想象您的文件处理代码在平台之间看起来至少有一点不同。 使用FILE_SHARE_DELETE本质上使您处于 POSIX.1-2008 之前的情况,没有任何方法可以确保无竞争的文件系统遍历(并且没有 POSIX 的后unlink文件访问以打开文件)。

无论如何,如果你想拥有的值的标志syscall.FILE_SHARE_DELETE Windows和0其他平台上,你仍然可以轻松地做到这一点与同时通过构建标签控制自己的常量定义,然后您调用os.OpenFile的主要代码在所有平台上都可以相同(使用该自定义标志)。

在其他平台的syscall包中存在许多特定于平台的标志,但它们不能全部被os包公开并且在它们所在的平台上没有操作'支持。 如果您想要特定于平台的代码和行为,则有必要使用syscall包。

Go 标准库应该(可能)在这里做的唯一一件事是在 Windows 上支持syscall.FILE_SHARE_DELETE中的syscall.Open (以及因此os.OpenFile )。

正如我在上面提到的,如果我们添加 FILE_SHARE_DELETE,许多程序员会错误地使用它来模拟 Unix 行为。 但这并不好。 例如,请考虑创建应用程序以监视文件存在的情况。 如果应用程序删除文件,则支持不能从其他应用程序中找到文件。 但 FILE_SHARE_DELETE 只标记稍后删除。 文件还在。 在 UNIX 上,它运行良好,但 Windows 不行。 请不要使用 FILE_SHARE_DELETE 来模拟 UNIX 行为。

@mattn我同意您的担忧-人们可能会尝试使用该标志作为一种便利,以使 Windows 代码与 POSIX 系统“工作相同”,而无需了解其中的微妙含义。 但是,我认为如果他们必须去syscall包才能访问该标志,他们可能会花时间了解它的作用。

@mattn ,对于类似 Unix 的行为,应用程序需要 2 行额外的平台中立代码:

fd, err := os.OpenFile(path, ... | syscall.FILE_SHARE_DELETE, 0600)
...
tmp_path := mkTempName()
err = os.Rename(path, tmp_path)  // works immediately; path is now available
err = os.Remove(tmp_path)

记录这一点需要 os.OpenFile() 的文档中的一句话。

请不要让我向 pkg 系统调用分发补丁,每个使用我的 Windows 代码的人都必须申请! 另请参阅https://github.com/golang/go/issues/32088#issuecomment -493876119。

我想我们都同意大多数事实,只是换一种说法,所以我会尝试总结一下,并制定行动方针:

  1. 同意:Windows 和 POSIX 在上述行为上有所不同,我们不应该试图以相同的行为为目标。
  2. 同意:允许 FILE_SHARE_DELETE 作为标志的请求很有用
  3. 建议:让票证更改为“支持 FILE_SHARE_DELETE”,这样听起来更像是功能请求而不是错误
  4. 大部分同意:该功能应该是程序级别的开发人员标志,而不是基础包。

行动项目:

  1. 更改 syscall_windows.go
    函数打开(
    ...
    共享模式 := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | (mode & FILE_SHARE_DELETE) )
  2. 想要利用此行为的开发人员要么
    file, err := os.OpenFile(..., os.O_RDWR | syscall.FILE_SHARE_DELETE, ...)
    或者如果他们希望他们的程序是跨平台的,将创建一个内部标志:

mycode_windows.go
const OPENFILEFLAG = syscall.FILE_SHARE_DELETE
mycode_others.go
const OPENFILEFLAG = 0

然后使用:
file, err := os.OpenFile(..., os.O_RDWR | OPENFILEFLAG, ...)

如果同意,修复是最小的(一个班轮),风险非常低

让我们对此进行投票,我们可以快速解决这个问题

同意:允许 FILE_SHARE_DELETE 作为标志的请求很有用

我不同意这一点,因为我不明白这个问题的目的是什么?

目的:允许开发者重命名或删除打开的文件。

缺少什么: syscall_windows.go 当前硬编码
func Open(path string, mode int, perm uint32) (fd Handle, err error) {
...
sharemode := uint32(**FILE_SHARE_READ | FILE_SHARE_WRITE**)

提议的变化:
当开发人员在打开按位 4 (FILE_SHARE_DELETE) 的情况下传递模式参数时,共享模式将相应更改
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | **(mode & FILE_SHARE_DELETE)** )

我仍然想知道你为什么不在 Rename() 或 Remove() 之前调用 Close()。 您可以阅读 Go 标准库的代码。 这些文件在 Rename() 或 Remove() 之前由 Close() 关闭。 为什么必须在 Renamoe/Rename 之后调用 Close() ?

我不确定@networkimprov正在考虑什么商业案例,我对此的外卖将是一个临时共享文件——我的应用程序和另一个应用程序正在使用这个文件,我想确保(即使我的应用程序崩溃)这个文件完成后将不再存在 - 所以我创建它,Remove() 它,当我关闭它或我的应用程序关闭时 - 文件被删除。

但看起来@networkimprov想要使用 Rename(),所以可能是一个差异用例。

https://github.com/golang/go/issues/32088#issuecomment -494305074 中所述,在程序退出之前,我们可能不需要关闭重命名的文件。

@mattn ,请不要坚持我们不应该使用 C 和 C++ 开发人员可以使用的操作系统功能。

我想确保(即使我的应用程序崩溃)完成后该文件将不再存在

@guybrand defer语句不能保证在每种类型的崩溃中都运行,所以如果您试图确保文件删除,那么推迟os.Remove操作不是可靠的方法去做吧。 如果您想要一个自动删除的文件,系统删除的临时文件是一个更好的选择。 如果你想要一个由两个程序共享的文件位置,它应该由锁协调(例如fcntl锁在 POSIX 和LockFileEx / UnlockFileEx在 Windows 上),而不是文件存在.

正如#32088(评论)中提到的,在程序退出之前,我们可能不需要关闭重命名的文件。
@mattn ,请不要坚持我们不应该使用 C 和 C++ 开发人员可以使用的操作系统功能。

@networkimprov打开后重命名操作是一个有效的用例,但如果你是保存文件HANDLE ,你不能这样做吗? FILE_SHARE_DELETE的唯一目的是允许其他进程在您打开文件时重命名文件。 在任何情况下,您现在都可以使用FILE_SHARE_DELETE ,您只需手动调用CreateFileW并将结果传递给os.NewFile 。 您可以在此处找到一个示例。 结果HANDLE可以传递给os.NewFile

@mattn我开始同意您的观点,即这面旗帜更像是一把枪,而不是一个有用的工具。 为了清楚起见,我实际上并不想要这个标志,我能看到它存在的唯一论据是 POSIX 和 Windows 之间控制的完整性和奇偶性。 另一方面,正如我上面提到的,手动调用CreateFileW (指定此标志)然后将结果交给os.NewFile也相对容易,所以也许不需要添加对它的支持。

@havoc-io 没有 os.File.Rename()。 这将是一个很好的解决方案,但它是一个相对沉重的提升。

重新 CreateFileW + os.NewFile(),见https://github.com/golang/go/issues/32088#issuecomment -493876119。

@havoc-io

我想确保(即使我的应用程序崩溃)完成后该文件将不再存在
@guybrand不能保证在每种类型的崩溃中都运行 defer 语句
这正是我的意思,当我说“即使我的应用程序崩溃”时。

我能看到它存在的唯一论据是 POSIX 和 Windows 之间控制的完整性和奇偶性
我不同意这会带来“POSIX 和 Windows 之间控制的完整性和奇偶性” - IMO 它只会让开发人员混淆,认为行为是相同的,而不是,我在上面举了几个例子。

易于手动调用 CreateFileW
这真的是一个调整,容易与否,换句话说:“我们不想支持这个”,顺便说一句,这是一个好的决定 IMO,但请求者是@networkimprov而不是我。

仅供参考,这就是我们放弃使用 FILE_SHARE_DELETE 的原因。

CL: https :

在 erlang/otp 中提交: https :

蟒蛇: https :

@havoc-io 没有 os.File.Rename()。 这将是一个很好的解决方案,但它是一个相对沉重的提升。
重新 CreateFileW + os.NewFile(),参见 #32088(注释)。

@networkimprov你不能只使用os.Rename(file.Name(), <target>)吗? 这就是 Go 默认情况下没有FILE_SHARE_DELETE的美妙之处 - 你知道file.Name()仍然会指向有效的东西,因为你持有文件HANDLE (你可以' t 保证在 POSIX 上,即使使用renameat )。 关于CreateFileW + os.NewFile ,您评论中描述的大部分堆栈都将变得无关紧要,并且makeInheritSa几乎可以肯定是您想要的东西(它只是用来模拟POSIX的错误的默认文件描述符继承行为 - 它不是默认值)。 您唯一会错过的是内部fixLongPath路径函数,对于您描述的用例,这可能不是必需的。

@guybrand只是为了澄清我的意思......

完整性: API 表面完整性(即,如果您出于某种原因想使用它,则可以使用FILE_SHARE_DELETE
控制奇偶性:通过os.OpenFileflag参数为CreateFileW指定标志的能力。 例如,我可以在 POSIX 上通过os.OpenFile路由syscall.O_NOFOLLOW ,但在 Windows 上我不能通过os.OpenFile路由syscall.FILE_FLAG_OPEN_REPARSE_POINT 。 不过,我认为这里的完全控制权是一个白日梦,因为os包是围绕 POSIX API 建模的。 在必要的时候,我很高兴能拿到syscall.CreateFileW

@mattn感谢您的链接。 Go CL 用于修补 syscall.Open() _default_。讨论没有给出禁止 Open() 使用 _flag_ 的一般理由。它指出第三方包可以返回缺少标志的 os.File,但这只是调用者尝试重命名/删除该文件时的问题——这是一种罕见的极端情况。

Python 线程指出,您应该在删除之前重命名打开的文件,正如我在上面所写的。

Erlang 已经将 file_share_delete 作为 _default_ 超过六年了

回顾上面链接的 Erlang 体验和 Python 线程,很明显:

  1. 默认的 File_share_delete 在跨平台环境中运行良好; 唯一需要注意的是,如果可以重用原始文件名,则用户代码应在删除文件之前立即将文件重命名为唯一名称(微不足道的更改)。
  2. 大多数 Windows 程序不能简单地使用 file_share_delete,因为 MSVC 运行时只允许它与 O_TEMPORARY 结合使用。

因此,我得出的结论是 syscall.Open()默认情况下应该使用 file_share_delete ,并且 syscall 应该提供一个全局标志以在必要时

如果在 1.14 周期期间该方法出现问题,则可以应用显式标志方法。

因此,我得出的结论是 syscall.Open() 默认情况下应该使用 file_share_delete,并且 syscall 应该提供一个全局标志以在必要时禁用它(例如调试)。

@networkimprov除了以微妙和不那么微妙的方式破坏大量现有程序之外,事实是现在引入此标志对于依赖于FILE_SHARE_DELETE缺失的程序来说将是一个安全问题. 特别是,它会为依赖于它们保持打开的文件和目录的不变性的程序打开许多使用时间检查漏洞。

编辑:要进一步阅读为什么更改默认值是一个坏主意并且几乎肯定会破坏现有程序,请参阅Hyrum 定律

Go 在 Windows 上最常见的情况是开发,用于部署到 Linux或其他 Unix。

如果您的代码依赖于任何 GOOS=windows 行为,则它在 Linux 上已损坏并且可能易受攻击。 所以我们已经有一个可能的安全问题。

os.Rename() 和 .Remove() 不会记录 Windows 上的差异,因此没有人会猜测它们存在。 现在记录它们不会修复现有代码。 修复不兼容性并在其上发布博客文章将有更多帮助。

请注意,Erlang 在采用 file_share_delete 作为默认值之前已经存在了一段时间。

Go 在 Windows 上最常见的情况是开发,用于部署到 Linux或其他 Unix。

如果您的代码依赖于任何 GOOS=windows 行为,则它在 Linux 上已损坏并且可能易受攻击。 所以我们已经有一个可能的安全问题。

@networkimprov

按照这种逻辑,还应该修改 Windows 上的文件打开基础结构,以使文件描述符在默认情况下在进程创建过程中可继承,以匹配 POSIX 的默认行为。 现有程序可能会因依赖运行时/标准库以前的默认行为而受到指责,并且可以在发行说明中包含模板 CVE,供用户为其程序归档。

但当然这不是已经完成的事情,而是 Go 的运行时和标准库向后弯曲以解决 POSIX 的默认和设计错误的行为,试图反映 Windows 所做的事情。

共享文件访问的情况与此相同。 Windows 可以说设计正确,而 POSIX.1-2008 必须添加许多功能来解决其设计限制。

此外,作为@mattn提及上述几次,包括FILE_SHARE_DELETE当打开文件时不会使Windows运行起来像POSIX -仍然会有显著的行为差异将变得更加明显,这个标志不是不同的os.Removeos.Rename行为。

我认为,如果您的代码普遍依赖于任何平台的行为,那么它在其他平台上就会被破坏并且可能容易受到攻击(这包括依赖于 Windows 上的 POSIX 行为)。 如果您没有花时间理解和解决代码中的平台变化,那么您就没有进行跨平台开发。

无论如何, @ianlancetaylor已经否决了使用全局标志来控制将其打开的想法,因此我严重怀疑您是否会在更改默认值后将其关闭。

在我看来,这里唯一不破坏(且不损害安全性)的选项是在syscall.Openflag参数中添加对syscall.FILE_SHARE_DELETE支持,或者只是强制希望FILE_SHARE_DELETE功能使用CreateFileW + os.NewFile 。 是的,它需要想要这种行为的开发人员做一些工作,但是 Win32 和 POSIX 是根本不同的野兽,令人惊讶的是,运行时和标准库在弥补差异方面做得一样好。 这些平台之间在标准库中表现出更多显着差异,例如完全不同的权限模型, os.File.Chmod在 Windows 上不起作用, os.Chown在 Windows 上不起作用的事实Windows,内部轮询器支持不同平台上不同类型文件的事实等。Go 的运行时和标准库尽其所能(并且做得很好),但它们并不是解决跨平台开发困境的灵丹妙药。 在某些时候,开发人员必须自己处理这些差异。 添加一个重大更改来修改(注意我没有说正确)一个晦涩的行为边缘案例对我来说没有任何意义。

我想为此再分享一个用例。

目前,当 Docker 日志轮换逻辑以 Docker 引擎将活动日志重命名为名称 .1 的方式工作时,使用旧名称创建新日志。
任何使用命令docker logs --follow <container>主动跟踪日志的客户端
会从文件系统事件中注意到:
https://github.com/moby/moby/blob/916eabd459fe707b5c4a86377d12e2ad1871b353/daemon/logger/loggerutils/logfile.go#L552 -L593

还有这种逻辑使 Windows 立即注意到这些事件:
https://github.com/moby/moby/blob/916eabd459fe707b5c4a86377d12e2ad1871b353/daemon/logger/loggerutils/logfile.go#L670 -L676

这一切在 Linux 上运行良好,但在 Windows 上,如果有任何客户端跟踪日志,则日志轮换失败The process cannot access the file because it is being used by another process. (问题:https://github.com/moby/moby/issues/39274)

由于可能有 n 个正在跟踪日志的客户端,因此首先收集所有打开的文件句柄并在文件旋转之前关闭它们是非常棘手的,所以我真的很希望能够支持syscall.FILE_SHARE_DELETE标志。

你好! 背景介绍:我在 Chrome CI 基础架构上工作。 我们正在将我们的低级工作代码移植到 Go。 我们每天在 Windows 上运行数十万次测试调用。

我已阅读所有评论,希望以下是对每个人意见的清晰正确的总结。

我们的用例是在 Windows 上构建 Go 可执行文件以在 Windows 上运行,这与@networkimprov令人惊讶的特征不同。 我将在此评论中忽略此“假设 Windows 上的 POSIX 行为”用例,因为它只是一个明显不正确的假设,并将@mattn拖入突出差异,恕我直言,这是一个有争议的问题,更多内容如下。

作为我们迁移的一部分,我们需要 FILE_SHARE_DELETE,部分原因是日志文件轮换,类似于 #39274。 碰巧这也是问题#25965的问题。

我同意@ianlancetaylor改变整个工具链的行为,正如https://codereview.appspot.com/8203043/ 中提出的那样,这不是一个好主意。 当使用文件句柄作为锁定文件时,会立即想到这个建议的一个问题。 我们使用这种行为。 以及@havoc-io 所说的。 所以我们也忽略这个。

重申一下,我将在本评论的其余部分忽略这些建议:

  • 试图表现得完全像 POSIX,那是行不通的
  • 将全局行为更改为选择退出(或不选择退出!),这会破坏用户

这给我们留下了一个每次调用标志,其名称显然是特定于 Windows 的。 这正是@guybrand 的建议。

我直言,我不理解@guybrand 的建议是合理的。 是的,Windows 在文件共享方面表现不同。 就像文件删除权限一样,在 Windows 上有很大的不同。 它已经有很多不同的方式。 对“开发人员可能误解其行为”的立场反对特定于 Windows 的标志不是事实,而是一种观点。 您假设开发人员不知道他们在做什么,或者不会阅读 MSDN 上的文档。 对于一般情况 😎,这是一个公平的反对意见,但基于阻止合法用例作为可选标志,这不是一个强有力的反对意见。

具有讽刺意味的是,这意味着在解决此问题之前,要修复 #25965,我必须将 'go run' 更改CreateFileW直接调用FILE_FLAG_DELETE_ON_CLOSE

谢谢!

我只是想知道如何存在案例以知道我们是否应该实现新功能(golang.org/x/sys?)来打开/创建具有新属性的文件,或者改变原始行为。 目前,据我从上面的评论中了解到:

  1. 创建用于记录的可删除(可重命名)文件。
  2. 为将在关闭时删除的临时文件创建文件。

对于 1,可以从新功能中提供创建/打开具有属性的文件。 我们可以使用 logger.SetOutput(file) 设置文件。

对于2,也可以创建/打开新功能。

顺便说一句,我猜#25965 与这个问题无关。 可能是Windows的限制。

(无论如何,我们不能删除使用该属性打开的文件所在的目录)

@mattn你能解释一下之间的权衡吗:

  • 在 golang.org/x/sys 中添加一个全新的功能
  • 支持 OpenFile() 的新标志(已定义!)。 [1]

我不明白为什么我们应该支持第一个而不是第二个。

[1] 我刚刚意识到我在这里假设改变了 os.OpenFile() 的行为,但问题调用了 Open()。

首先,系统调用包已经被锁定。 所以我正在寻找一种在不更改系统调用包的情况下解决此问题的方法。

顺便说一句,我猜#25965 与这个问题无关。 可能是Windows的限制。

(无论如何,我们不能删除使用该属性打开的文件所在的目录)

@mattn这并不完全正确。 FILE_SHARE_DELETE标志将允许您将这些文件移动到另一个文件夹,以便您可以删除原始文件。 这是一个简单的 PowerShell 示例:

# Create folder and open new file with FILE_SHARE_DELETE flag
New-Item -Type Directory -Path C:\folder1
$file = [System.IO.File]::Open("C:\folder1\test.txt", "Create", "ReadWrite", "Delete")

# Create temp folder and move all open files to there
New-Item -Type Directory -Path C:\folder1-removing
Get-ChildItem -Path C:\folder1 -Recurse | Move-Item -Destination C:\folder1-removing\

# Remove original folder
Remove-Item -Path C:\folder1

# Trigger removal for files on temp folder (NOTE! Those will exist on disk until they are closed).
Get-ChildItem -Path C:\folder1-removing -File -Recurse | Remove-Item -Force

# Close file and remove temp folder
$file.Close()
Remove-Item -Path C:\folder1-removing

首先,系统调用包已经被锁定。 所以我正在寻找一种在不更改系统调用包的情况下解决此问题的方法。

@mattn如果只是API被冻结的问题,那么正如我上面提到/演示的那样,可以调整syscall.Open以理解syscall.FILE_SHARE_DELETE ,在不更改API的情况下解决问题。

如果是实现冻结,则可以对golang.org/x/sys/windowsOpen函数进行更改,然后将其出售到internal/syscall包中。 file_windows.go代码已经在使用internal/syscall函数。 唯一的不一致是syscall.Open不会理解这个标志,但是大多数想要访问低级 Windows API 功能的人无论如何都在使用golang.org/x/sys/windows ,并且可能使用CreateFile而不是Open

启动系统调用包“锁定”的提案指出:
_核心存储库将不依赖于 go.sys 包_

所以改变 x/sys 不会帮助 os.OpenFile() 的调用者。 但是 internal/syscall 可以添加 Open(),并且 os.OpenFile() 会调用它。

@maruel ,Docker 项目,可能还有许多其他项目,假定 os.Rename() 和 .Remove() 打开文件的 Unix 行为,因为包“os”没有提及它们的 Windows 特定行为,但有 __fifteen 提及__其他 Windows 行为。

核心存储库将不依赖于 go.sys 包
但是 internal/syscall 可以添加 Open(),并且 os.OpenFile() 会调用它。

@networkimprov好的,我错误地认为internal/syscallgolang.org/x/sys的供应商子集,但无论哪种方式,我认为这是正确的策略(添加internal/syscall/windows.Open )。 当然,这是假设syscall.Open的行为由于冻结是不可变的。 理想情况下,它可以直接修改,可能会在golang.org/x/sys/windows.Open进行相应的更改。

尽管 syscall 包被冻结,但我们可以对其进行更改以修复错误或解决严重缺陷。 特别是我们可以更改它以更好地支持 os 包。

但是如果人们直接调用syscall.Open ,那么他们应该使用 x/sys/windows 包,而不是 syscall 包。

但是如果人们直接调用syscall.Open ,那么他们应该使用 x/sys/windows 包,而不是 syscall 包。

我认为没有人希望直接调用syscall.Open - 这只是讨论的目标,因为它是os.OpenFile (因此在syscall.Open中添加对FILE_SHARE_DELETE支持syscall.Open添加了对使用带有os.OpenFile的标志的支持)。

谢谢。 可以更改 syscall 包以支持对 os 包的更改。

谢谢@ianlancetaylor和@

  • 修改 syscall.Open() 以明确允许 os.OpenFile() 接受 FILE_SHARE_DELETE (并最终 FILE_FLAG_DELETE_ON_CLOSE 作为潜在的后续行动)
  • 更新 os.OpenFile() 文档以描述新的仅限 Windows 标志。
  • 更新 os.OpenFile()、os.Rename() 和 os.Remove() 文档以阐明 POSIX 和 Windows 之间的行为差​​异。

第三点是解决@networkimprov 的担忧。 我理解那里的挑战,虽然描述操作系统的工作方式不应该是语言的责任,但我开始同意@mattn 的观点,即这些案例足够微妙,需要更多文档。 我认为 Rename and Remove 的文档可以立即改进,无需等待任何行为改变。

如果得到批准,我愿意为此做出贡献。

这给我们留下了一个每次调用标志,其名称显然是特定于 Windows 的。

您是否提出新的 os.Open 标志? 然后,所有操作系统都应该支持该标志 - 这是 os 包的一个整体点 - 独立于操作系统。 那么该标志的语义是什么? 而且您需要一些新的测试来验证标志是否按文档说明工作。

我不明白为什么你不能使用 syscall 包编写你需要的任何代码。 我认为您的代码不会与其他 Windows 程序很好地共存。 也许在特殊情况下是可以的。 但我不希望人们轻易使用这个功能——所以它不应该成为标准库的一部分。

亚历克斯

@alexbrainman
请看这个

@alexbrainman
请看这个

我确实看过。 我仍然看不到您提出的所有更改将如何在其他操作系统上起作用? 您必须进行哪些测试才能实施您提议的更改?

亚历克斯

引用这个

而对于 env,DELETE_WHEN_FREED 甚至可以为 0。 除了窗户

所以 - 没有什么可以在其他操作系统上测试的,呼吁:
fd,错误:= os.OpenFile(路径,os.O_RDWR|os.O_CREATE|os.DELETE_WHEN_FREED,0600)
在 Windows 中会将 const 转换为4
fd, err := os.OpenFile(路径, os.O_RDWR|os.O_CREATE| 4 , 0600)
而其他人 ( 0 )
fd, err := os.OpenFile(路径, os.O_RDWR|os.O_CREATE| 0 , 0600)

无需在其他操作系统上进行测试

我看不出我们如何添加仅在 Windows 上可用的新 os 包 const 或变量。

c:\>go doc os
package os // import "os"

Package os provides a platform-independent interface to operating system
functionality. 
...
The os interface is intended to be uniform across all operating systems.
Features not generally available appear in the system-specific package
syscall.

亚历克斯

亚历克斯首先在此处说明了您反对的答案https://github.com/golang/go/issues/32088#issuecomment -494157514

测试是微不足道的

_, err := os.OpenFile(path, os.O_CREATE | syscall.FILE_SHARE_DELETE, 0);
err = os.Rename(path, path+"2")

我们正处于有人可以提交补丁的地步; 我怀疑@ianlancetaylor会接受它。

我调查了将FILE_SHARE_DELETE为 #32188 的一部分,但发现设置它并没有从经验上降低os.Renameio.ReadFile调用的错误率; 无论如何,仍然需要重试循环。

我很想看到一个测试证明与设置这个标志有显着的可观察差异,因为我自己并不真正理解它的含义。

几年前我已经尝试过 FILE_SHARE_DELETE,它确实表现得很好
不同。
我可能可以重新启动我的 Windows 环境。 查一下,如果有帮助的话,
但它会是~Go 1.3 左右。

2019 年 6 月 10 日星期一 17:44,Bryan C. Mills [email protected]
写道:

我调查了将 FILE_SHARE_DELETE 设置为 #32188 的一部分
https://github.com/golang/go/issues/32188 ,但发现设置它
没有从经验上降低 os.Rename 和
io.ReadFile 调用; 无论如何,仍然需要重试循环。

我很想看到一个能证明显着的测试
与设置这个标志有明显的区别,因为我真的不
我自己理解它的含义。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4VVIVJ2IEWQPWCWBZTPZZSETA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXKCG4Y#issuecomment-500441971
或使线程静音
https://github.com/notifications/unsubscribe-auth/ABNEY4STTW7U526HPO6TUHDPZZSETANCNFSM4HNPNYIA
.

@bcmills ,阅读 #32188 我了解到您在重命名期间_未打开_的文件上看到来自 os.Rename() 的错误? 我想一个人可能会超过 NTFS 日志,我相信它是为所有目录更改操作编写的,从而引发错误; 但我不知道它会是什么错误。

在 syscall.Open() 中启用 FILE_SHARE_DELETE 的请求是允许 os.Rename() 处理打开的文件。 我已经在 Win7 上使用 1.12 测试了该标志,它可以工作; 没有它就会失败。

我没有在“负载下”的 Windows 上专门测试 os.Rename(),但是自从启用 fsd 以来,没有看到 os.Rename() 在打开的文件上出现任何意外错误,如果我这样做了,我会报告它。

我了解到您在重命名期间未打开的文件上看到来自 os.Rename() 的错误?

是的。

我不知道它会是什么错误。

根据经验,我看到的错误(来自MoveFileExReplaceFile和/或CreateFile )是ERROR_ACCESS_DENIEDERROR_SHARING_VIOLATION ,和ERROR_FILE_NOT_FOUND

在 syscall.Open() 中启用 FILE_SHARE_DELETE 的请求是允许 os.Rename() 处理打开的文件。 我已经在 Win7 上使用 1.12 测试了该标志,它可以工作; 没有它就会失败。

对; 我试图解决的相关问题是允许对os.Rename并发调用成功,并允许对io.ReadFileos.Rename并发调用成功。 让单个os.Rename与现有句柄一起工作可能是朝着正确方向迈出的一步,但我认为这对于我们使用 POSIX rename的用例来说是不够的

我很想看到一个测试证明与设置这个标志有显着的可观察差异,因为我自己并不真正理解它的含义。

@bcmills PowerShell 示例,因为我写得比 Go 快得多

New-Item -Type Directory -Path C:\folder1
for($i=0; $i -le 1000; $i++)
{
    $temp = [System.IO.File]::Open("C:\folder1\test.txt", "Create", "ReadWrite", "Delete")
    Set-Variable -Name "file$i" -Value $temp -Force
    $TempName = "C:\folder1\test.txt." + (New-Guid).Guid
    Rename-Item -Path C:\folder1\test.txt -NewName $TempName
    Remove-Item -Path $TempName -Force
}

在此之后完成的会有一千test.txt.???下的文件C:\folder1但是当你接近PowerShell的这些文件将被删除。

那么FILE_SHARE_DELETE标志实际上允许您重命名/移动/删除打开的文件,但您仍然需要确保目标文件名是唯一的,因为只要句柄打开,它们就会存在于磁盘上。

“使用 POSIX 重命名”在 Windows 中是不可能的,也许这就是你得到错误的原因。

POSIX:

创建文件“文件名”
从“文件名”读/写
删除“文件名”

创建文件“文件名”
...
删除“文件名”

会起作用,因为每个创建文件都会有一个 diff fd。

视窗:
创建文件“文件名”
从“文件名”读/写
删除“文件名”

创建文件“文件名” - 繁荣,共享违规

应该很容易重构...

那么 FILE_SHARE_DELETE 可以做什么呢?
文件打开时允许延迟删除。

而已。

2019 年 6 月 10 日星期一 19:32,Olli Janatuinen [email protected]
写道:

我很想看到一个能证明显着的测试
与设置这个标志有明显的区别,因为我真的不
我自己理解它的含义。

@bcmills https://github.com/bcmills PowerShell 示例,正如我写的那样
比 Go 快得多

New-Item -Type Directory -Path C:folder1for($i=0; $i -le 1000; $i++)
{
$temp = [System.IO.File]::Open("C:folder1test.txt", "Create", "ReadWrite", "Delete")
Set-Variable -Name "file$i" -Value $temp -Force
$TempName = "C:folder1test.txt。" + (New-Guid).Guid
Rename-Item -Path C:folder1test.txt -NewName $TempName
删除项目 -Path $TempName -Force
}

完成后会有一千个test.txt。??? 下的文件
C:folder1 但是当您关闭 PowerShell 时,这些文件将被删除。

那么 FILE_SHARE_DELETE 标志实际上允许你做什么
重命名/移动/删除打开的文件,但您仍然需要确保目的地
文件名是唯一的,因为只要句柄它们就会存在于磁盘上
是开放的。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4WSTWIUVNVHLYMBFY3PZZ62DA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXKMMDY#issuecomment-500483599
或使线程静音
https://github.com/notifications/unsubscribe-auth/ABNEY4VS5XF3EXB6QPBWVQ3PZZ62DANCNFSM4HNPNYIA
.

允许与 os.Rename 并发调用 io.ReadFile 成功

@bcmills我认为应该始终使用 fsd 标志集,假设它没有与尝试在不相关文件上执行相同序列的其他线程同时运行。 它通常应该在没有标志的情况下失败。

测试脚本的这一方面正确地发现了 Windows syscall.Open() 中的错误。 但是没有其他人在这个线程中发表评论希望它修复:-(所以是的,修补脚本。

TLDR:
对 syscal_windows.go 进行小的安全更改并按预期工作。

解释:
所以,这就是我所做的,按预期工作,没有风险(IMO)

  1. 调整 syscal_windows.go
func Open(path string, mode int, perm uint32) (fd Handle, err error) {

sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | (mode & FILE_SHARE_DELETE))

添加的是:“|(模式和FILE_SHARE_DELETE)”
如果开发人员不会发送模式和 4 组(他为什么要 - 这以前从来没有工作过......) - 什么都没有改变,所以像这样的代码

os.OpenFile(filename,os.O_RDWR|os.O_CREATE, 0600)

将与更改前完全一样,只有开发人员会

os.OpenFile(filename,os.O_RDWR|os.O_CREATE  | 4 , 0600)

会有“感觉”的变化

  1. 运行代码
package main

import (
    "fmt"
    "os"
)

func main() {
    filename := "./myfile"
    if f ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{
        fmt.Printf("Open file %s error : %s" , filename, err.Error())
    } else if _,err:= f.WriteString("bla bla") ;err!=nil{
        fmt.Printf("writing to %s returned error : %s" , filename, err.Error())
    } else if err := os.Remove(filename);err!=nil{
        fmt.Printf("removing %s returned error : %s" , filename, err.Error())
    }
}

它奏效了

  1. 在没有 |4 的情况下运行代码,但它失败了
  2. 运行代码并添加一点:
package main

import (
    "fmt"
    "os"
)

func main() {
    filename := "./myfile"
    if f ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{
        fmt.Printf("Open file %s error : %s" , filename, err.Error())
    } else if _,err:= f.WriteString("bla bla") ;err!=nil{
        fmt.Printf("writing to %s returned error : %s" , filename, err.Error())
    } else if err := os.Remove(filename);err!=nil{
        fmt.Printf("removing %s returned error : %s" , filename, err.Error())
    } else if secondF ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{
        fmt.Printf("reOpen file %s error : %s" , filename, err.Error())
    } else {
        secondF.Close()
    }
}

当然它在 secondF 上失败了
但这是 Windows 中的预期行为 - 无需“负载测试” - deferred .Remove 上没有并发选项,所以我认为我们可以安全地将这半行添加到 syscal_windows,而不会影响任何现有代码,并且允许

我们需要做的一切(粗体):
syscal_windows.go:
共享模式 := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | (mode & FILE_SHARE_DELETE) )

我调查了将FILE_SHARE_DELETE为 #32188 的一部分,但发现设置它并没有从经验上降低os.Renameio.ReadFile调用的错误率; 无论如何,仍然需要重试循环。

我很想看到一个测试证明与设置这个标志有显着的可观察差异,因为我自己并不真正理解它的含义。

@bcmills现在您可以从https://github.com/olljanat/go/commit/3828f1a5d0ebb69b4c459d5243799ded36ac1ee8找到针对此的单元/回归测试,目前它在 Windows 上无法出错The process cannot access the file because it is being used by another process.并在FILE_SHARE_DELETE之后开始工作

笔记! 在 Windows 上,您仍然需要将日志example.log.1文件移动到随机/唯一名称的步骤,因为其他原因将example.log重命名example.log.1将失败Access is denied.错误即使启用了FILE_SHARE_DELETE标志。 那是开发人员需要处理的特定于平台的事情。

/cc @jhowardmsft @jterry75 @jstarks @ddebroy仅供参考; 你可能也对这个感兴趣。

@kevpar - 仅供参考,因为您也在解决这个问题

如果加入线程的任何人都可以说出原因(例如现有代码被破坏)以使其成为默认值,而不是通过 os.OpenFile() 中的仅限 Windows 标志可用,请务必加油!

@networkimprov IMO 应该使用仅限 Windows 的标志来处理它,因为开发人员还需要处理一些其他 Windows 特定的事情,比如我的日志轮换示例。

然而,正如我所看到的那样,有几位微软员工被邀请参加讨论,所以看看他们是否有不同的想法很有趣。

顺便说一句,有没有人开始为此开发新的解决方案? 有志愿者吗?

@olljanat
如果它是一个修复标志,那么修复是一个单行,请阅读:
https://github.com/golang/go/issues/32088#issuecomment -500562223

我将尝试在这里代表 Windows 团队的观点……我们宁愿 Go 始终设置 FILE_SHARE_DELETE,没有任何选项。 一般来说,为了更容易移植到/从 Windows,我们更喜欢与 Linux 一致的行为,我不明白为什么 Windows 用户或软件更喜欢默认的 !FILE_SHARE_DELETE 行为,这足以成为规则的例外。

一个有趣的注释,与@mattn的评论相反:在最新版本的 Windows 中,我们更新了 DeleteFile(在 NTFS 上)以执行“POSIX”删除,其中文件立即从命名空间中删除,而不是等待所有打开要关闭的文件的句柄。 它仍然尊重 FILE_SHARE_DELETE,但现在的行为更像 POSIX 取消链接。 此功能是为 WSL 添加的,并且默认情况下也被认为值得用于 Windows 软件。

换句话说,如果你在最新版本的 Windows 上运行 mattn 的测试程序和 del、dir 等序列,你会看到文件在删除文件后立即从命名空间中消失,而不是在测试程序退出后。 就像Linux一样。

因此,即使在 Windows 本身中,由于其 appcompat 风险要大得多,我们也在做一些小改动,以简化将非 Windows 软件移植到 Windows 的过程。 我真的鼓励 Go 也这样做。

@jstarks ,谢谢你为我辩护。 为了那个位置,我在这个线程中吸收了大量的热量。

我建议使用全局选项来禁用 fsd,以防任何 Windows 部署依赖于当前未记录的行为。

@ianlancetaylor你同意吗?

由于 CreateFile 的默认行为是不能删除打开的文件,我认为我们应该保持默认行为。 有人可能想要原始行为。 例如,可能有程序员正在检查他们的应用程序是否正在使用无法删除文件的方式工作。

Python 的行为与 Go 的当前行为相同。 而且我从来没有听说过 Python 有计划改变不能删除打开文件的行为。

我没有花时间就这个问题发表认真的意见,但我认为全球选项不是一个好的选择。 我们应该要么保持当前行为(也许添加一个标志来选择其他行为)或者我们应该改变行为(也许添加一个标志来选择原始的、当前的行为)。

关于是否更改默认行为,我认为我们必须问,更改行为是否更有可能修复编写为跨 Unix 和 Windows 系统运行的 Go 程序,或者是否更有可能更改行为将破坏为在 Windows 系统上运行而编写的 Go 程序。 我不知道答案。

正如@mattn建议的那样,还值得询问其他语言的作用。

如果它成为默认值,并且任何 Windows 部署都依赖它,我想他们会想要一个 os.OpenFile() 标志和一个全局选项来切换回用于过渡目的。

有一个禁用 IPv6 的全局选项,最后我检查了。

发现影响的一种方法是在提示时更改它(并在博客上宣布?)并查看报告的内容。

问题文本中都提到了 Python 和 Erlang:
六年前,Erlang 将其设为默认值:erlang/
由于 MSVC 运行时的限制,Python 想要但不能: https :

我将尝试在这里代表 Windows 团队的观点……我们宁愿 Go 始终设置 FILE_SHARE_DELETE,没有任何选项。 一般来说,为了更容易移植到/从 Windows,我们更喜欢与 Linux 一致的行为,我不明白为什么 Windows 用户或软件更喜欢默认的 !FILE_SHARE_DELETE 行为,这足以成为规则的例外。

在最新版本的 Windows 中,我们更新了 DeleteFile(在 NTFS 上)以执行“POSIX”删除,即立即从命名空间中删除文件,而不是等待文件的所有打开句柄关闭。 它仍然尊重 FILE_SHARE_DELETE,但现在的行为更像 POSIX 取消链接。 此功能是为 WSL 添加的,并且默认情况下也被认为值得用于 Windows 软件。

@jstarks这是非常有趣的细节。 这是我们可以期待微软也向后移植到旧操作系统版本的东西吗? 我尤其想到了最新的、刚刚发布的 LTSC 版本的 Windows Server 2019,它仍将得到长期支持。

好像微软这样做了,那么我也更愿意在 Go 上默认启用 FILE_SHARE_DELETE。

Java、C# 和我知道的所有其他主要语言都采用默认的操作系统行为。 在我看来,默认行为应该留给操作系统实现者。 如果 Windows 真的想在打开文件方面采用 POSIX 方法,他们应该冒这个风险,并像对待 DeleteFile 一样这样做。

我确实认为 Go 库应该可以创建/打开共享删除文件。 我可以举一个处理文件模式的实际例子。 在将 Hadoop 移植到 Windows 时,我们必须付出额外的努力,在 JNI 中创建一个特殊的方法来获取共享删除文件句柄或流。

https://github.com/apache/hadoop/search?q=getShareDeleteFileInputStream&unscoped_q=getShareDeleteFileInputStream

@jstarks

说的时候

在最新版本的 Windows 中,我们更新了 DeleteFile(在 NTFS 上)以执行“POSIX”删除,即立即从命名空间中删除文件

你的意思是打开的文件只保留为 fd ,下面的代码:

package main

import (
    "fmt"
    "os"
)

func main() {
    filename := "./myfile"
    if f ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{
        fmt.Printf("Open file %s error : %s" , filename, err.Error())
    } else if _,err:= f.WriteString("bla bla") ;err!=nil{
        fmt.Printf("writing to %s returned error : %s" , filename, err.Error())
    } else if err := os.Remove(filename);err!=nil{
        fmt.Printf("removing %s returned error : %s" , filename, err.Error())
    } else if secondF ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{
        fmt.Printf("reOpen file %s error : %s" , filename, err.Error())
    } else {
        secondF.Close()
    }
}

不会失败

} else if secondF ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{

应用建议的修复后

如果是这样,那真的很酷!

关于是否更改默认行为,我认为我们必须问,更改行为是否更有可能修复编写为跨 Unix 和 Windows 系统运行的 Go 程序,或者是否更有可能更改行为将破坏为在 Windows 系统上运行而编写的 Go 程序。 我不知道答案。

没有problem to be fixed在 Windows 上运行 Unix 程序。 Windows 处理已打开文件的删除与 Unix 不同。 它类似于文件名区分大小写:Unix 区分大小写,Windows 不区分大小写。 对于这两种差异,我们无能为力。

而且,虽然约翰保证微软正在尝试改变事情,但我会等到改变就在这里。 并且很多 Windows 用户仍然使用 Windows 7 和所有其他 Windows 版本。 约翰你也会更新 Windows 7 吗?

我认为我们应该让微软来处理这个变化。 如果我们修复 Go 用户,仍然会有非 Go 开发人员。 所以微软无论如何都必须处理这个问题。

此外, FILE_SHARE_DELETE 有自己的小提琴。 例如,阅读https://bugs.python.org/issue15244

实际上,它与 Unix 的语义并不完全相同。

删除文件后,您无法创建同名文件或删除包含该文件的目录,直到句柄关闭。

但是,可以通过在删除文件之前将文件移动到其他位置(如根目录)来解决此问题。

可能还有很多其他我们不知道的类似情况。 我们根本没有使用 FILE_SHARE_DELETE。 您是否建议我们将 Go 库更改为默认使用 FILE_SHARE_DELETE,然后等待用户抱怨他们的程序损坏?

正如@mattn建议的那样,还值得询问其他语言的作用。

我只检查了 Mingw C - 它的行为就像 Go 一样。 使用 fopen 打开文件后,在调用 fclose 之前无法将其删除。 如果任何 Microsoft 开发工具(C、C++、C#、VB 和其他)不同,我会感到惊讶。

而且我看不出我们如何改变这样的默认行为。 有些程序可能有专门打开的文件,所以它不能被其他程序删除。 在我看来,这是 Windows 上的有效方法。 我们必须支持它向前发展。

亚历克斯

7年前添加,我相信:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858

到目前为止,没有人发布任何依赖于当前未记录的 Windows 行为的 Go 应用程序案例。 但我们至少听说过 4 起 Go 应用程序因此在 Windows 上失败的案例。

如上所述,在 os.Remove() 之后有一个简单的文件名重用修复,我们应该记录:
os.Rename(path, unique_path); os.Remove(unique_path)

到目前为止,没有人发布任何依赖于当前未记录的 Windows 行为的 Go 应用程序案例。

这些人甚至不知道可以报告 Go 错误。

你不应该期望这些用户监控 go issue 列表的变化,希望某些事情会影响他们。 他们有生活要过。 我们有责任不要破坏他们使用的工具。

亚历克斯

亚历克斯,很明显你没有被说服。 然后我看到了两条可能的前进路径,以便人们可以在 Go 中编写可移植代码:

  1. Open的新选择加入标志,对于 Windows 意味着FILE_SHARE_DELETE ,而对于其他操作系统则被忽略。 然后,我们鼓励任何编写期望 POSIX 语义的代码的人在任何地方传递这个标志; 或者,
  2. 一个新的 Go 包posix (在某个 repo 的某个地方),它有一个函数Open ,它用 FILE_SHARE_DELETE 包装 CreateFile 但在其他方面表现为os.Open 。 在非 Windows 平台上,它只会直接调用os.Open 。 然后,我们鼓励任何编写期望 POSIX 语义的代码的人在任何地方都使用posix.Open而不是os.Open

这两种方法都需要 Linux 开发人员采取额外的措施来保留 Windows 上的 POSIX 行为,但至少这些方法比编写一次性的 CreateFile 包装器(就像我们刚刚为 containerd 所做的那样)要好。

亚历克斯,很明显你没有被说服。

我没有被说服。 但是你不需要说服我,你需要说服 Go Team。 他们将做出决定。

  1. Open的新选择加入标志,对于 Windows 意味着FILE_SHARE_DELETE ,而对于其他操作系统则被忽略。 然后,我们鼓励任何编写期望 POSIX 语义的代码的人在任何地方传递这个标志; 或者,

我也不喜欢这个选项。

首先,os 包应该为任何操作系统提供可用的工具。 任何特定于操作系统的东西都应该进入系统调用,或者 golang.org/x/sys/windows,或者我们喜欢的任何其他包。

其次,我担心我的程序中会出现FILE_SHARE_DELETE文件类型,即使我不想这样做。 想象一下,如果我导入决定使用FILE_SHARE_DELETE文件模式的外部包。 我怎么知道我的一些代码使用了FILE_SHARE_DELETE ? 我将不得不 grep 包源代码。 或者包文档必须警告其用户。 我想,如果某些包作者直接使用FILE_SHARE_DELETE syscall.CreateFile ,这也可能发生。 但是,我认为,如果FILE_SHARE_DELETE被祝福为 os 包,这将更常见。 我怀疑,对 Windows 一无所知的 Linux 用户会默认使用此标志,以使他们的 Linux 程序“更容易”移植到 Windows。

第三,我们必须记录FILE_SHARE_DELETE标志。 我们不能只说 POSIX。 我们必须描述标志的作用。 简单来说。 还要记住:

实际上,它与 Unix 的语义并不完全相同。

删除文件后,您无法创建同名文件或删除包含该文件的目录,直到句柄关闭。

但是,可以通过在删除文件之前将文件移动到其他位置(如根目录)来解决此问题。

来自https://github.com/golang/go/issues/32088#issuecomment -504321027。 我们也必须记录下来。 以及FILE_SHARE_DELETE标志带来的所有其他功能。

第四,我们需要为FILE_SHARE_DELETE添加新的测试。 这些测试会是什么? 如果我们忘记测试某些功能,后来发现使用FILE_SHARE_DELETE标志无法实现该功能,会发生什么? 如果我们不喜欢它,我们不能删除FILE_SHARE_DELETE标志。 一旦它进入,它就会留下来。 我们必须在未来支持它。

2. 一个新的 Go 包posix (在某个 repo 的某个地方),它有一个函数Open ,它用 FILE_SHARE_DELETE 包装 CreateFile 但在其他方面表现为os.Open

我不明白这怎么可能。 但是,如果可能的话,您应该能够自己创建包。 例如,作为 github.com/jstarks/posix。 不? 我们可以在 golang.org/x/sys/windows/posix 或其他东西下添加包,但我看不出有什么不同。

亚历克斯

os 包应该为任何操作系统提供可用的设施......
我们必须记录 FILE_SHARE_DELETE 标志。 我们不能只说 POSIX...
我们需要为 FILE_SHARE_DELETE 添加新的测试。 这些测试会是什么?

所有这些都在上面得到了解决。

如果我导入决定使用 FILE_SHARE_DELETE 文件模式的外部包。 我怎么会知道

这是将其设为默认值并提供全局标志来禁用它的另一个论点。

我在 golang-nuts 上发帖寻找受影响的 Windows 应用; 我会每隔几天 ping 它一次以保持可见。 如果这没有太多浮出水面,那么将其设为 1.14 的默认提示应该可以澄清一些事情。
https://groups.google.com/d/topic/golang-nuts/8BiP_mPoCd4/discussion

这是将其设为默认值并提供全局标志来禁用它的另一个论点。

顺便提一句。 该标志在 Linux 上应该做什么? Afaiu 我们实际上也错过了在 Linux 中打开文件的功能,因为它们不能被其他进程删除? 基于此https://gavv.github.io/articles/file-locks/#open -file-description-locks-fcntl 应该可以在现代版本的 Linux 上实现它。

该标志(例如var OpenWithout_FILE_SHARE_DELETE = false )将在 syscall_windows.go 中定义,因为它会影响 syscall.Open() 的行为。

特定于 Linux 的标志将具有不同的名称,并在 syscall_linux.go 中定义。 顺便说一句,我不知道如何防止在 Linux 上删除打开的文件,而不是通过对其目录的权限。 您提供的链接描述了“咨询”锁定。

@jstarks在我们考虑引入FILE_SHARE_DELETE标志之前,我们还需要决定如何处理 Go 无法删除正在运行的可执行文件。 我不认为FILE_SHARE_DELETE标志在这里可以帮助我们。 它可以? 如果我们不能实现删除仍在运行的可执行文件,那么我们就不能声称任何 POSIX 兼容性。 FILE_SHARE_DELETE标志看起来像一半的解决方案。

更重要的是,有时我们无法删除甚至完成的进程的可执行文件。 例如,参见#25965、#19491 和#32188 了解详细信息。 基本上我们在进程句柄上调用 WaitForSingleObject 直到它发出信号。 但这并不能保证可执行文件可以被删除。 在尝试删除文件之前,我们必须添加 5 毫秒的等待时间。 甚至这并不总是有效 - https://github.com/golang/go/issues/25965#issuecomment -482037476

另外,与这个问题无关,但是,鉴于我引起了您的注意,也许您可​​以帮助我们恢复 windows-arm 端口。 有关详细信息,请参阅#32135。 基本上@jordanrh1将 Go 移植到 windows-arm,但我们不再有 windows-arm builder。 所以我们甚至不知道端口是否仍在工作或损坏。 如果您可以帮助我们运行 builder,也许我们可以尝试并继续支持 windows-am 端口。 否则端口可能会被删除 - https://github.com/golang/go/wiki/PortingPolicy我不知道,如果有人有兴趣在 Windows 10 Iot 上运行 Go 程序,但我们已经有了这个支持。 仅仅因为我们没有工作的建设者而放弃它会很遗憾。

谢谢你。

亚历克斯

请注意删除临时可执行文件的建议解决方案: https :

golang-nuts 帖子没有结果; 开始了一个新线程:
https://groups.google.com/d/topic/golang-nuts/aRvSo3iKvJY/discussion

至少我认为应该调查的一个不太假设的问题是对文件锁定和依赖它的程序的影响。

FILE_SHARE_DELETE打开每个文件句柄会有效地削弱LockFileEx对 POSIX 咨询锁的语义,因为任何指定了FILE_SHARE_DELETE文件都可以被删除,即使锁是保存在该文件的某个区域上。 1

当然,这只适用于使用从os.File.Fd返回的描述符进行的LockFileEx调用,但这正是最流行的 Go 文件锁定库(目前有33 个已知的导入器,包括 Docker 、gVisor 和 Kubernetes)。 它也在Terraform 中完成,并且LockFileEx用于加载其他 Go 代码

我仍然认为这种改变在最好的情况下似乎是一个不明智的努力(在最坏的情况下是一个 CVE 工厂)。 我真的不明白这些模糊的好处如何超过将要发生的明确的代码破坏。

我还认为大多数 golang-nuts 用户不会完全理解这种变化的含义。 在 Go 团队的许可下,golang-dev 上的帖子可能会更有效地引发讨论(尤其是因为这是一个核心更改)。 实际上,它在技术上会影响 Go 工具链,以上述方式使用LockFileEx


1是的,我知道某些 Windows 锁定实施仍会比 POSIX 上的更强,但如果您可以核对文件,那么使用锁定文件进行资源保留就不会了。

当我打开问题时,我发布到 golang-dev。
https://groups.google.com/d/topic/golang-dev/R79TJAzsBfM/discussion

您列出的任何库是否依赖于 GOOS=windows 行为? 您一再声称此更改会破坏现有代码,但从未提供任何证据。

请停止发表耸人听闻的、无法支持的言论。 它没有澄清任何东西,我觉得它很冒犯。

当我打开问题时,我发布到 golang-dev。 https://groups.google.com/d/topic/golang-dev/R79TJAzsBfM/discussion

似乎那里的每个人都出于自己的原因否决了这个想法(所有这些都类似于本线程前面提到的那些),但如果现在提出这个想法以在 Go 1.14 中应用这个变化,那么后续行动线程可能有用(带有此讨论的提醒链接)。

您列出的任何库是否依赖于GOOS=windows 行为? 您一再声称此更改会破坏现有代码,但从未提供任何证据。

可以肯定地说,这需要一个完整的安全评估(对大量代码)。 我想我确实提供了足够的证据表明人们依赖的行为会随着这个标志的引入而明显改变。 这只是 GitHub 上公开可用的代码。

请停止发表耸人听闻的、无法支持的言论。 它没有澄清任何东西,我觉得它很冒犯。

如果链接和代码支持不够,那么我不知道你的书中会有什么。 你不会找到因为这种变化而停止编译的代码,但你肯定会发现代码的行为与它不同,而且很多人会认为代码因为这些行为变化而“损坏”。

不要把我的反对意见放在个人身上,我认为这里的每个人(包括你)只是想增加他们的意见来改进语言。 如果你要支持核心变革,你应该期待健康的抵抗。

正如我在上面所解释的,使用 FILE_SHARE_DELETE 删除文件与 UNIX 的功能不同。 例如,请尝试此步骤。

主程序

#include <windows.h>
#include <stdio.h>

int
main() {
  // ensure delete the file
  DeleteFile("test.txt");

  // create test.txt with FILE_SHARE_DELETE
  HANDLE h = CreateFile("test.txt",
      GENERIC_READ | GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
      NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  puts("waiting 10sec, please run watch.exe on another cmd.exe");
  Sleep(10000);
  if (DeleteFile("test.txt")) {
    puts("deleted");
  }
  getchar();
  return 0;
}

手表.c

#include <stdio.h>
#include <shlwapi.h>

int
main() {
  // waiting test.txt will be removed.
  while (PathFileExists("test.txt")) {
    puts("watching...");
    Sleep(1000);
  }
  // now test.txt should not exists. So this is possible to create new file
  system("notepad test.txt");
  return 0;
}

生成文件

all : main.exe watch.exe

watch.exe : watch.c
    gcc -o watch.exe watch.c -lshlwapi

main.exe : main.c
    gcc -o main.exe main.c

首先,请运行 main.exe,然后运行 ​​watch.exe。 在 UNIX 上, unlink(2) 立即删除该文件。 因此,正在监视文件存在的另一个进程可以创建具有相同文件名的新文件。 但在 Windows 上,notepad.exe 显示错误对话框“访问冲突”,因为 Windows 不会立即删除该文件。 我不能告诉你这会发生什么。 安全问题? 丢失重要文件? 对不起,我不知道。

@havoc-io Windows 在关闭之前不会删除打开的文件,并且只保留打开文件的锁。 所以使用锁定并不意味着依赖于 os.Remove() 的失败。 请提供具体取决于该故障的示例。

下周我会 ping golang-dev 线程。 两个人回复了它; 没有人读过这个问题,也没有提供任何例子。

耸人听闻的、无法支持的主张不是“健康的抵抗”。

@havoc-io Windows 在关闭之前不会删除打开的文件,并且只保留打开文件的锁。 所以使用锁定并不意味着依赖于 os.Remove() 的失败。 请提供具体取决于该故障的示例。

@networkimprov您将两种不同类型的锁定混为一谈。

锁定文件(使用LockFileEx锁定)通常用于保护资源。 一个例子可能是确保只有一个守护进程实例在运行。 如果 Windows Go 守护程序打开一个文件,然后对该文件执行LockFileEx并成功获取排他锁,则它可能正确地假定锁定文件不会从其下删除。 但是,如果默认启用了FILE_SHARE_DELETE ,那么所有现有的做出该假设的代码都不再正确。 如果某个额外的代理要删除该锁定文件(例如,用户无意中删除了一个目录),那么当新的守护程序决定启动时,原始守护程序可能仍在运行。

如果LOCKFILE_FAIL_IMMEDIATELY标志被省略,锁文件也可以用于管道命令,导致LockFileEx等待轮到它获取锁。 如果锁定文件被无意中删除,它可能会导致同时执行多个本应流水线化的命令。

耸人听闻的、无法支持的主张不是“健康的抵抗”。

没有人在此线程中提出耸人听闻或无法支持的主张。 每个不同意你的提议的人,包括我自己,都提供了代码支持的合理论据。 支持您的提议的人也是如此。

请查看 WinAPI 文档; Windows 在关闭文件之前不会删除打开的文件,并且只为打开的文件持有 LockFileEx() 锁,因此无论删除如何,当文件关闭时,锁都不会持续存在。 尝试在删除后打开锁定(即打开)文件会看到错误,因此无法获得第二个锁定。 等待锁定的线程会打开文件,因此不会被删除中断。

除非我误读了文档,否则您的最后两个方案不受支持。 这是耸人听闻且不受支持的:“这种变化似乎......在最坏的情况下是一个 CVE 工厂。我真的不明白这种模糊的好处如何超过将发生的明确的代码破坏。”

@networkimprov问题是资源限制锁定文件通常基于文件路径。 在文件关闭之前,底层文件对象可能不会被删除,但如果通过DeleteFile删除,它将无法在文件系统1上的先前路径中访问,这将允许创建其他锁定文件在那个路径上,否定了锁定文件的目的。

我不认为这是一个耸人听闻的评论——如果代码依赖于某些不再正确的操作系统不变量,这样的事情似乎很明显会导致安全问题。 而且这里的好处确实看起来很模糊(正如一些人评论的那样:这不会使 Windows 行为与 POSIX 保持一致)并且破坏似乎很明显(它至少会破坏某些锁定行为)。

1至少在测试和基于此评论时似乎是这种情况。

来自https://github.com/golang/go/issues/ 的“在最新版本的 Windows 中,我们更新了 DeleteFile(在 NTFS 上)以执行 'POSIX' 删除,其中文件立即从命名空间中删除” 32088

重新受益,我认为您应该查看整个线程。

AFAIK 那个版本的 Windows 没有发布。

@networkimprov我相信在 Windows 10 上已经是这种情况。如果在打开文件时将FILE_SHARE_DELETE指定为CreateFileW ,我似乎能够删除用LockFileEx锁定的文件。

DeleteFileW() 在那种情况下不会出错。 你怎么知道它是立即从文件系统中删除的,还是在文件/应用程序关闭时删除的? 您是否在删除文件后尝试打开/重新创建文件?

实际上,它在技术上会影响 Go 工具链,它_也_以上述方式使用LockFileEx

go命令中,我们非常需要 POSIX 语义,并且不应以任何方式依赖于FILE_SHARE_DELETE的缺失。 事实上,我专门添加了cmd/go/internal/robustio包来解决 Windows 文件锁定偏离 POSIX 的问题。

@ianlancetaylor我曾多次向 golang-nuts & -dev 发帖,寻求任何依赖于当前(未记录的)Windows syscall.Open() 行为的项目的评论。 没有一个用例出现。

由于上面已经出现了几个需要 FILE_SHARE_DELETE 的用例(并且由于 Microsoft 明确要求我们使用它),让我们在 1.14 之前的版本中默认设置此标志,并重新评估循环期间原始行为表面是否有任何用例。

正如本线程前面所建议的,需要不带此标志的 open() 调用的人可以使用CreateFileW()os.NewFile()自己滚动。

我们至少可以为此暴露一个旋钮吗?

这对于在 Windows 上实现日志轮换非常有用。

这对于在 Windows 上实现日志轮换非常有用。

我不确定,但我猜它不会起作用,因为如果文件句柄未关闭,FILE_SHARE_DELETE 不允许使用原始文件名创建新文件。

@Random-Liu 您是在寻求一种在 Open() 上启用 fsd 还是禁用它的方法?

@mattn它对我os.Rename旧日志文件之后,我可以使用原始名称创建一个新的日志文件,并通知守护程序重新打开日志文件。

@networkimprov我想要一种启用FILE_SHARE_DELETE 。 我目前正在直接使用syscall.CreateFile

我的 Windows 版本:

Major  Minor  Build  Revision
-----  -----  -----  --------
10     0      17763  0

@Random-Liu 但我想我们不能用它来模拟 UNIX 行为。

https://github.com/golang/go/issues/32088#issuecomment -510345908

外部应用程序无法访问原始文件名。

mattn,没有必要一直重复这一点。 已经说过,当文件名必须在句柄关闭之前可重用时,我们必须记录(在 Windows 上)在删除文件之前重命名文件的需要。

如果它是在应用程序本身关闭之前重命名文件,我觉得对于想要进行 logrotate 的用户来说没有任何优点。

@ianlancetaylor重试: https :

我已将此问题标记为发布阻止程序。

@bcmills我可以让您有兴趣发布 CL 吗?

这一行: https :
只需要: sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)

编辑:验证它会创建一个文件的测试,然后在关闭它之前尝试重命名它。

我们很快就需要这个,所以在冻结之前有时间让任何问题浮出水面......

os.Remove() 文档还应该提到,在 Windows 上,如果文件名需要在文件关闭之前可供重用,则应在删除之前重命名打开的文件。 在打开文件关闭之前,Windows 不会完成对打开文件的删除操作。

看起来最新版本的 Windows 满足https://github.com/golang/go/issues/32088#issuecomment -502850674。 有关详细信息,请参阅https://github.com/papertrail/go-tail/pull/10#issuecomment -529460973。 我们现在可以重用已删除文件的名称,即使旧文件仍有打开的句柄,前提是这些句柄是使用FILE_SHARE_DELETE共享模式打开的。 删除前无需重命名。 也许在 Go 中默认设置FILE_SHARE_DELETE终于有意义了?

这就是意图; 我们只需要有人提交一份 CL。 (我愿意,但我不能签署 CLA。)
我之前的两条评论描述了必要的改变......

@jstarks @jordanrh1 @thaJeztah

问题:
我们如何获得一致的行为?
如果该程序在“最新的 Windows”上运行,它将允许创建一个与“待删除”文件具有相似名称的文件,旧版本将因错误而中断。

这实际上可能会说:“通过了测试/质量保证,但现在不适用于大多数目标”,并且
将来“通常有效,有时无效”(距现在 2 年的开发人员将无法重现“文件已退出”错误)。

我们需要弄清楚如何识别“旧”版本,或者更改这些版本的默认行为,或者在“有待删除”时改进错误——我不确定我们该怎么做。
如果我们不能,我们如何处理不一致。

@guybrand如上所述,我们将记录在 Windows 上“如果文件名需要在文件关闭之前可重用,则应在删除之前重命名打开的文件。”

这适用于新旧 Windows 版本以及 POSIX。

这听起来像是一种不同的方法:“在重用之前总是重命名,即使在新的 Windows 或 POSIX 上也是如此”
我喜欢它——它支持一种在任何地方都会成功的普遍做法。

更改https://golang.org/cl/194957提到这个问题: syscall: allow FILE_SHARE_DELETE on syscall.Open on Windows

golang-nuts线程在这里,并且没有响应。 这似乎表明没有人阅读golang-nuts并知道这种行为是依赖它。

golang-dev线程是herehere ,后者(最近的线程)也没有获得反对意见。

@alexbrainman -2'd CL 基于明显缺乏决策来实施它,所以我将此问题重新标记为NeedsDecision以便我们可以得到明确的方向。

就我个人而言,这个决定似乎很明确:鉴于微软在这个线程上的人支持将默认行为更改为使用FILE_SHARE_DELETE (https://github.com/golang/go/issues/32088 #issuecomment-502850674),并且golang-nuts上的任何人似乎都不在乎一种或另一种方式,我认为我们应该这样做。

我在这个线程上看到的反对意见似乎分为两类:

  1. @alexbrainman (https://github.com/golang/go/issues/32088#issuecomment-504321027) 和@mattn (https://github.com/golang/go/issues/32088#issuecomment-494417146) 的关注似乎是因为行为仍然不匹配 POSIX,所以期望 POSIX 语义的用户可能会感到困惑。 (但 Windows 上的Open已经不提供 POSIX 语义,所以对我来说,这种担忧似乎与提议的更改无关。)

  2. @havoc-io 的担忧(https://github.com/golang/go/issues/32088#issuecomment-510314947)似乎是FILE_SHARE_DELETE在 Windows 上运行时会减少文件锁定包提供的不变量. 但是这个论点对我来说似乎有点令人困惑,因为作为示例的具体包( github.com/gofrs/flock )明确指出“锁定行为不能保证在每个平台上都是相同的”,并且来自快速审核没有一个具体示例似乎依赖于锁定和文件删除之间特定于 Windows 的交互。

不过, https: //github.com/golang/go/issues/32088#issuecomment -498690757 中的评论确实让我很感兴趣:

我同意@ianlancetaylor改变整个工具链的行为,正如https://codereview.appspot.com/8203043/ 中提出的那样,这不是一个好主意。 当使用文件句柄作为锁定文件时,会立即想到这个建议的一个问题。 我们使用这种行为。

@maruel ,您能否详细说明您的程序如何依赖文件锁定和文件删除或重命名之间的特定于 Windows 的交互?

我们在 Chrome 基础设施中使用文件存在作为锁定机制。 我想说不要太担心这个用例。 实际上,根据我的理解,O_CREATE + FILE_FLAG_DELETE_ON_CLOSE语义足以满足此需求,并且可以用Global\\命名空间中的互斥锁替换,我们已经在 python 代码库中这样做了。

未提及此解决方案:

path := "delete-after-open"
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
if err != nil { ... }
// defer statements are executed in LIFO
defer os.Remove(path)            // or defer os.Rename(path, path+"2")
if err != nil { ... }
defer fd.Close()

它有两个缺点:

  • Close() 在 Open() 之后不正确
  • 恢复错误代码更复杂

@iwdgo此解决方案适用于单进程使用。 上面的大部分讨论都是关于来自多个进程的并发访问。 这就是使旗帜变得重要的原因。

为了补充@bcmills对该问题的重新描述,我们查看了其他三个跨平台开发工具:

七年前, Mingw-w64将其
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858

六年前, Erlang将其设为默认值:erlang/

由于当时他们考虑的 MSVC 运行时的限制, Python无法采用它: https :

第二个 golang-nuts 线程也没有反对意见:
https://groups.google.com/d/topic/golang-nuts/aRvSo3iKvJY/discussion

你想让 FILE_SHARE_DELETE 默认为 Open() 吗? 我们的讨论不应该是这样。

  1. @alexbrainman ( # 32088 ( comment) ) 的担忧似乎是因为行为仍然不符合 POSIX ......

我主要关心的是破坏人们的程序。 类似于@minuxhttps://codereview.appspot.com/8203043/#msg10 上所说的

另一个原因:使用 Windows 的人接受无法重命名或
删除打开的文件是常态,并且已经习惯了这一事实。

我们不能在这里默默地改变 Go 1.0 程序的行为,也许人们
实际上依赖它。

Go 1.0 兼容性承诺在这里发生了什么? 在https://golang.org/doc/go1compat中的什么地方说,如果您在 go-nuts 宣布您的意图,可以更改打开文件的行为?

或许我们可以在引入 Go 2 后改变这种行为? 我们可以使用模块来保护现有代码不被破坏吗? 我是否错误地认为这是为模块设计的?

就我个人而言,这个决定似乎很明确:鉴于微软在这个线程上的人支持将默认行为更改为使用FILE_SHARE_DELETE ...

如果 Microsoft 对您来说是这个主题的权威,那么您应该看看 Microsoft 开发工具是做什么的。

我使用了这个程序https://play.golang.org/p/4ZPmV6Df3SD类似于@mattnhttps://github.com/golang/go/issues/32088#issuecomment -493753714 上发布的内容我用最近的 C 编译器构建了它

Microsoft (R) C/C++ 优化编译器版本 19.16.27030.1 for x64
版权所有 (C) 微软公司。 版权所有。

而且,当我运行程序时,在程序存在之前我无法删除C:\Users\Alex\AppData\Local\Temp\a.txt文件。

与我最近使用的类似 C# 程序https://play.golang.org/p/SuM2iWYpZir相同

Microsoft (R) Visual C# 编译器版本 2.10.0.0 (b9fb1610)
版权所有 (C) 微软公司。 版权所有。

同样,这个 C 程序https://play.golang.org/p/6HgxePzEW_W用最近的 Mingw 构建

gcc (x86_64-win32-seh-rev0, 由 MinGW-W64 项目构建) 7.3.0
版权所有 (C) 2017 Free Software Foundation, Inc.

有同样的效果。 因此,这些工具都没有默认设置 FILE_SHARE_DELETE 标志。

我个人认为这种变化没有任何好处。 这包括我的 Go 贡献。 在进程运行时,我们仍然无法删除可执行文件。 如果任何进程在其中设置了当前目录,我们仍然会在删除目录时遇到问题。

亚历克斯

你想让 FILE_SHARE_DELETE 默认为 Open() 吗?

目的是让 os.Open 默认使用 FILE_SHARE_DELETE。 这个https://golang.org/cl/194957是计划。

亚历克斯

我主要关心的是破坏人们的程序

到目前为止,没有人能指出一个会中断的程序示例,因为它依赖于在尝试重命名/删除使用 Go 打开的文件时出现错误。 但是有记录的案例表明当前行为会破坏 Windows 上的 Go 应用程序。

Go 1.0 兼容性承诺在这里发生了什么?

os.Rename() 和 .Remove() 的 Go 文档对此 Windows 功能保持沉默,但有15 次提及其他 Windows 特定行为。 顺便说一句,Go 2 已经发布: https :

gcc (x86_64-win32-seh-rev0, 由 MinGW-W64 项目构建) 7.3.0

你在 gcc 中使用谁的 C-lib——也许是微软的? Mingw-w64 是 v6.0,它在 FILE_SHARE_VALID_FLAGS 中包含 FILE_SHARE_DELETE。

如上面关于 Python 的记录,过去 MSVC 运行时出于未知原因不允许某些 CreateFile() 标志组合。 我不知道情况是否仍然如此,但这可能是 MS 没有更改其 C-lib fopen() 的原因。

我看不出这种改变有什么好处。

上面已经有好几个人反复解释了这些好处; 特别是允许 os.Rename() 在打开的文件上。

标记为提案,因为讨论不容易收敛。

尝试总结到目前为止的问题和讨论:

  • 在 Unix 系统上,如果一个进程打开一个文件,第二个进程可以删除该文件。 原始进程可以继续使用该文件(它不再有名称),包括读取和写入,都成功了。 在最后一个打开的引用消失之前,文件内容不会从磁盘中删除。

  • 在 Windows 系统上,默认情况下,如果一个进程打开一个文件,第二个进程_不能_删除该文件。 只有在没有其他进程打开文件时才能删除文件。 FILE_SHARE_DELETE 标志改变了这种行为:如果对文件的所有打开引用都是用 FILE_SHARE_DELETE 打开的,那么另一个进程可以调用 DeleteFile,它会成功而不是失败。

FILE_SHARE_DELETE 的 Windows 行为似乎仍然与 Unix 略有不同。 MSDN 页面说:

如果应用程序尝试删除具有为正常 I/O 打开的其他句柄或作为内存映射文件(打开其他句柄时必须已指定 FILE_SHARE_DELETE)的文件,DeleteFile 函数将失败。

DeleteFile 函数将文件标记为在关闭时删除。 因此,在文件的最后一个句柄关闭之前,不会发生文件删除。 随后调用 CreateFile 以打开文件失败并显示 ERROR_ACCESS_DENIED。

也就是说,在打开的引用消失之前,不会从文件系统中删除文件 _name_。 这与 Unix 不同,在 Unix 中,名称在打开的引用关闭之前就消失了。 因此,如果您要删除文件以准备重新创建同名文件,则该文件在 Unix 中有效,但在 Windows 中无效,甚至在 FILE_SHARE_DELETE 中也无效。

最初的建议是更改 syscall.Open 以始终设置此标志。 鉴于系统调用应该接近内核接口,添加该标志似乎不合适。 但我们可以考虑是否应该在 os.Open / os.OpenFile 期间设置。

@alexbrainman提出 Unix 和 Windows 不同的论点,即使我们设置此标志,它们也会保持不同,因此我们不应该通过在用户背后进行此更改来混淆问题。

来自微软的@jstarks说微软希望我们自动设置 FILE_SHARE_DELETE 并且 Windows 的“最新版本”(不清楚是哪个版本,或者是否已发布)更改 DeleteFile 以使名称的 Unix 语义在从 DeleteFile 返回时消失。 因此,如果我们这样做,在最新的 Windows 上,我们真的会在 Unix 和 Windows 上获得相同的行为。

@networkimprov认为我们应该做出改变,并且没有会破坏程序的实际示例。

@alexbrainman似乎仍然不相信。

@mattn还建议我们不应该自动设置标志,部分原因是(至少在每个人都使用最新的 Windows 之前)它仍然与 Unix 不同。

总的来说,包 os 正试图提供一个可移植的 API,而且似乎 - 特别是在 Windows 更改时 - 自动设置标志会有所帮助。 我们在 os.Open 中自动设置了其他标志,以帮助用户减少对行为的惊讶,特别是 O_CLOEXEC (close-on-exec)。 由于在 os.Open 中设置此标志而在 Windows 上中断的任何程序也必然会在 Unix 上中断,对吗?

特别是因为至少微软的一位工程师说我们应该设置它,所以在包 os.s 中设置似乎是可以的。 不希望设置标志的人仍然可以退回到包系统调用。

@alexbrainman@mattn ,假设我们在 os.Open 中设置了标志。 我们知道有些程序会更好地工作。 您是否有会破坏的真实(或可能)程序的具体示例? 这些程序在 Unix 上不会被破坏吗? 谢谢。

@rsc ,感谢您的意见。 在包“os”中启用它对我有用。 如果采用这种方法,则应提供一个启用 FILE_SHARE_DELETE 的 syscall.Open() 标志以确保完整性。

为了澄清您的总结,流程边界不是一个因素; 在单个过程中效果是相同的。 并且该标志也会影响文件重命名; 与文件删除不同,它使用标志立即完成。 (这允许在删除后立即提供文件名可用性的解决方法。)

谢谢@networkimprov。 是的,我了解流程边界并非严格相关; 它只是使情况更容易描述。

我在问题文本中更改了标题并修改了行动计划:

a) os.Create/Open/OpenFile() 应始终在 Windows 上启用 file_share_delete,
b) Windows 上的 syscall.Open() 应该接受一个启用 file_share_delete 的标志,并且
c) Windows 上的系统调用应该为新标志导出一个常量。

os.Remove() 的文档还应注意,要在 Windows 上删除打开的文件后重用文件名,必须这样做: os.Rename(path, unique_path); os.Remove(unique_path)

不能否认存在用户使用当前 Go 的 os.Open 行为实现独占处理的可能性。

但是有记录的案例表明当前行为会破坏 Windows 上的 Go 应用程序。

这些是什么? 问题编号是多少? 你所说的break什么意思? 他们什么时候断的?

你在 gcc 中使用谁的 C-lib——也许是微软的? Mingw-w64 是 v6.0,它在 FILE_SHARE_VALID_FLAGS 中包含 FILE_SHARE_DELETE。

我不知道。 我刚刚从网上下载了 x86_64-7.3.0-release-win32-seh-rt_v5-rev0.7z 文件。 你可以google一下。 它的版本是 7.3。

我看不出这种改变有什么好处。

好处上面已经反复解释过,...

您在报价单中留下了personally这个词。 我谈谈我自己的需求。 这包括我的 Go 项目贡献。

尝试总结到目前为止的问题和讨论:

您没有说 Windows 上的 Microsoft 开发人员工具默认不提供此标志。 查看我的评论https://github.com/golang/go/issues/32088#issuecomment -531713562

你也忽略了我关于 Go 1 兼容性的评论

Go 1.0 兼容性承诺在这里发生了什么? 在https://golang.org/doc/go1compat中的什么地方说,如果您在 go-nuts 宣布您的意图,可以更改打开文件的行为?

这种变化如何适应 Go 1.0 的兼容性承诺?

总的来说,包 os 正试图提供一个可移植的 API,而且似乎 - 特别是在 Windows 更改时 - 自动设置标志会有所帮助。

我同意此更改旨在将 Unix 软件移植到 Windows。

不幸的是,牺牲了 Windows 用户的利益。 这种变化会让 Windows 用户和开发人员感到惊讶,因为 Windows 上没有其他程序或工具以这种方式工作。 也许在未来,但还没有。

即使对于将 Unix 软件移植到 Windows 的人来说,恕我直言,这种变化也做得很糟糕。 请参阅我的评论中的We still won't be able to delete executable files while process is running. We will still have problems deleting directories if any process have their current directories set there.部分https://github.com/golang/go/issues/32088#issuecomment -531713562

我们在 os.Open 中自动设置了其他标志,以帮助用户减少对行为的惊讶,特别是 O_CLOEXEC (close-on-exec)。 由于在 os.Open 中设置此标志而在 Windows 上中断的任何程序也必然会在 Unix 上中断,对吗?

我不知道你在这里是什么意思。 Windows 上的 O_CLOEXEC 意味着防止文件句柄逃逸到子进程中。 并且 O_CLOEXEC 总是设置在 os.Open 在 Windows 上。 因此,Windows 上的子进程无法访问使用 os.Open 打开的文件。 所以呢? 听起来很合理。

这些程序在 Unix 上不会被破坏吗?

我不明白你的问题。

您是否有会破坏的真实(或可能)程序的具体示例?

我不知道,如果我有这样的程序。 这是不可能的。 我不记得我写过的所有程序。 我不记得他们做了什么。

但我可以想象一个 Windows 服务打开预定义的文件,比如说c:\mysvc.txt ,并保持打开状态。 想象另一个程序检查服务是否处于活动状态,并在必要时重新启动服务。 这个另一个程序可以尝试删除c:\mysvc.txt ,如果文件消失了,它可以安全地假设服务进程已经死了。

另外,我可以想象,某些程序可能希望阻止用户删除或移动他们的文件 - https://stackoverflow.com/questions/11318663/prevent-a-user-from-deleting-moving-or-renaming-a-file

亚历克斯

这是在 Windows 上重新部署 Go 的数据点:
Win8 7 年前出货。
这个影响 Win8+ 的 Go 错误仅在 5 个月前被报告:#31528

@rsc任何在尝试重命名/删除用 Go 打开的文件时会出错的程序在 Unix 上都会被破坏。 例如,Alex 的c:\mysvc.txt场景。

如果在 Windows 上开发并部署到 Linux/etc(反之亦然),这可能是错误的来源。

我们的用例是我们有一个记录到文件的记录器。
日志文件支持轮换(例如,如果日志文件> maxSize 然后轮换)。
我们还有阅读器可以打开这些文件并在阅读时保持打开状态。

如果没有设置FILE_SHARE_DELETE的能力,我们就无法在有活跃读者的情况下执行轮换(重命名和/或删除)。

@mattn

不能否认存在用户使用当前 Go 的 os.Open 行为实现独占处理的可能性。

可能是这样,但无论如何,这样的代码在 Unix 上是不正确的。 包 os 旨在成为一个可移植的接口。 此外,我们对_实际_程序比对可能性更感兴趣。

@alexbrainman

您没有说 Windows 上的 Microsoft 开发人员工具默认不提供此标志。 请参阅我的评论 #32088(评论)

是的,但那是 C; 我们正在谈论围棋。 我们一般不会模仿 C API。 我们正在尝试在包 os 中提供一个大部分可移植的抽象。 关于 O_CLOEXEC 的要点在于,在 Unix 系统上 os.Open 设置了 O_CLOEXEC,即使在相应的 C 调用中默认没有设置它。 我们_do_尝试提供在所有系统中表现大致相同的有用默认值。

就 Go 1 的兼容性而言,更改一个操作系统上的极端情况行为以更好地与其他操作系统保持一致似乎没问题。 我们不承诺永远保留所有跨系统的不兼容性。

坚持 Window 特定行为的 Windows 用户始终可以选择使用 package syscall,这将清楚地表明所达到的行为实际上仅适用于 Windows。

同样,您是否有会破坏的真实程序的具体示例?

@cpuguy83 ,感谢您提供帮助的程序的实际用例。

@rsc正如你所说,我还没有弄清楚 FILE_SHARE_DELETE 的破坏性影响。 同样,也没有发现可以通过此更改实现 logrotate 的确认。

但是,一旦 Go 包含了可以删除文件的更改,即使 Go 无法通过此更改在 Windows 上实现 logrotate,用户也会期望这种行为。 所以我们必须仔细考虑这种变化。 基本上,Windows 无法删除未使用 FILE_SHARE_DELETE 标记的文件。 用户可能认为此更改将允许 Go 删除​​任何文件。 特别是,由 os.NewFile 创建的带有句柄的文件可以操作 os.File 结构的方法,即使这个标志没有设置。 我想程序员应该知道文件可以在 Windows 上删除,也可以不自己删除。 我更喜欢一种机制,它允许 FILE_SHARE_DELETE 通过标志传递 os.OpenFile 而不是默认值。 我认为应该遵循操作系统的默认行为。

是的,但那是 C; 我们正在谈论围棋。

还有 C#。 我没有调查,但我很确定 Windows 上的任何其他开发工具都会以这种方式运行。

就 Go 1 的兼容性而言,更改一个操作系统上的极端情况行为以更好地与其他操作系统保持一致似乎没问题。

对于非 Windows 用户来说,这是极端情况。 对于 Windows 用户,此行为是标准行为。

同样,您是否有会破坏的真实程序的具体示例?

考虑一下我上面具体的服务示例。

@cpuguy83 ,感谢您提供帮助的程序的实际用例。

@cpuguy83你能多谈谈你的问题吗? 我想看看这个改变将如何解决你的问题,所以我想看看一些真实的代码。 如果您可以提供一些小程序,该程序现在已损坏,但一旦解决此问题就可以工作,那将不胜感激。 它不一定要建造,但至少它应该提供足够的供大家判断。 谢谢你。

基本上,Windows 无法删除未使用 FILE_SHARE_DELETE 标记的文件。 用户可能认为此更改将允许 Go 删除​​任何文件。

我同意这是一个很好的观点。 只有 Go 程序打开的文件才能被删除。 但是非 Go 程序打开的所有其他文件仍然无法删除。 这将使 os.Remove 变得不可预测 - 有时它会起作用,有时它不会。

亚历克斯

我有代码调用修补的 syscall.Open() 来重命名打开的文件。 这就是实现日志轮换的方式; 有用。

// several processes or goroutines do this
   fd, err := os.OpenFile("log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, ...)
   _, err = fd.Write(log_entry) // one or more times
   err = fd.Close()

// log rotator process does this
   err := os.Rename("log", "log_old") // active writers not disturbed
   fmt.Println(err) // "sharing violation" without FILE_SHARE_DELETE

但是您不需要任何人告诉您:-/

自然,来自 os.NewFile() 的 os.File 可能具有许多不同于来自 os.Create/Open/OpenFile() 的属性,这是一个特性,并不令人意外。

您声称 Microsoft C fopen() 行为是 Windows 默认行为; 这不是真的。 没有 CreateFile() 默认共享模式; 这不是FILE_SHARE_READ | FILE_SHARE_WRITE

您断言已部署的 Windows 程序通常假定__其他供应商的程序__始终以特定方式打开文件。 如果是这种情况,Microsoft 不会要求我们默认启用 file_share_delete。

@networkimprov

但是您不需要任何人告诉您:-/

我想知道外部进程可以在任何情况下正确删除和重命名文件。

我想知道的是:包含该更改后,日志文件是从 Go 输出的,外部进程执行的 logrotate 工作正常,并且它可以作为生产准备工作而没有任何错误。

@alexbrainman

不幸的是,代码很复杂,但这是实际的真实代码: https :

目前拟议中的减排是刚刚叉os.OpenFile和syscall.Open: https://github.com/moby/moby/pull/39974/files

需要明确的是,只需选择从 OpenFile 传递FILE_SHARE_DELETE甚至传递到syscall.Open就足以处理我们的案例。

不幸的是,代码很复杂,但这是实际的真实代码: https :

目前拟议中的减排是刚刚叉os.OpenFile和syscall.Open: https://github.com/moby/moby/pull/39974/files

@cpuguy83感谢您的指点。

不幸的是,我对它的简要了解并没有提供足够的信息来判断当前的问题是否是解决方案。 我必须从 repro 开始(可能是这个 https://github.com/moby/moby/issues/39274#issuecomment-497966709 )并从那里拿走它。 我不确定我什么时候有时间花在这上面。 如果您有比我的计划更好的建议,请告诉我。

亚历克斯

下面是标准实践日志轮换的工作演示,每次打开日志文件时,有 100 个 goroutine 以 0.1-2 秒的间隔记录 50 行,另一个 goroutine 以 1 分钟的间隔轮换日志。 我昨天在 Win7 笔记本电脑上运行了几个小时,错误为零。

它使用 syscall.Open() 的补丁构建,启用 FILE_SHARE_DELETE; 否则它会失败。 如果没有这个标志,人们可以设计一个更复杂的日志记录方案,但它还有许多其他用途; 我自己的代码出于不同的原因重命名打开的文件。

@rsc ,如果仍有任何疑问,我相信这可以完成您的建议。 (如上所述,我在 golang-dev 和 golang-nuts 中反复发帖询问依赖当前行为的项目,但结果为零。)

package main

import (
   "fmt"
   "os"
   "math/rand"
   "syscall"
   "time"
)

const kLogFile = "winrotate"

func main() {
   syscall.Open_FileShareDelete = true
   rand.Seed(time.Now().UnixNano())

   for a := 0; a < 100; a++ {
      go runLog()
   }

   fmt.Println("rotating to "+ kLogFile +"N.txt")
   for a := 0; true; a++ {
      if a >= 5 {
         a = 0
      }
      time.Sleep(60 * time.Second)
      err := os.Rename(kLogFile +".txt", kLogFile + string('0'+a) +".txt")
      if err != nil {
         fmt.Println(err)
         os.Exit(1)
      }
   }
}

const kLineVal = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func runLog() {
   aLine := make([]byte, 102)
   for {
      aFd, err := os.OpenFile(kLogFile +".txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
      if err != nil {
         fmt.Println(err)
         return
      }
      for _, aV := range kLineVal {
         for a := range aLine {
            aLine[a] = byte(aV)
         }
         aEnd := aV - ('z' - 100) // limit line len to 100
         copy(aLine[aEnd:], []byte{'\r','\n'})
         _, err = aFd.Write(aLine[:aEnd + 2])
         if err != nil {
            fmt.Println(err)
            return
         }
         time.Sleep(time.Duration(100 + rand.Intn(1900)) * time.Millisecond)
      }
      err = aFd.Close()
      if err != nil {
         fmt.Println(err)
         return
      }
   }
}

syscall_windows.go 的补丁:

diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go
index de05840..e1455d5 100644
--- a/src/syscall/syscall_windows.go
+++ b/src/syscall/syscall_windows.go
@@ -245,6 +245,8 @@ func makeInheritSa() *SecurityAttributes {
    return &sa
 }

+var Open_FileShareDelete = false
+
 func Open(path string, mode int, perm uint32) (fd Handle, err error) {
    if len(path) == 0 {
        return InvalidHandle, ERROR_FILE_NOT_FOUND
@@ -270,6 +272,9 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) {
        access |= FILE_APPEND_DATA
    }
    sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE)
+   if Open_FileShareDelete {
+       sharemode |= FILE_SHARE_DELETE
+   }
    var sa *SecurityAttributes
    if mode&O_CLOEXEC == 0 {
        sa = makeInheritSa()

@networkimprov该文件未被外部进程重命名。 如果重命名文件是在同一个过程中进行的,还有其他一些方法可以实现它。

https://github.com/mattn/go-sizedwriter/blob/master/_example/example.go#L14

以下是标准实践日志轮换的工作演示

谢谢你的例子, @networkimprov 。 我现在可以看到您正在尝试做什么。

您的代码的唯一问题是,如果某些外部进程将在没有 FILE_SHARE_DELETE 的情况下打开您正在写入的文件,那么无论我们在 Go 中进行何种更改,您都会遇到与现在相同的问题。

@cpuguy83我不会看你的问题(正如我在这里承诺的那样 https://github.com/golang/go/issues/32088#issuecomment-536233195 ),因为@networkimprov示例给了我一张他正在尝试的图片去做。

亚历克斯

Alex,如果允许外部访问,可以循环重命名尝试,如果不允许,则限制活动日志文件。

Mattn,旋转重命名通常由单独的过程完成。

这里似乎没有明确的共识。 具有 Unix 背景的开发人员似乎支持这一点,但我们最活跃的两个 Windows 开发人员( @alexbranman和 @mattn)却不赞成。 这一变化也只会真正统一最新版本的 Windows,它具有新的类 Unix 行为 FILE_SHARE_DELETE。

几年后重新审视这个话题可能是值得的,看看新标志行为的可用范围有多大,以及其他语言是否已经收敛到一个共同的行为。 如果有人想在两年左右的时间内提交一个新问题来重新考虑这一点,那很好。

但就目前而言,鉴于缺乏共识,这似乎是一种可能的下降

开放一周以供最终评论。

是否存在仅提供该选项的争论?

如果我们正在寻找更多的声音来加入,我肯定会赞成做_something_。 目前,唯一的解决方案是从 Go 的标准库中复制一堆代码,更改一行,然后永远维护该分支。 日志监视器或实时“尾巴”的每个实现最终似乎都会这样做。

例如,请参阅https://github.com/elastic/beats/blob/master/libbeat/common/file/file_windows.go#L85 -L103

如果我正确地遵循,这个提案实际上有两个部分:

  1. 打开文件时允许使用标志 FILE_SHARE_DELETE -
    实现应该简单安全,开发人员必须明确
    打开新文件时请求此模式
  2. 默认情况下打开此标志 - 可能有风险,并且仅得到很好的支持
    使用最后一个 Windows 10 版本时。

如果所有人都同意 1,也许我们可以在开发人员时允许该标志
现在明确要求它,并在几年后重新访问 2。

2019 年 10 月 2 日,星期三,19:32 Mark Dascher, notifications @github.com 写道:

如果我们正在寻找更多的声音来加入,我肯定会赞成
做某事。 现在,唯一的解决方案是从字面上复制一个
一堆Go标准库中的代码,改一行,然后
永远保持那个叉子。 日志监视器或实时的每个实现
“尾巴”最终似乎做到了这一点。

例如,请参阅
https://github.com/elastic/beats/blob/master/libbeat/common/file/file_windows.go#L85 -L103


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4VFQLSYVI66ENT6G4LQMTLJ7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAFRRIQ#issuecomment-537598114
或使线程静音
https://github.com/notifications/unsubscribe-auth/ABNEY4U5L3WFZYNIUOSEY2TQMTLJ7ANCNFSM4HNPNYIA
.

绝对赞成_至少_有1. (打开文件时允许使用标志FILE_SHARE_DELETE)

只是为了重新审视我之前的建议跟进示例实现

syscall.FILE_SHARE_DELETE标志非常适合os.OpenFileflag参数,并且会提供一个简单的机制来按需启用此行为。 它不会与 Windows 上的任何其他os.O_*标志发生冲突(这可以强制/设计),并且它提供了一种指定系统特定文件打开行为的惯用方式(只要os ' s 面向 POSIX 的接口在 Windows 上被认为是惯用的)。 这与已用于在 POSIX 平台上传递特定于系统的文件打开行为的路由相同(例如,Linux 上的syscall.O_DIRECTORY )。

即使决定不默认打开它(我认为这是正确的选择),我也同意该标志有其用例(包括@networkimprov 指出的那些),并且它会很有用无需求助于CreateFileW调用和复制样板代码即可打开它。

请考虑上面的替代方案 [1],它允许开发人员在必要时设置FILE_SHARE_DELETE选项并防止分叉的 Go 标准库代码块。

__Rust__ 默认情况下也会设置此标志,并允许您_关闭_任何 FILE_SHARE_* 选项。
https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html

@rsc我能问一下针对该提案提出的哪些技术论据没有被驳倒吗?

@networkimprov在阅读了整个提案和讨论后,我认为您需要关闭它并明确重新打开一个新问题,以明确向 ossyscall 包添加新的 Windows 特定标志。 这将确保新行为是选择加入而不是选择退出。 目前的提议意味着改变现有的代码,这让人们感到担忧。

@rsc我能问一下针对该提案提出的哪些技术论据没有被驳倒吗?

很抱歉,但这不是提案流程的运作方式。 仅仅“反驳”别人的论点是不够的。 “提案过程的目标是及时就结果达成普遍共识。” 关于这里的前进道路没有明确的共识。 已经提出了技术论据,但它们并没有说服为 Windows 移植做出重大贡献的主要 Go 开发人员(即@alexbrainman和@mattn)。 除了缺乏明确的共识之外,今天也没有明确的紧迫性迹象:在提交此问题之前,Go 在 Windows 上运行良好近 10 年。 据我了解,不理会一切意味着 Go 将继续像往常一样正常工作。

我提交了 #34681 以提供一种更简单的方法来使用 FILE_SHARE_DELETE 打开文件,以防(仍有可能)该文件被拒绝。 开始一个仅限于这个想法的新线程似乎比继续这个已经很长的线程更有帮助。

@rsc ,在你拒绝之前,让我们听听 Alex 对你的 O_ALLOW_DELETE 提案的看法。

在本次讨论的早期,他反对设置 file_share_delete 的新 os.OpenFile() 标志,原因与他不同意您的 os.Create/Open/OpenFile() 建议的原因相同。 他担心其他程序会假设没有人以这种方式打开文件,因为 MSVC fopen() 不能这样做。 因此,那些(仍未指定)程序将破坏设置标志的 Go 程序。

亚历克斯,如果允许外部访问,可以循环重命名尝试,...

如果您准备循环重命名,则无需更改 Go repo 代码中的任何内容。 您的程序将像现在一样运行良好。

目前,唯一的解决方案是从 Go 的标准库中复制一堆代码,更改一行,然后永远维护该分支。

我认为将代码复制到单独的包中很好。 在调查https://github.com/moby/moby/pull/39974 时,我有同样的想法。 我认为那里不需要维护太多代码。 事实上我实现了

https://github.com/alexbrainman/goissue34681

随意复制或按原样使用。 也许,鉴于人们对这个功能非常感兴趣,您实际上创建了可以被其他人共享和使用的适当包。 通过这种方式,您可以使其保持最新并修复错误。

@rsc ,在你拒绝之前,让我们听听 Alex 对你的 O_ALLOW_DELETE 提案的看法。

请试试

https://github.com/alexbrainman/goissue34681

第一的。 如果您由于某种原因不满意,我们可以讨论向 Go 存储库添加新功能。

谢谢你。

亚历克斯

我对这个回应真的很失望。

复制大量代码,这在很大程度上甚至不被理解(如
为什么这会做它正在做的事情)只是这样我们就可以添加一个选项
系统本身支持,只是 Go,很可能我认为不支持
即使是故意的,也没有提供将选项传递给的方法
syscall.Open 似乎是一个荒谬的情况。

为什么我必须重写 syscall.Open 来传递这个选项?

2019 年 10 月 5 日星期六 18:43,Alex Brainman [email protected]写道:

亚历克斯,如果允许外部访问,可以循环重命名尝试,...

如果您准备循环重命名,则无需更改任何内容
在 Go 回购代码中。 您的程序将像现在一样运行良好。

现在,唯一的解决方案是从字面上复制一堆代码
Go 的标准库,更改一行,然后永远维护那个 fork。

我认为将代码复制到单独的包中很好。 在调查时
moby/moby#39974 https://github.com/moby/moby/pull/39974我也一样
主意。 我认为那里不需要维护太多代码。 事实上我
就这样实现了

https://github.com/alexbrainman/goissue34681

随意复制或按原样使用。 也许,鉴于,有这么多的兴趣
这个功能,你实际上创建了可以共享的正确包
并被他人使用。 通过这种方式,您可以使其保持最新并修复错误。

@rsc https://github.com/rsc ,在你拒绝之前,让我们听听什么
Alex 想到了您的 O_ALLOW_DELETE 提案。

请试试

https://github.com/alexbrainman/goissue34681

第一的。 如果您由于某种原因不满意,我们可以讨论添加新的
Go repo 的功能。

谢谢你。

亚历克斯


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=AAGDCZXHULQEMHAPTO6ZUJTQNFGE7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAN7QIQ#issuecomment-5387039
或使线程静音
https://github.com/notifications/unsubscribe-auth/AAGDCZWYUNMCTAGO567AV73QNFGE7ANCNFSM4HNPNYIA
.

>

  • 布莱恩·高夫

我对这个回应真的很失望。

我很抱歉你有这种感觉。 但是你真的尝试过使用我的包裹吗?

亚历克斯

@rsc ,由于 Alex 也反对 os.OpenFile() 标志,我们应该什么都不做吗?

将此功能放在构建标志后面怎么样?

至于“Go 在 Windows 上运行了近 10 年”是否可以正常工作,如果您需要重命名打开的文件,则肯定没有。 (在过去的 7 年里,它在 Windows 8/10 笔记本电脑上也被破坏了。)

我已经在 moby/moby 中拥有自己的 os.OpenFile 和 syscall.Open 分支。

为什么我们在这里如此不屑一顾?

2019 年 10 月 8 日星期二 01:47 Alex Brainman [email protected]写道:

我对这个回应真的很失望。

我很抱歉你有这种感觉。 但是你真的尝试过使用我的包裹吗?

亚历克斯


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=AAGDCZRV72GY4IJQJVJWMYTQNRCIHA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEATNATA#issuecomment-539414604
或使线程静音
https://github.com/notifications/unsubscribe-auth/AAGDCZRNYSMIE6BX77XJVWDQNRCIHANCNFSM4HNPNYIA
.

>

  • 布莱恩·高夫

我建议拒绝这个具体的提议。 我们不会为依赖 os.OpenFile 现有行为的 Windows 用户介绍一堆 TOCTOU 安全漏洞。

这里更大的问题是,“我们如何向 Go 用户展示 Windows 的CreateFileNtCreateFile函数的大量有趣标志?” 我希望我们能够在不同的提案中解决这些问题,但肯定不会像这里所建议的那样在默认情况下将它们全部打开。

@zx2c4你读过整个帖子吗? 尽管反复尝试,我们无法识别出依赖于现有未记录行为的 Go 用户的单个案例。

https://github.com/golang/go/issues/32088#issuecomment -537590826 到现在已经一周了,仍然没有明确的共识,这意味着我们应该拒绝这个。

拒绝。

我已经在https://github.com/golang/go/issues/34681#issuecomment -565853605 中发布了一个包含优缺点的选项摘要。

此页面是否有帮助?
0 / 5 - 0 等级