Go: propuesta: os: Create/Open/OpenFile() establece FILE_SHARE_DELETE en Windows

Creado en 16 may. 2019  ·  194Comentarios  ·  Fuente: golang/go

En Linux y MacOS podemos escribir esto; en Windows falla con una "violación de uso compartido":

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()

Si desarrolla en Windows y lo implementa en Linux, etc., y su código se basa en este comportamiento _no documentado_ GOOS=windows de os.Rename() & .Remove(), está roto y quizás vulnerable. Tenga en cuenta que el paquete "os" tiene _quince menciones_ de otro comportamiento específico de Windows.

Para arreglar esto, syscall.Open() en https://golang.org/src/syscall/syscall_windows.go#L272
necesita sharemode |= FILE_SHARE_DELETE

Microsoft recomienda que esto sea el predeterminado: https://github.com/golang/go/issues/32088#issuecomment -502850674
Rust lo convirtió en predeterminado: https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html
Mingw-w64 lo convirtió en predeterminado hace siete años:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858
Erlang lo convirtió en predeterminado hace más de seis años: erlang/ otp@0e02f48
Python no pudo adoptarlo debido a las limitaciones del tiempo de ejecución de MSVC: https://bugs.python.org/issue15244

~Por lo tanto, syscall.Open() debería usar file_share_delete de forma predeterminada , y syscall debería proporcionar ambos:
a) un interruptor global para deshabilitarlo (para cualquier aplicación existente que dependa de su ausencia), y
b) un indicador para usar con os.OpenFile() para deshabilitarlo en un identificador de archivo específico.~

Actualización después de https://github.com/golang/go/issues/32088#issuecomment-532784947 por @rsc :
a) os.Create/Open/OpenFile() siempre debe
b) syscall.Open() en Windows debe aceptar un indicador que habilite file_share_delete, y
c) syscall en Windows debería exportar una constante para la nueva bandera.

Los documentos para os.Remove() también deben tener en cuenta que para reutilizar un nombre de archivo después de eliminar un archivo abierto en Windows, se debe hacer: os.Rename(path, unique_path); os.Remove(unique_path) .

Gana documentos 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

Si hay una razón para no hacer esto, debe documentarse en os.Remove() & .Rename().

cc @alexbrainman
@gopherbot agrega OS-Windows

FrozenDueToAge OS-Windows Proposal Proposal-FinalCommentPeriod release-blocker

Comentario más útil

Intentaré representar la perspectiva del equipo de Windows aquí... preferimos que Go establezca siempre FILE_SHARE_DELETE, sin ninguna opción. En general, para facilitar la migración hacia/desde Windows, preferiríamos un comportamiento consistente con Linux, y no veo por qué los usuarios o el software de Windows preferirían el comportamiento predeterminado !FILE_SHARE_DELETE lo suficiente como para que esto sea una excepción a la regla.

Una nota interesante, contrariamente al comentario de

En otras palabras, si ejecuta el programa de prueba de mattn y la secuencia de del, dir, etc. en la última versión de Windows, verá que el archivo desaparece del espacio de nombres inmediatamente después de eliminarlo, no después de que finalice el programa de prueba. Al igual que Linux.

Por lo tanto, incluso en el propio Windows, con su riesgo de compatibilidad de aplicaciones significativamente mayor, estamos realizando pequeños cambios para facilitar la migración de software que no es de Windows a Windows. Realmente animo a Go a hacer lo mismo.

Todos 194 comentarios

/cc @alexbrainman

syscall.Open() en https://golang.org/src/syscall/syscall_windows.go#L272
debería usar sharemode := ... | FILE_SHARE_DELETE

¿Por qué debería?

Alex

FILE_SHARE_DELETE habilita el ejemplo de código anterior, que funciona en MacOS y Linux pero falla en Windows. También es necesario para:

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 habilita el ejemplo de código anterior, que funciona en MacOS y Linux pero falla en Windows.

¿Por qué no ajustamos el código de MacOS y Linux en su lugar?

También es necesario para:

No entiendo lo que estás tratando de decir.

Alex

@networkimprov Debe llamar a Eliminar después de Cerrar () en Windows.

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 cuando realiza E/S de archivos confiables (como para bases de datos), es una práctica estándar crear un archivo con un nombre temporal, escribirlo, sincronizarlo y cambiarle el nombre.

Los archivos abiertos se pueden renombrar o eliminar de forma predeterminada en Unix. Parece un descuido que la bandera de Windows para esta capacidad no esté configurada. Dudo que convenzamos al equipo de Go de cambiar la forma en que funciona para Linux y MacOS :-)

@mattn, aplique la solución que describí y pruebe el código que publiqué.

Dudo que convenzamos al equipo de Go de cambiar la forma en que funciona para Linux y MacOS :-)

Estoy bien como están las cosas ahora.

Alex

¿Hay alguna razón para omitir esta capacidad común en Windows?

¿Puede proporcionar un interruptor en syscall_windows.go para que podamos seleccionar el comportamiento de Unix al inicio del programa?

Estoy de acuerdo en que agreguemos nuevas API o indicadores. Pero tengo objeciones para cambiar el comportamiento actual. Dado que FILE_SHARE_DELETE no es lo mismo que el comportamiento de 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;
}

Complete este código en Windows e intente ejecutarlo como test.exe . Mientras esta aplicación espera presionar una tecla, abra un nuevo cmd.exe, elimine "test.txt" como se muestra a continuación.

image

El archivo se puede eliminar pero permanecer allí mientras exista el proceso. Por lo tanto, este cambio no funcionará bien para su esperado.

Me doy cuenta de que la entrada del directorio no se elimina, pero los documentos dicen

Las llamadas posteriores a CreateFile para abrir el archivo fallan con ERROR_ACCESS_DENIED.

Así que no entiendo tu registro de pantalla.

De todos modos, un cambio como este estaría bien:

syscall.OpenFileShareDelete = true

Me gustaría mencionar que esto me ha picado antes en el trabajo.

Básicamente teníamos:

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

// lots of code

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

El código funcionó bien en nuestras plataformas CI basadas en Unix, pero explotó en Windows.

Suponiendo que no haya efectos secundarios extraños, sería bueno que todo funcionara .

IIRC, hemos realizado algunos ajustes en el comportamiento de GOOS=windows & plan9 en el pasado para que coincida más con la semántica de Unix. No me importaría hacer que este sea otro caso similar si la semántica es lo suficientemente cercana. Sin embargo, el comentario de

Sin embargo, no quiero ver ninguna opción global. Eso parece una pesadilla de depuración.

@bradfitz
Probablemente no, por favor refiérase a mi comentario aquí
https://groups.google.com/forum/#!topic/golang-dev/R79TJAzsBfM
o si lo prefiere puedo copiar el contenido aquí, ya que también hay una posible solución, aunque no la implementaría como el comportamiento predeterminado , ya que esto no sería consistente con el comportamiento de los programas de Windows, sino más bien una solución para crear una cruz similar -os experiencia.

@guybrand escribe en el hilo golang-dev:

mi programa está escribiendo un archivo llamado "my.data", y luego llama a otro programa y no espera a que finalice... este programa, digamos, carga este archivo, lo que demora 10 segundos (llamemos a este otro programa "uploader") .
...
Cuando mi programa llama a .Remove en Windows (FILE_SHARE_DELETE está activado):
...
uploader recibiría un error diciéndole que el archivo se eliminó (probablemente EOF).

Suponiendo que "uploader" abra el archivo antes de que "mi programa" llame a os.Remove(), creo que contradijo los documentos de Win API:

_DeleteFile marca un archivo para su eliminación al cerrarlo. Por lo tanto, la eliminación del archivo no se produce hasta que se cierra el último identificador del archivo. Las llamadas posteriores a CreateFile para abrir el archivo fallan con ERROR_ACCESS_DENIED._

Re "emparejar un _open_osfhandle() de CreateFile con un _fdopen", ¿puede señalar un código de ejemplo?

Si agregamos FILE_SHARE_DELETE, muchos programadores lo usarán por error para simular el comportamiento de Unix. En este caso, puede crear una función separada por restricciones de compilación para cada sistema operativo. Y debería devolver os.NewFile() , use FILE_SHARE_DELETE en Windows.

haga una función separada por restricciones de compilación para cada sistema operativo... devuelva os.NewFile(), use FILE_SHARE_DELETE en Windows

Para conservar la funcionalidad de os.OpenFile(), este plan parece requerir la reimplementación de todo:

os.OpenFile()
abrirArchivoNolog()
abrir documento()
abrirDir()
archivo nuevo()
arreglarLongPath()
llamada al sistema.Open()
hacerHeredarSa()

@redimprov

@guybrand escribe en el hilo golang-dev:

mi programa está escribiendo un archivo llamado "my.data", y luego llama a otro programa y no espera a que finalice... este programa, digamos, carga este archivo, lo que demora 10 segundos (llamemos a este otro programa "uploader") .
...
Cuando mi programa llama a .Remove en Windows (FILE_SHARE_DELETE está activado):
...
uploader recibiría un error diciéndole que el archivo se eliminó (probablemente EOF).

Suponiendo que "uploader" abra el archivo antes de que "mi programa" llame a os.Remove(), ¿no ha contradicho los documentos de Win API?

DeleteFile marca un archivo para su eliminación al cerrarlo. Por lo tanto, la eliminación del archivo no se produce hasta que se cierra el último identificador del archivo. Las llamadas posteriores a CreateFile para abrir el archivo fallan con ERROR_ACCESS_DENIED.

Re "emparejar un _open_osfhandle() de CreateFile con un _fdopen", ¿puede señalar un código de ejemplo?

No veo una contradicción con la API de Windows, señale lo que parece una contradicción.

En cuanto a una muestra de código, busqué un poco en Google y encontré esto:
http://blog.httrack.com/blog/2013/10/05/creating-deletable-and-movable-files-on-windows/

PERO
Tenga en cuenta: esto solo le dará un comportamiento similar al de nix internamente, cualquier otro programa externo que trabaje con él lo romperá (ya que el 99% usa la API estándar de Windows

@redimprov
Si quiere decir que "esto suena como" dir my.data "muestra el archivo, por lo que recuerdo, este era el comportamiento hasta Windows ... XP o 7 (no recuerdo cuál) y cambió desde (tal vez el explorador simplemente oculta de alguna manera) - Puedo volver a probar la próxima vez que inicie mi entorno de Windows (sucede cada pocas semanas...)

Si quiere decir "esto suena como que otros procesos con un identificador para el archivo aún podrían leer y escribir en el archivo", apostaría a almorzar en el segundo en que lea y escriba en dicho archivo, obtendrá un error de estilo "EOF". - pero nuevamente necesito confirmar que sea 100% positivo.

En cualquier caso, mi punto sería (en este punto, estoy tomando "lados" en el punto "nativo" frente a "agnóstico del sistema operativo"), incluso si implementa una solución de estilo _fdopen, obtendría inconsistencia entre su servicio y todos los demás ejecutables con los que colabora, por lo que el uso solo podría ser en interacción con otros ejecutables de go (o servicios raros que SÍ usan fd directamente).

En otras palabras, su aplicación sería el "niño más inteligente de la clase", que ningún otro niño puede entender.
Dos ejemplos de muchos que se me ocurren:
Entradas :
Mi aplicación descarga un archivo, el antivirus identifica su daño y lo elimina/pone en cuarentena (== una especie de cambio de nombre), si uso fd, mi aplicación aún podría hacer lo que quiera con él (que es coll, pero puede terminar golpeando un virus...)
salidas :
mi aplicación canaliza un archivo a otro servicio ("cargador") y lo elimina, incluso me molesté y escribí a un probador para ver que todo funcionaba bien, y la prueba pasa.
Ahora, en lugar de mi prueba de inicio, uso filezilla, transferimos, dropbx API, lo que sea
fallaría/no se comportaría de la misma manera que mi prueba funciona...

¿Todavía crees que cambiar esto al comportamiento predeterminado tiene sentido?

¿Hay alguna razón para omitir esta capacidad común en Windows?

Nunca he considerado esa pregunta. No se.

La forma en que funcionan los archivos de Go en Windows es consistente con todas las demás herramientas de desarrollo que he usado en mi vida. Me sorprendería que los archivos Go funcionaran como usted propone. También sospecho que rompería muchos programas existentes.

Alex

También sospecho que rompería muchos programas existentes.

@alexbrainman También sugerí un interruptor para habilitarlo, en lugar de cambiar el valor predeterminado.

@bradfitz hay syscall.SocketDisableIPv6, eso no es realmente diferente de una bandera para ajustar el comportamiento de syscall.Open().

Dado que syscall_windows*.go indica
func Abrir (cadena de ruta, modo int, perm uint32) (manejador fd, error err) {
....
modo compartido := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE)

y type_windows*.go tiene
ARCHIVO_COMPARTIR_ELIMINAR = 0x00000004

Todo está bastante listo, la única pregunta es cómo implementar la bandera.
No me gusta la opción global y no es explícita, y aunque un solo desarrollador puede recordar que ha establecido os.DeleteUponLastHandleClosed = 1, no es una buena práctica para desarrolladores a largo plazo o múltiples.
Otras opciones serían establecer un número reservado específico para las banderas, como en:
fd, err := os.OpenFile(ruta, os.O_RDWR|os.O_CREATE|os.DELETE_WHEN_FREED, 0600)
mientras que DELETE_WHEN_FREED puede incluso ser 0 para env. Aparte de las ventanas,

Otra opción sería usar el parámetro perm, que no es compatible con Windows, esto puede ser un poco incómodo.
fd, err := os.OpenFile(ruta, os.O_RDWR|os.O_CREATE, 777)
777 está reservado, por lo que necesitaríamos un 1777 o -777 para admitir ambos sistemas
hacer que sea legible DELETE_WHEN_FREED | 777

La última opción que se me ocurre es os.OpenDeletableFile(
¿Qué os.OpenFile en nix's y
turno
modo compartido := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
en las ventanas

todas las soluciones anteriores son simples de implementar, un poco más de tiempo para probar (sistemas operativos cruzados), solo necesita un votante ...

Una forma de admitir este comportamiento sin cambiar los valores predeterminados o ampliar la superficie de la API podría ser simplemente aceptar syscall.FILE_SHARE_DELETE en el parámetro flag de os.OpenFile en Windows y doblarlo en el cálculo sharemode valor. Dado que los indicadores syscall.O_* (y, por lo tanto, os.O_* ) en Windows usan valores inventados de todos modos , podrían diseñarse para evitar colisionar con cualquier indicador específico de Windows que se quisiera incluir. Afortunadamente, ya evitan colisiones con syscall.FILE_SHARE_DELETE .

En cualquier caso, me opondría rotundamente a cambiar el comportamiento predeterminado. Hacer que FILE_SHARE_DELETE el valor predeterminado lo pondría en la misma posición que los sistemas POSIX en términos de no poder garantizar un recorrido del sistema de archivos sin carreras. Tener este indicador como opcional es la razón por la que Windows no necesita el equivalente de openat , renameat , readlinkat , etc.

Realmente no he pensado en lo que deberíamos hacer aquí, pero quiero dejar una cosa clara:

¿Puede proporcionar un interruptor en syscall_windows.go para que podamos seleccionar el comportamiento de Unix al inicio del programa?

No estaremos haciendo esto. Eso haría imposible que un solo programa use diferentes paquetes que esperan un comportamiento diferente.

@havoc-io
Su sugerencia crearía programas propensos a errores ya que syscall.FILE_SHARE_DELETE difiere del comportamiento estándar de POSIX.
Tenga en cuenta que los autores de syscall_windows*.go conocían el indicador FILE_SHARE_DELETE (está ahí) y decidieron que usarlo no sería el comportamiento preferido.
¿Por qué?

tomemos el código de Liam, él
diferir fd.Cerrar()
borra/cambia de nombre
y luego lee/escribe

por lo que el orden de clasificación real es
abierto
marcar como borrado
lectura/escritura (y probablemente proceso cruzado/rutina cruzada de lectura y escritura también)
cerca

El comportamiento actual de Windows alerta al desarrollador "esto está mal", el desarrollador probablemente presionará la eliminación para que también se posponga
Si agrega FILE_SHARE_DELETE, Windows le permitirá "marcar como eliminado aunque el archivo esté abierto", PERO otro proceso/rutina que se ejecuta al mismo tiempo que intentará acceder al archivo entre la eliminación y el cierre (que es probablemente la tarea más larga en este código ) fallaría
Resultado: en lugar de un comportamiento consistente de "puedes hacer eso", obtendrías un "a veces", especialmente cuando hay muchos usuarios simultáneos, falla y no sé por qué.
Entonces, no está resolviendo el problema, solo maneja un caso de uso específico en el que el desarrollador "solo lo ejecuta una vez"
Mi voto es por el comportamiento consistente, por supuesto...

¿Cómo es eso diferente de Posix?
Una vez marcado como eliminado, el archivo ya no está en la tabla de archivos, solo existe como un fd, lo que permite que otra rutina/proceso cree un archivo con el mismo nombre.

Creo que deberíamos dejar de buscar una "misma solución" ya que hay diferencias que no resolveremos en el proceso (¿nombres de archivo sensibles a mayúsculas y minúsculas? :) dejaremos que Linux 5.2 haga eso...)

En conjunto, parece buscar una solución para un archivo temporal, si es así, Windows admite GetTempFileName, que puede considerarse como una solución estándar para un archivo que se "usa y luego se desecha".

Si, por otro lado, deseamos permitir que un desarrollador posponga las eliminaciones en Windows, eso es posible, vea mis sugerencias anteriores, pero el desarrollador debe asumir la responsabilidad de eso y comprender la conciencia; por lo tanto, debe ser una bandera con una buena convención de nombres.

El uso principal de esta función es cambiar el nombre de un archivo abierto después de crearlo:

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

No sé por qué no agregaría un indicador os.O_WRNDEL (cambiar el nombre de Windows/eliminarlo; un no-op en otras plataformas) y documentar las diferencias de ese modo con Unix; que un archivo eliminado permanece en su directorio, pero no se puede abrir, hasta que os.Close(). También mencione que mover el archivo a un directorio temporal antes de eliminarlo proporciona un comportamiento más similar a Unix.

@guybrand Creo que tal vez no estás entendiendo mi propuesta. No estoy defendiendo que FILE_SHARE_DELETE esté habilitado de forma predeterminada; de hecho, me opongo por las razones mencionadas en el segundo párrafo de mi comentario. Mi propuesta fue un mecanismo que permite a los usuarios optar por el comportamiento de FILE_SHARE_DELETE (por archivo) mientras siguen usando la infraestructura de archivos del paquete os . Esta propuesta proporciona un mecanismo para hacerlo sin extender la superficie API de ningún paquete.

No sé por qué no agregaría un indicador os.O_WRNDEL (cambiar el nombre de Windows/eliminarlo; no funciona en otras plataformas)

@networkimprov Eso es esencialmente lo que propongo, excepto que no tiene sentido definir una nueva bandera ya que ya existe una perfectamente válida: syscall.FILE_SHARE_DELETE . El único cambio de implementación sería observar esta bandera en syscall.Open en Windows y agregar comprobaciones para asegurarse de que ninguna adición futura a las banderas syscall.O_* / os.O_* colisione con esta bandera. La documentación de os.OpenFile podría actualizarse para reflejar la aceptación de este indicador en Windows.

@havoc-io perdón por el malentendido, esta fue mi comida para llevar de:
"... para simplemente aceptar syscall.FILE_SHARE_DELETE... en el modo compartido computado..."

Agregar coincidencias de os.O_WRNDEL con mi segunda sugerencia, además de no ser lo suficientemente explícito para el desarrollador en términos de "cuál sería el comportamiento", quizás os.WADRNDEL: Windows permite cambiar el nombre/eliminar en diferido).

@alexbrainman También sugerí un interruptor para habilitarlo, en lugar de cambiar el valor predeterminado

No estoy interesado. Gracias.

Alex

El cambio de nombre de

Lo siento, te pregunto de nuevo ¿cuál es tu sugerencia? eliminar el archivo de apertura? cambiar el nombre del archivo de apertura? ¿ambos?

Tres de nosotros ahora estamos sugiriendo una nueva bandera, por ejemplo

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)

Tres de nosotros ahora estamos sugiriendo una nueva bandera

Creo que una nueva bandera es inapropiada, especialmente una que no es fácilmente analizable por alguien que lee el código. Usar syscall.FILE_SHARE_DELETE sería mucho más explícito y claro, indicando inmediatamente al lector que algo específico de la plataforma está sucediendo.

Esto ya está permitido en las plataformas POSIX. Hay muchos indicadores open específicos de POSIX (e incluso específicos de la plataforma) que no están incorporados en el paquete os . Por ejemplo, no tendría sentido agregar algo como os._DEVTONLY para admitir el indicador O_EVTONLY Darwin, porque simplemente puede pasar el indicador del paquete syscall directamente a os.OpenFile , por ejemplo:

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

En este caso, el indicador específico de la plataforma se transmitirá a la llamada al sistema open subyacente.

Si el comportamiento de FILE_SHARE_DELETE es realmente algo que la gente necesita, entonces creo que la respuesta es simplemente hacer que os.OpenFile pueda enhebrar de manera similar las banderas en la llamada subyacente de CreateFileW en Windows. Al menos por FILE_SHARE_DELETE .

Puede encontrar un ejemplo de implementación aquí .

@redimprov

Tres de nosotros ahora estamos sugiriendo una nueva bandera, por ejemplo

¿Qué esperas de la bandera?

El cambio de nombre de

Correcto, pero ambos necesitan FILE_SHARE_DELETE para que se les permita mientras el archivo está abierto
Si se refiere a mi terminología sugerida, podemos
os.WARNDFDEL: Windows permite cambiar el nombre y la eliminación diferida es demasiado largo, yo mismo usaría un acrónimo en absoluto WINDOWS_ALLOW_OPENNED_FILE_RENAME_OR_DEFFERED_DELETE
es mejor en mi opinión.

@guybrand Realmente no veo el valor de agregar una nueva bandera específica de la plataforma con un nombre complejo y un comportamiento opaco al paquete os cuando una bandera perfectamente buena para esto ha existido durante décadas ( FILE_SHARE_DELETE ). Usar syscall.FILE_SHARE_DELETE como bandera para esto es mucho más claro y explícito. Como mencioné anteriormente , hay muchas banderas específicas de POSIX y específicas de la plataforma que no se agregan al paquete os pero que aún acepta.

syscall.FILE_SHARE_DELETE es solo para Windows; necesitamos algo que no sea operativo en otros lugares. ¿Tal vez debería aparecer en pkg os, con un prefijo WIN o WINDOWS?

@mattn por favor vea el parche de arriba de @havoc-io

necesitamos algo que no sea operativo en otros lugares.

@networkimprov No es necesario que la bandera exista en otro lugar. Su código de Windows se verá así:

import (
    "syscall"
    "os"
)

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

Sí, este código deberá estar bloqueado en la plataforma, pero probablemente deba hacerlo de todos modos, ya que el comportamiento con este indicador no coincidirá con el de los sistemas POSIX. O, si desea la comodidad de tener el mismo código en todas las plataformas, puede definir su propia constante (con un valor de syscall.FILE_SHARE_DELETE en Windows y 0 en sistemas que no son de Windows) y pasar a os.OpenFile . Esto es exactamente lo que harías si quisieras incorporar una bandera de O_* específica de la plataforma que no fuera parte del paquete os (por ejemplo, O_EVTONLY en Darwin o O_ASYNC en Linux).

Cuando escribo código, espero que sea multiplataforma, FILE_SHARE_DELETE no existe en syscall_* solo en Windows (por lo tanto, no se compilará en otros sistemas).

¿Podemos usar 0x4 const, como su:
ARCHIVO_COMPARTIR_ELIMINAR = 0x00000004

Aparte de ser feo,
netbsd_arm
O_NDELAY = 0x4
O_NOBLOQUE = 0x4

Entonces, usar 0x4 crearía el código de manera diferente en netbsd_arm.

Entonces, o agregamos FILE_SHARE_DELETE a todas las plataformas, las de Windows serán 0x4, otras serán 0x0 y, por lo tanto, las "descartaremos", o crearemos un indicador específico para este problema.

Y de todos modos debemos cambiar syscall_windows*
función abierta(
...
modo compartido := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | isDeleteOn )
donde isDeleteOn verificará si el modo incluye la nueva bandera

@guybrand

Cuando escribo código, espero que sea multiplataforma

Le advierto que el hecho de que la misma llamada os.OpenFile compile en varias plataformas no significa que su código sea multiplataforma. Hay una serie de consideraciones de seguridad en Windows que debe tener en cuenta al usar FILE_SHARE_DELETE , y la semántica de los ciclos de vida de los archivos es tan diferente entre los sistemas POSIX y Windows que es realmente difícil imaginar un escenario en el que su el código de manejo de archivos no se vería al menos un poco diferente entre las plataformas. El uso de FILE_SHARE_DELETE esencialmente lo coloca en una situación anterior a POSIX.1-2008, sin ninguna forma de garantizar un recorrido del sistema de archivos libre de carreras (y sin el beneficio del acceso a archivos posterior a unlink de POSIX para abrir archivos).

De todos modos, si desea una bandera que tenga un valor de syscall.FILE_SHARE_DELETE en Windows y 0 en otras plataformas, aún puede hacerlo fácilmente con su propia definición constante controlada por etiquetas de compilación, y luego su código principal que llama a os.OpenFile puede ser el mismo en todas las plataformas (usando esa bandera personalizada).

Hay muchas banderas específicas de la plataforma que existen en los paquetes syscall otras plataformas, pero no todas pueden ser expuestas por el paquete os y no pueden ser operadas en las plataformas donde no están No es compatible. Si desea un código y un comportamiento específicos de la plataforma, es necesario que busque el paquete syscall .

Lo único que la biblioteca estándar de Go debería (potencialmente) hacer aquí es admitir syscall.FILE_SHARE_DELETE en syscall.Open (y, en consecuencia, os.OpenFile ) en Windows.

Como mencioné anteriormente , si agregamos FILE_SHARE_DELETE, muchos programadores lo usarán por error para simular el comportamiento de Unix. Pero no es bueno. Por ejemplo, piensa en el caso de crear una aplicación para ver el archivo que existe. Se supone que el archivo no debe encontrarse desde otras aplicaciones si una aplicación elimina un archivo. Pero FILE_SHARE_DELETE solo marca para eliminarlo más tarde. Queda el archivo. En UNIX, funciona bien, pero Windows no. NO utilice FILE_SHARE_DELETE para simular el comportamiento de UNIX.

@mattn Estoy de acuerdo con sus preocupaciones: las personas pueden intentar usar la bandera como una conveniencia para hacer que el código de Windows "funcione igual" que los sistemas POSIX sin comprender las implicaciones sutiles. Sin embargo, creo que si tienen que ir al paquete syscall para acceder a ese indicador, es posible que se hayan tomado el tiempo para comprender lo que hace.

@mattn , para un comportamiento similar al de Unix, las aplicaciones necesitarían 2 líneas adicionales de código

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)

Documentar esto requiere una oración en los documentos para os.OpenFile().

¡Por favor, no me hagan distribuir un parche para pkg syscall que todos los que trabajan en mi código de Windows deben aplicar! Consulte también https://github.com/golang/go/issues/32088#issuecomment -493876119.

Creo que todos estamos de acuerdo en la mayoría de los hechos, solo expréselo de manera diferente, así que intentaré resumir y establecer el curso de acción:

  1. De acuerdo: Windows y POSIX difieren en el comportamiento anterior y no debemos intentar apuntar al mismo comportamiento.
  2. De acuerdo: la solicitud para permitir FILE_SHARE_DELETE como indicador es útil
  3. Sugerencia: cambiemos el ticket a "support FILE_SHARE_DELETE" para que suene como una solicitud de función en lugar de un error
  4. Mayormente de acuerdo: la función debe ser un indicador de desarrollador a nivel de programa, no un paquete base.

Elementos de acción:

  1. cambiar syscall_windows.go
    función abierta(
    ...
    sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | (modo & FILE_SHARE_DELETE) )
  2. El desarrollador que quisiera utilizar este comportamiento debería
    file, err := os.OpenFile(..., os.O_RDWR | syscall.FILE_SHARE_DELETE, ...)
    o si quisieran que su programa fuera multiplataforma, crearían una bandera interna de:

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

y luego usa:
file, err := os.OpenFile(..., os.O_RDWR | OPENFILEFLAG, ...)

Si esto se acuerda, la solución es mínima (una sola línea), con muy bajo riesgo

Vamos a votar sobre esto, y podemos arreglarlo rápidamente.

De acuerdo: la solicitud para permitir FILE_SHARE_DELETE como indicador es útil

Todavía no estoy de acuerdo con esto porque no entiendo cuál es el propósito de este problema.

Propósito: permitir a los desarrolladores cambiar el nombre o eliminar archivos abiertos.

Lo que falta: syscall_windows.go actualmente codificado de forma rígida :
func Open(path string, mode int, perm uint32) (fd Handle, err error) {
...
sharemode := uint32(**FILE_SHARE_READ | FILE_SHARE_WRITE**)

Cambio propuesto:
Cuando el desarrollador pasa un parámetro de modo con bit a bit 4 (FILE_SHARE_DELETE) activado, el modo compartido cambiará en consecuencia por
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | **(mode & FILE_SHARE_DELETE)** )

Todavía me pregunto por qué no llama a Close() antes de Rename() o Remove(). Puede leer el código de la biblioteca estándar de Go. Los archivos se cierran con Close() antes de Rename() o Remove(). ¿Por qué debe llamar a Close() después de Renamoe/Rename?

No estoy seguro de cuál es el caso comercial que está considerando

pero parece que @networkimprov quiere usar Rename(), por lo que probablemente sea un caso de uso diferente.

Como se menciona en https://github.com/golang/go/issues/32088#issuecomment -494305074, es posible que no necesitemos cerrar un archivo renombrado hasta que salga del programa.

@mattn , por favor, no insista en que nunca deberíamos usar una característica del sistema operativo que los desarrolladores de C y C++ puedan usar.

Quiero asegurarme (incluso si mi aplicación falla) de que este archivo ya no existirá después de que termine

@guybrand No se garantiza que una declaración de defer se ejecute en cada tipo de bloqueo, por lo que si intenta garantizar la eliminación de archivos, aplazar una operación de os.Remove no es una forma confiable de hazlo Si desea un archivo que se elimine automáticamente, un archivo temporal que el sistema elimine es una mejor opción. Si desea una ubicación de archivo compartida por dos programas, debe estar coordinada por bloqueos (por ejemplo, fcntl bloqueos en POSIX y LockFileEx / UnlockFileEx en Windows), no por existencia de archivo .

Como se menciona en #32088 (comentario), es posible que no necesitemos cerrar un archivo renombrado hasta que salga del programa.
@mattn , por favor, no insista en que nunca deberíamos usar una característica del sistema operativo que los desarrolladores de C y C++ puedan usar.

@networkimprov Una HANDLE , ¿no puede hacerlo ya? El único propósito de FILE_SHARE_DELETE sería permitir que otros procesos cambien el nombre del archivo mientras lo mantiene abierto. En cualquier caso, puede usar FILE_SHARE_DELETE ahora, solo necesita invocar manualmente CreateFileW y pasar el resultado a os.NewFile . Puede encontrar un ejemplo de cómo se ve esto aquí . El HANDLE resultante se puede pasar a os.NewFile .

@mattn Estoy empezando a estar de acuerdo contigo en que esta bandera será más una pistola que una herramienta útil. Para que quede claro, en realidad no quiero esta bandera, y los únicos argumentos que puedo ver para su existencia son la integridad y la paridad de control entre POSIX y Windows. Por otro lado, como mencioné anteriormente, también es relativamente fácil invocar manualmente CreateFileW (con este indicador especificado) y luego transferir el resultado a os.NewFile , por lo que tal vez no sea necesario añadir soporte para ello.

@havoc-io no hay os.File.Rename(). Esa sería una buena solución, pero es un trabajo relativamente pesado.

Re CreateFileW + os.NewFile(), consulte https://github.com/golang/go/issues/32088#issuecomment -493876119.

@havoc-io

Quiero asegurarme (incluso si mi aplicación falla) de que este archivo ya no existirá después de que termine
@guybrand No se garantiza que una declaración
Esto fue exactamente lo que quise decir, cuando dije "incluso si mi aplicación falla".

los únicos argumentos que puedo ver para su existencia son la integridad y la paridad de control entre POSIX y Windows
No estoy de acuerdo, esto traería "integridad y paridad de control entre POSIX y Windows": en mi opinión, solo confundiría al desarrollador si pensara que el comportamiento es el mismo cuando no lo es, di algunos ejemplos arriba.

fácil de invocar manualmente CreateFileW
Eso es realmente un ajuste, fácil o no, y en otras palabras, decir: "no queremos admitir esto en marcha", que por cierto es una buena decisión en mi opinión, pero el solicitante es @networkimprov, no yo.

Para su información, esta es la razón por la que dejamos de usar FILE_SHARE_DELETE.

CL: https://codereview.appspot.com/8203043/

Confirmar en erlang/otp: https://github.com/erlang/otp/commit/0e02f488971b32ff9ab88a3f0cb144fe5db161b2

Pitón: https://bugs.python.org/issue15244

@havoc-io no hay os.File.Rename(). Esa sería una buena solución, pero es un trabajo relativamente pesado.
Re CreateFileW + os.NewFile(), vea #32088 (comentario).

@networkimprov ¿No puedes simplemente usar os.Rename(file.Name(), <target>) ? Esa es la belleza de que Go no tenga FILE_SHARE_DELETE activado de manera predeterminada: sabe que file.Name() aún apuntará a algo válido ya que tiene el archivo HANDLE (algo que puede t garantía en POSIX, incluso con renameat ). Con respecto a CreateFileW + os.NewFile , la mayor parte de la pila descrita en su comentario se volvería irrelevante, y makeInheritSa es casi seguro algo que no desea (solo está ahí para simular POSIX mal comportamiento de herencia del descriptor de archivo predeterminado; no es el predeterminado). Lo único que se perdería sería la función de ruta interna fixLongPath , que probablemente no sea necesaria para los casos de uso que ha descrito.

@guybrand Solo para aclarar lo que quiero decir...

Completitud: completitud de la superficie de la API (es decir, la capacidad de usar FILE_SHARE_DELETE si desea usarlo por alguna razón)
paridad de control: la capacidad de especificar banderas a CreateFileW través del parámetro os.OpenFile de flag . Por ejemplo, puedo enrutar syscall.O_NOFOLLOW a os.OpenFile en POSIX, pero no puedo enrutar syscall.FILE_FLAG_OPEN_REPARSE_POINT a os.OpenFile en Windows. Aunque, de nuevo, creo que la paridad total de control aquí es una quimera, ya que el paquete os está modelado en torno a las API POSIX. Estoy bastante feliz de llegar a syscall.CreateFileW cuando es necesario.

@mattn gracias por los enlaces. El Go CL fue parchear el syscall.Open() _default_. La discusión no da ninguna razón general para no permitir una _bandera_ para Open(). Señala que un paquete de terceros puede devolver un archivo os sin la bandera, pero eso solo es un problema si la persona que llama intenta cambiar el nombre/eliminar ese archivo, un caso raro.

El hilo de Python señala que debe cambiar el nombre de un archivo abierto antes de eliminarlo, como escribí anteriormente.

Erlang ha tenido file_share_delete como _predeterminado_ durante más de SEIS años .

Reflexionando sobre la experiencia de Erlang y el hilo de Python vinculado anteriormente, es evidente que:

  1. File_share_delete por defecto funciona bien en un entorno multiplataforma; la única advertencia es que el código de usuario debe cambiar el nombre de un archivo a un nombre único inmediatamente antes de eliminarlo (un cambio trivial) si se puede reutilizar el nombre de archivo original.
  2. La mayoría de los programas de Windows no pueden usar file_share_delete simplemente porque el tiempo de ejecución de MSVC solo lo permite en combinación con O_TEMPORARY.

Por lo tanto, concluiría que syscall.Open() debería usar file_share_delete de forma predeterminada , y syscall debería proporcionar un indicador global para deshabilitarlo cuando sea necesario (por ejemplo, depuración).

Si aparecen problemas con ese enfoque durante el ciclo 1.14, se puede aplicar el método de marca explícita en su lugar.

Por lo tanto, concluiría que syscall.Open() debería usar file_share_delete de forma predeterminada, y syscall debería proporcionar un indicador global para deshabilitarlo cuando sea necesario (por ejemplo, depuración).

@networkimprov Además de romper una gran cantidad de programas existentes de formas sutiles y no tan sutiles, el hecho es que introducir esta bandera ahora sería un problema de seguridad para los programas que dependen de la ausencia de FILE_SHARE_DELETE . En particular, abriría una serie de vulnerabilidades desde el momento de la verificación hasta el momento del uso para los programas que dependen de la inmutabilidad de los archivos y directorios que mantienen abiertos.

Editar: Para obtener más información sobre por qué cambiar el valor predeterminado es una mala idea y es casi seguro que romperá los programas existentes, consulte la Ley de Hyrum .

El caso más común de Go on Windows es el desarrollo, para la implementación en Linux u otro Unix.

Si su código depende de algún comportamiento de GOOS=windows, está dañado y posiblemente sea vulnerable en Linux. Así que ya tenemos un posible problema de seguridad.

os.Rename() y .Remove() no documentan las diferencias en Windows, por lo que nadie adivinaría que existen. Documentarlos ahora no arreglará el código existente. Arreglar la incompatibilidad y publicar una publicación de blog ayudaría mucho más.

Tenga en cuenta que Erlang había existido por un tiempo antes de que adoptara file_share_delete como predeterminado.

El caso más común de Go on Windows es el desarrollo, para la implementación en Linux u otro Unix.

Si su código depende de algún comportamiento de GOOS=windows, está dañado y posiblemente sea vulnerable en Linux. Así que ya tenemos un posible problema de seguridad.

@redimprov

Según esa lógica, la infraestructura de apertura de archivos en Windows también debe modificarse para hacer que los descriptores de archivos sean heredables de forma predeterminada en la creación del proceso, para que coincidan con el comportamiento predeterminado de POSIX. Se puede culpar a los programas existentes por confiar en el comportamiento predeterminado anterior de la biblioteca estándar/en tiempo de ejecución, y se puede incluir una plantilla CVE en las notas de la versión para que los usuarios las registren para sus programas.

Pero, por supuesto, eso no es lo que se hace, y en su lugar, el tiempo de ejecución y la biblioteca estándar de Go hacen todo lo posible para evitar el comportamiento predeterminado y mal diseñado de POSIX, tratando de reflejar lo que hace Windows.

La situación es la misma con el acceso a archivos compartidos. Podría decirse que Windows acertó en el diseño y POSIX.1-2008 tuvo que agregar una serie de funciones para sortear sus limitaciones de diseño.

Además, como @mattn ha mencionado varias veces anteriormente, incluir FILE_SHARE_DELETE al abrir archivos NO hace que Windows se comporte como POSIX; aún habrá diferencias de comportamiento significativas que serán mucho más evidentes con esta marca que con os.Remove y os.Rename comportamiento.

Yo diría que si su código se basa universalmente en el comportamiento de cualquier plataforma, está roto y posiblemente sea vulnerable en otras plataformas (y eso incluye confiar en el comportamiento de POSIX en Windows). Si no se toma el tiempo para comprender y abordar las variaciones de la plataforma en su código, entonces no está realizando un desarrollo multiplataforma.

En cualquier caso, @ianlancetaylor ya vetó la idea de usar una bandera global para controlar la activación de esto, por lo que dudo seriamente que obtenga una para desactivarla después de cambiar la configuración predeterminada.

En mi opinión, la única opción que no rompe (y no compromete la seguridad) aquí es agregar soporte para syscall.FILE_SHARE_DELETE en el argumento syscall.Open de flag , o simplemente forzar usuarios que desean que la funcionalidad FILE_SHARE_DELETE use CreateFileW + os.NewFile . Sí, requiere un poco de trabajo por parte de los desarrolladores que desean este comportamiento, pero Win32 y POSIX son bestias fundamentalmente diferentes y es sorprendente que el tiempo de ejecución y la biblioteca estándar hagan un trabajo tan bueno como el de allanar sus diferencias. Hay muchas más diferencias significativas entre estas plataformas que se manifiestan en la biblioteca estándar, por ejemplo, modelos de permisos completamente diferentes, el hecho de que os.File.Chmod no funciona en Windows, os.Chown no funciona en Windows, el hecho de que el sondeador interno admita diferentes tipos de archivos en diferentes plataformas, etc. El tiempo de ejecución y la biblioteca estándar de Go hacen lo mejor que pueden (y lo hacen bien), pero no son una panacea para los problemas de desarrollo multiplataforma. En algún momento, los desarrolladores tienen que manejar estas diferencias por su cuenta. Agregar un cambio de última hora para modificar (observe que no dije correcto ) un caso de borde de comportamiento oscuro no tiene ningún sentido para mí.

Quería compartir un caso de uso más para esto.

Actualmente, cuando la lógica de rotación del registro de Docker funciona de manera que el motor de Docker cambiará el nombre del registro activo al nombre .1, cree uno nuevo con el nombre anterior.
Cualquier cliente que esté siguiendo activamente el registro con el comando docker logs --follow <container>
notará que de los eventos del sistema de archivos:
https://github.com/moby/moby/blob/916eabd459fe707b5c4a86377d12e2ad1871b353/daemon/logger/loggerutils/logfile.go#L552 -L593

También existe esta lógica que hace que Windows se dé cuenta de esos eventos inmediatamente:
https://github.com/moby/moby/blob/916eabd459fe707b5c4a86377d12e2ad1871b353/daemon/logger/loggerutils/logfile.go#L670 -L676

Todo esto funciona bien en Linux, pero en Windows, la rotación del registro no produce el error The process cannot access the file because it is being used by another process. si hay algún cliente siguiendo el registro (problema: https://github.com/moby/moby/issues/39274)

Como es posible tener un número n de esos clientes que están siguiendo el registro, sería muy complicado recopilar primero todos los identificadores de archivos abiertos y cerrarlos antes de que el archivo gire, por lo que realmente me gustaría ver bien tener soporte para syscall.FILE_SHARE_DELETE bandera.

¡Hola! Información general rápida: trabajo en la infraestructura de Chrome CI. Estamos transfiriendo nuestro código de trabajador de bajo nivel a Go. Ejecutamos cientos de miles de pruebas de invocación en Windows por día.

He leído todos los comentarios y espero que el siguiente sea un resumen claro y correcto de la opinión de cada individuo.

Nuestro caso de uso es crear ejecutables de Go en Windows para que se ejecuten en Windows, a diferencia de la sorprendente caracterización de @networkimprov . Ignoraré este caso de uso de "asumir el comportamiento de POSIX en Windows" en este comentario, ya que es simplemente una suposición incorrecta y arrastré a @mattn para resaltar las diferencias, que en mi humilde opinión es un punto discutible, más abajo sobre eso.

Como parte de nuestra migración, necesitamos FILE_SHARE_DELETE, en parte debido a la rotación del archivo de registro, similar a #39274. Sucede que también es un problema para el número 25965.

Estoy de acuerdo con @ianlancetaylor, cambiar todo el comportamiento de la cadena de herramientas, como se propone en https://codereview.appspot.com/8203043/, no es una buena idea. Un problema inmediato que viene a la mente de esta propuesta cuando se utiliza un identificador de archivo como archivo de bloqueo. Usamos este comportamiento. Y lo que dijo @havoc-io. Así que ignoremos esto también.

Para reiterar, voy a ignorar estas propuestas en el resto de este comentario:

  • Intentar comportarse exactamente como POSIX, eso no va a funcionar
  • Cambie el comportamiento global como exclusión voluntaria (¡o no exclusión voluntaria!), que rompe a los usuarios

Esto nos deja con un indicador por llamada con un nombre que es claramente específico de Windows. Esto es exactamente lo que propone @guybrand .

Con todo respeto, no entiendo la objeción de @mattn . Lo que propone @guybrand es razonable. Sí, Windows se comporta de manera diferente con respecto al uso compartido de archivos. Es como los derechos de eliminación de archivos, es muy diferente en Windows . Ya diverge en muchos sentidos. Tener una objeción por una bandera específica de Windows en la postura de que "Los desarrolladores podrían malinterpretar su comportamiento" no es un hecho, es una opinión. Asumes que los desarrolladores no saben lo que están haciendo o no leerán la documentación en MSDN. Esta es una objeción justa para el caso general 😎, pero no fuerte sobre la base de bloquear un caso de uso legítimo como bandera opcional.

Irónicamente, esto significa que hasta que se resuelva este problema, para arreglar el #25965 tendré que cambiar 'go run' para llamar a CreateFileW directamente 🙃 porque también necesitamos FILE_FLAG_DELETE_ON_CLOSE .

¡Gracias!

Solo quiero saber cuántos casos existen para saber si debemos implementar una nueva función (golang.org/x/sys?) Para abrir/crear un archivo con nuevos atributos, o cambiar el comportamiento original. Actualmente, por lo que puedo saber de los comentarios anteriores:

  1. creando un archivo eliminable (renombrable) para el registro.
  2. creando un archivo para un archivo temporal que se eliminará al cerrar.

Para 1, se puede proporcionar desde una nueva función para crear/abrir un archivo con los atributos. Y podemos configurar el archivo usando logger.SetOutput(file).

Para 2, también puede crear/abrir con una nueva función.

Por cierto, supongo que #25965 no está relacionado con este tema. Probablemente, es una limitación de Windows.

(En cualquier caso, no podemos eliminar el directorio donde existe el archivo abierto con este atributo)

@mattn ¿Puede explicar el intercambio entre:

  • agregando una función completamente nueva en golang.org/x/sys
  • apoyando una nueva bandera (¡ya definida!) para OpenFile(). [1]

No entiendo por qué deberíamos favorecer lo primero en lugar de lo segundo.

[1] Me acabo de dar cuenta de que asumí aquí cambiar el comportamiento de os.OpenFile() pero el problema llama a Open().

En primer lugar, el paquete syscall ya está bloqueado. Así que estoy buscando una manera de solucionar esto sin cambiar el paquete syscall.

Por cierto, supongo que #25965 no está relacionado con este tema. Probablemente, es una limitación de Windows.

(En cualquier caso, no podemos eliminar el directorio donde existe el archivo abierto con este atributo)

@mattn eso no es completamente cierto. FILE_SHARE_DELETE indicador

# 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

En primer lugar, el paquete syscall ya está bloqueado. Así que estoy buscando una manera de solucionar esto sin cambiar el paquete syscall.

@mattn Si solo se trata de un problema de congelación de la API, entonces, como mencioné / demostré anteriormente, syscall.Open podría ajustarse para comprender syscall.FILE_SHARE_DELETE , resolviendo el problema sin cambiar la API.

Si se trata de una congelación de la implementación, entonces el cambio podría realizarse en la función golang.org/x/sys/windows de Open y luego venderse en el paquete internal/syscall . El código file_windows.go ya está usando funciones de internal/syscall . La única inconsistencia entonces sería que syscall.Open no entendería este indicador, pero la mayoría de las personas que buscan acceder a la funcionalidad API de Windows de bajo nivel están usando golang.org/x/sys/windows todos modos, y probablemente usando CreateFile lugar de Open .

La propuesta que inició el "bloqueo" del paquete syscall establece:
_El repositorio central no dependerá de los paquetes go.sys_

Entonces, cambiar x/sys no ayudaría a las personas que llaman a os.OpenFile(). Pero internal/syscall podría agregar Open(), y os.OpenFile() lo llamaría.

@maruel , el proyecto Docker, y presumiblemente muchos otros, asumieron el comportamiento de Unix para os.Rename() & .Remove() de archivos abiertos porque el paquete "os" no menciona el comportamiento específico de Windows para ellos, pero tiene __quince menciones__ de otro comportamiento de Windows.

El repositorio central no dependerá de los paquetes go.sys
Pero internal/syscall podría agregar Open(), y os.OpenFile() lo llamaría.

@networkimprov Bien, tenía la impresión equivocada de que internal/syscall era un subconjunto de golang.org/x/sys , pero de todos modos creo que esa es la estrategia correcta (agregar internal/syscall/windows.Open ). Por supuesto, esto sería bajo el supuesto de que el comportamiento de syscall.Open es inmutable debido a la congelación. Idealmente, podría modificarse directamente, potencialmente con los cambios correspondientes en golang.org/x/sys/windows.Open .

Aunque el paquete syscall está congelado, podemos cambiarlo para corregir errores o abordar deficiencias graves. En particular, podríamos cambiarlo para admitir mejor el paquete os.

Pero si las personas llaman a syscall.Open directamente, entonces deberían usar el paquete x/sys/windows, no el paquete syscall.

Pero si las personas llaman a syscall.Open directamente, entonces deberían usar el paquete x/sys/windows, no el paquete syscall.

No creo que nadie esté buscando llamar a syscall.Open directamente; es solo el objetivo de la discusión porque subyace en os.OpenFile (por lo que agregar soporte para FILE_SHARE_DELETE en syscall.Open agrega soporte para usar la bandera con os.OpenFile ).

Gracias. Está bien cambiar el paquete syscall para admitir cambios en el paquete os.

Gracias @ianlancetaylor y @

  • Modifique syscall.Open() con la intención explícita de permitir que os.OpenFile() acepte FILE_SHARE_DELETE (y eventualmente FILE_FLAG_DELETE_ON_CLOSE como un posible seguimiento)
  • Actualice la documentación de os.OpenFile() para describir la nueva marca solo para Windows.
  • Actualice la documentación de os.OpenFile(), os.Rename() y os.Remove() para aclarar la diferencia de comportamiento entre POSIX y Windows.

El tercer punto es abordar la preocupación de @networkimprov . Entiendo el desafío allí, y aunque no debería ser responsabilidad del idioma describir cómo funciona el sistema operativo, estoy empezando a estar de acuerdo con @mattn en que estos casos son lo suficientemente sutiles como para justificar más documentación. Creo que el documento para Renombrar y Eliminar se puede mejorar de inmediato, sin necesidad de esperar ningún cambio de comportamiento.

Estaré dispuesto a contribuir en esto si hay aprobación.

Esto nos deja con un indicador por llamada con un nombre que es claramente específico de Windows.

¿Propones nueva bandera os.Open? Luego, la bandera debe ser compatible con todos los sistemas operativos, ese es un punto completo del paquete os, ser independiente del sistema operativo. Entonces, ¿cuál sería la semántica de esa bandera? Y necesitaría algunas pruebas nuevas que verifiquen que la bandera funcione según lo documentado.

No veo por qué no puede escribir el código que necesita usando el paquete syscall. No creo que su código coexista bien con otros programas de Windows. Y tal vez esté bien en circunstancias especiales. Pero no quiero que las personas usen esta funcionalidad a la ligera, por lo que no debería ser parte de la biblioteca estándar.

Alex

@alexbrainman
por favor mira esto

@alexbrainman
por favor mira esto

Miré. Todavía no veo cómo funcionarán todos los cambios propuestos en otros sistemas operativos. ¿Qué pruebas tendrá que implementar para implementar el cambio propuesto?

Alex

citando esto

mientras que DELETE_WHEN_FREED puede incluso ser 0 para env. aparte de las ventanas

Entonces, nada que probar en otros sistemas operativos, la llamada para:
fd, err := os.OpenFile(ruta, os.O_RDWR|os.O_CREATE|os.DELETE_WHEN_FREED, 0600)
en windows estaría traduciendo la const a 4
fd, err := os.OpenFile(ruta, os.O_RDWR|os.O_CREATE| 4 , 0600)
Mientras otros ( 0 )
fd, err := os.OpenFile(ruta, os.O_RDWR|os.O_CREATE| 0 , 0600)

nada que probar en otros sistemas operativos

No veo cómo podríamos agregar una nueva constante o variable del paquete os que solo se puede usar en Windows.

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.

Alex

Alex, la respuesta a tu objeción se indicó por primera vez aquí https://github.com/golang/go/issues/32088#issuecomment -494157514

Una prueba es trivial

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

Estamos en el punto en que alguien podría enviar un parche; Sospecho que @ianlancetaylor lo aceptaría.

Investigué la configuración de FILE_SHARE_DELETE como parte de #32188, pero descubrí que configurarlo no reducía empíricamente la tasa de errores de las llamadas os.Rename y io.ReadFile ; de todos modos, todavía se necesitaba un bucle de reintento.

Me interesaría mucho ver una prueba que demuestre una diferencia observable significativa al establecer esta bandera, porque yo mismo no entiendo realmente sus implicaciones.

Experimenté FILE_SHARE_DELETE hace unos años, y se comportó
diferentemente.
Probablemente pueda relanzar mi entorno de Windows. y búscalo, si te sirve,
pero sería ~Go 1.3 más o menos.

El lunes 10 de junio de 2019 a las 17:44, Bryan C. Mills [email protected]
escribió:

Investigué la configuración de FILE_SHARE_DELETE como parte de #32188
https://github.com/golang/go/issues/32188 , pero encontré que configurarlo
no redujo empíricamente la tasa de errores de os.Rename y
Llamadas io.ReadFile; de todos modos, todavía se necesitaba un bucle de reintento.

Estaría muy interesado en ver una prueba que demuestre una significativa
diferencia observable de establecer esta bandera, porque realmente no
entender sus implicaciones yo mismo.


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4VVIVJ2IEWQPWCWBZTPZZSETA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXKCG4Y#issuecomment-5004419
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/ABNEY4STTW7U526HPO6TUHDPZZSETANNCNFSM4HNPNYIA
.

@bcmills , leyendo #32188 Deduzco que estaba viendo errores de os.Rename() en un archivo que _no estaba abierto_ durante el cambio de nombre. Me imagino que uno podría dejar atrás el diario NTFS, que creo que está escrito para todas las operaciones de cambio de directorio, y por lo tanto inducir un error; pero no tengo idea de qué error sería.

La solicitud aquí para habilitar FILE_SHARE_DELETE en syscall.Open() es para permitir que os.Rename() funcione en archivos abiertos. He probado esa bandera con 1.12 en Win7, funciona; y falla sin ella.

No he probado específicamente os.Rename() en Windows "bajo carga", pero no he visto ningún error inesperado de os.Rename() en archivos abiertos desde que habilité fsd. Lo informaré si lo hago.

Deduzco que estaba viendo errores de os.Rename() en un archivo que no estaba abierto durante el cambio de nombre.

Si.

No tengo idea de qué error sería.

Empíricamente, los errores que estoy viendo (de MoveFileEx , ReplaceFile , y/o son CreateFile ) son ERROR_ACCESS_DENIED , ERROR_SHARING_VIOLATION , y ERROR_FILE_NOT_FOUND .

La solicitud aquí para habilitar FILE_SHARE_DELETE en syscall.Open() es para permitir que os.Rename() funcione en archivos abiertos. He probado esa bandera con 1.12 en Win7, funciona; y falla sin ella.

Correcto; los problemas relacionados que estoy tratando de resolver son permitir que las llamadas simultáneas a os.Rename tengan éxito y permitir que las llamadas a io.ReadFile concurrentes con os.Rename tengan éxito. Hacer que un solo os.Rename funcione con identificadores abiertos puede ser un paso en la dirección correcta, pero no creo que sea suficiente para el tipo de casos de uso para los que empleamos el POSIX rename .

Me interesaría mucho ver una prueba que demuestre una diferencia observable significativa al establecer esta bandera, porque yo mismo no entiendo realmente sus implicaciones.

Ejemplo de @bcmills PowerShell mientras escribo mucho más rápido que 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
}

Después de que termine, habrá miles de archivos test.txt.??? debajo de C:\folder1 pero cuando cierre PowerShell, esos archivos se eliminarán.

Entonces, ¿qué indicador de FILE_SHARE_DELETE realmente le permite cambiar el nombre/mover/eliminar archivos abiertos, pero aún debe asegurarse de que los nombres de los archivos de destino sean únicos porque existirán en el disco siempre que los identificadores estén abiertos?

"Emplear el cambio de nombre de POSIX" es imposible en Windows, y tal vez esa sea la razón de los errores que está recibiendo.

POSIX:

crear archivo "nombre de archivo"
leer/escribir desde "nombre de archivo"
eliminar "nombre de archivo"

crear archivo "nombre de archivo"
...
eliminar "nombre de archivo"

funcionaría, ya que cada archivo de creación tendría un diff fd.

Ventanas:
crear archivo "nombre de archivo"
leer/escribir desde "nombre de archivo"
eliminar "nombre de archivo"

crear archivo "nombre de archivo" - boom, violación de uso compartido

Debería ser muy fácil de reconstruir...

¿Qué puede hacer FILE_SHARE_DELETE entonces?
permitir la eliminación diferida mientras el archivo está abierto.

Eso es.

El lunes 10 de junio de 2019 a las 19:32, Olli Janatuinen [email protected]
escribió:

Estaría muy interesado en ver una prueba que demuestre una significativa
diferencia observable de establecer esta bandera, porque realmente no
entender sus implicaciones yo mismo.

@bcmills https://github.com/bcmills Ejemplo de PowerShell mientras escribo eso
mucho más rápido que ir

Nuevo elemento -Tipo de directorio -Ruta C:carpeta1for($i=0; $i -le 1000; $i++)
{
$temp = [System.IO.File]::Open("C:folder1test.txt", "Crear", "ReadWrite", "Delete")
Conjunto-Variable -Nombre "archivo$i" -Valor $temp -Fuerza
$TempName = "C:carpeta1prueba.txt". + (Nuevo-Guid).Guid
Renombrar elemento -Ruta C:carpeta1prueba.txt -Nombre nuevo $TempName
Remove-Item -Path $TempName -Force
}

Después de que termine habrá mil test.txt.??? archivos bajo
C: carpeta 1, pero cuando cierre PowerShell, esos archivos se eliminarán.

Entonces, ¿qué bandera FILE_SHARE_DELETE realmente te permite
cambiar el nombre/mover/eliminar archivos abiertos, pero aún necesita asegurarse de que el destino
los nombres de los archivos son únicos porque existirán en el disco siempre que los identificadores
estan abiertos.


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4WSTWIUVNVHLYMBFY3PZZ62DA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXKMMDY#issuecomment-50048359999
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/ABNEY4VS5XF3EXB6QPBWVQ3PZZ62DANCNFSM4HNPNYIA
.

permitir que las llamadas a io.ReadFile concurran con os.Rename para tener éxito

@bcmills Creo que uno siempre debe trabajar con el conjunto de indicadores fsd, suponiendo que no se esté ejecutando simultáneamente con otros subprocesos que intentan la misma secuencia en archivos no relacionados. Y normalmente debería fallar sin la bandera.

Ese aspecto del script de prueba encontró correctamente un error en Windows syscall.Open(). Pero nadie más que comente en este hilo quiere que se solucione :-( así que sí, parchee el script.

TLDR:
cambio seguro menor a syscal_windows.go y funciona como se esperaba.

explicado:
Entonces, esto es lo que he hecho, funciona como se esperaba y sin riesgo (OMI)

  1. modificar 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))

la adición es: "| (modo & FILE_SHARE_DELETE)"
si el desarrollador no envía el modo y el conjunto 4 (¿por qué debería hacerlo? Esto nunca funcionó antes...) - nada ha cambiado, por lo que un código como

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

se comportará EXACTAMENTE como lo hace antes del cambio, solo los desarrolladores que

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

habrá "sentido" el cambio

  1. ejecutó el código
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())
    }
}

Y funcionó

  1. ejecutó el código sin el |4 y falló
  2. ejecutó el código con una pequeña adición:
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()
    }
}

y por supuesto falló en el segundoF
Pero ese es el comportamiento esperado en Windows, no es necesario "cargar la prueba", no hay una opción de concurrencia en diferido. Quitar, así que creo que podemos agregar con seguridad esta media línea a syscal_windows, sin comprometer ningún código existente y permitiendo solo a los desarrolladores que Realmente quiero usar este modo para poder diferir las eliminaciones.

todo lo que tenemos que hacer (en negrita):
syscal_windows.go:
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | (modo & FILE_SHARE_DELETE) )

Investigué la configuración de FILE_SHARE_DELETE como parte de #32188, pero descubrí que configurarlo no reducía empíricamente la tasa de errores de las llamadas os.Rename y io.ReadFile ; de todos modos, todavía se necesitaba un bucle de reintento.

Me interesaría mucho ver una prueba que demuestre una diferencia observable significativa al establecer esta bandera, porque yo mismo no entiendo realmente sus implicaciones.

@bcmills ahora puede encontrar pruebas unitarias/de regresión para esto en https://github.com/olljanat/go/commit/3828f1a5d0ebb69b4c459d5243799ded36ac1ee8 actualmente falla en el error The process cannot access the file because it is being used by another process. en Windows y comienza a funcionar después de FILE_SHARE_DELETE bandera

¡Nota! que en Windows aún necesita ese paso que mueve el archivo de registro example.log.1 a un nombre aleatorio/único porque otro motivo es que cambiar el nombre de example.log a example.log.1 fallaría en el error de Access is denied. incluso cuando el indicador FILE_SHARE_DELETE está habilitado. Eso es algo específico de la plataforma que el desarrollador debe cuidar.

/cc @jhowardmsft @jterry75 @jstarks @ddebroy FYI; usted podría estar interesado en este también.

@kevpar : para su

Si alguna persona que se une al hilo puede expresar razones (por ejemplo, el código existente está roto) para que esto sea el valor predeterminado, en lugar de estar disponible a través del indicador de solo Windows en os.OpenFile (), ¡hágalo!

@networkimprov En mi opinión , debe manejarse con el indicador de solo Windows porque se necesita un desarrollador para manejar también algunas otras cosas específicas de Windows de todos modos, como en mi ejemplo de rotación de registro.

Sin embargo, como puedo ver, se invitó a un par de empleados de Microsoft a debatir, por lo que es interesante ver si piensan de manera diferente.

¿Alguien por cierto ha comenzado a desarrollar una nueva solución para esta? ¿Algun voluntario?

@olljanat
Si es una solución como una bandera, entonces la solución es una sola línea, lea:
https://github.com/golang/go/issues/32088#issuecomment-500562223

Intentaré representar la perspectiva del equipo de Windows aquí... preferimos que Go establezca siempre FILE_SHARE_DELETE, sin ninguna opción. En general, para facilitar la migración hacia/desde Windows, preferiríamos un comportamiento consistente con Linux, y no veo por qué los usuarios o el software de Windows preferirían el comportamiento predeterminado !FILE_SHARE_DELETE lo suficiente como para que esto sea una excepción a la regla.

Una nota interesante, contrariamente al comentario de

En otras palabras, si ejecuta el programa de prueba de mattn y la secuencia de del, dir, etc. en la última versión de Windows, verá que el archivo desaparece del espacio de nombres inmediatamente después de eliminarlo, no después de que finalice el programa de prueba. Al igual que Linux.

Por lo tanto, incluso en el propio Windows, con su riesgo de compatibilidad de aplicaciones significativamente mayor, estamos realizando pequeños cambios para facilitar la migración de software que no es de Windows a Windows. Realmente animo a Go a hacer lo mismo.

@jstarks , gracias por reivindicarme. He tomado una tonelada de calor en este hilo para esa posición.

Sugeriría una opción global para deshabilitar fsd en caso de que cualquier implementación de Windows dependa del comportamiento actual no documentado.

@ianlancetaylor ¿estás de acuerdo?

Como el comportamiento predeterminado de CreateFile es que no puede eliminar el archivo de apertura, creo que deberíamos mantener el comportamiento predeterminado. Alguien puede querer el comportamiento original. Por ejemplo, puede haber programadores que estén comprobando que su aplicación funciona utilizando una forma imposible de eliminar el archivo.

El comportamiento de Python es el mismo que el comportamiento actual de Go. Y nunca escuché que Python tenga un plan para cambiar el comportamiento que no puede eliminar archivos abiertos.

No me he tomado el tiempo para desarrollar una opinión seria sobre este tema, pero no creo que una opción global sea una buena elección. Deberíamos mantener el comportamiento actual (y quizás agregar una bandera para seleccionar el otro comportamiento) o deberíamos cambiar el comportamiento (y quizás agregar una bandera para seleccionar el comportamiento original actual).

Con respecto a si cambiar o no el comportamiento predeterminado, creo que debemos preguntarnos si es más probable que cambiar el comportamiento arregle un programa Go escrito para ejecutarse en sistemas Unix y Windows o si es más probable que cambiar el comportamiento romperá un programa Go escrito para ejecutarse en sistemas Windows. No sé la respuesta a eso.

Como sugiere @mattn , también vale la pena preguntarse qué hacen otros idiomas.

Si se convierte en el valor predeterminado, y cualquier implementación de Windows depende de él, me imagino que querrían tanto un indicador os.OpenFile() como una opción global para volver a cambiar con fines de transición.

Hay una opción global para deshabilitar IPv6, la última vez que lo comprobé.

Una forma de descubrir el impacto es cambiarlo en la propina (¿y anunciarlo en el blog?) y ver qué se informa.

Python y Erlang se mencionan en el texto del problema:
Erlang lo convirtió en predeterminado hace más de seis años: erlang/ otp@0e02f48
Python quería pero no pudo, debido a las limitaciones del tiempo de ejecución de MSVC: https://bugs.python.org/issue15244

Intentaré representar la perspectiva del equipo de Windows aquí... preferimos que Go establezca siempre FILE_SHARE_DELETE, sin ninguna opción. En general, para facilitar la migración hacia/desde Windows, preferiríamos un comportamiento consistente con Linux, y no veo por qué los usuarios o el software de Windows preferirían el comportamiento predeterminado !FILE_SHARE_DELETE lo suficiente como para que esto sea una excepción a la regla.

en la versión más reciente de Windows, actualizamos DeleteFile (en NTFS) para realizar una eliminación "POSIX", donde el archivo se elimina del espacio de nombres inmediatamente en lugar de esperar a que se cierren todos los identificadores abiertos del archivo. Todavía respeta FILE_SHARE_DELETE, pero ahora se comporta más como POSIX unlink. Esta funcionalidad se agregó para WSL y también se consideró que valía la pena usarla de forma predeterminada para el software de Windows.

@jstarks ese es un detalle muy interesante. ¿Es algo que podemos esperar que Microsoft también haga backport a versiones anteriores del sistema operativo? Pienso especialmente en Windows Server 2019, que es la versión LTSC más reciente y recién lanzada, que seguirá siendo compatible durante mucho tiempo.

Como si Microsoft hiciera eso, preferiría habilitar FILE_SHARE_DELETE de forma predeterminada en Go también.

Java, C # y todos los demás lenguajes principales que conozco toman el comportamiento predeterminado del sistema operativo. En mi opinión, el comportamiento predeterminado debe dejarse en manos del implementador del sistema operativo. Si Windows realmente quiere adoptar el enfoque POSIX con respecto a la apertura de archivos, debe correr ese riesgo y hacerlo como lo hizo con DeleteFile.

Creo que la biblioteca Go debería permitir crear/abrir archivos compartidos y eliminados. Puedo dar un ejemplo práctico al tratar con el modo de archivo. Al migrar Hadoop a Windows, tenemos que hacer un esfuerzo adicional para crear un método especial en JNI para obtener el identificador o la secuencia de eliminación de archivos compartidos.

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

@jstarks

al decir

en la versión más reciente de Windows, actualizamos DeleteFile (en NTFS) para realizar una eliminación "POSIX", donde el archivo se elimina del espacio de nombres inmediatamente

¿Quiere decir que el archivo abierto permanece solo como fd y el código a continuación?

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()
    }
}

no fallará en

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

después de aplicar la solución sugerida ?

Si es así, ¡eso es realmente genial!

Con respecto a si cambiar o no el comportamiento predeterminado, creo que debemos preguntarnos si es más probable que cambiar el comportamiento arregle un programa Go escrito para ejecutarse en sistemas Unix y Windows o si es más probable que cambiar el comportamiento romperá un programa Go escrito para ejecutarse en sistemas Windows. No sé la respuesta a eso.

No hay problem to be fixed ejecutando un programa Unix en Windows. Windows maneja la eliminación de archivos abiertos de manera diferente a Unix. Es similar a la distinción entre mayúsculas y minúsculas en los nombres de archivos: Unix distingue entre mayúsculas y minúsculas y Windows no distingue entre mayúsculas y minúsculas. No podemos hacer nada acerca de ambas diferencias.

Y, mientras John asegura que Microsoft está tratando de cambiar las cosas, esperaría hasta que el cambio esté aquí. Y muchos usuarios de Windows todavía usan Windows 7 y todas las demás versiones de Windows. John, ¿actualizarías Windows 7 también?

Creo que deberíamos dejar que Microsoft maneje el cambio. Si reparamos los usuarios de Go, seguirá habiendo desarrolladores que no sean de Go. Entonces, Microsoft tendría que lidiar con esto independientemente.

Además, FILE_SHARE_DELETE tiene sus propios violines. Por ejemplo, leyendo https://bugs.python.org/issue15244

En realidad, no es exactamente la misma semántica que Unix.

Después de eliminar el archivo, no puede crear un archivo con el mismo nombre ni eliminar el directorio que lo contiene hasta que se haya cerrado el identificador.

Sin embargo, se puede solucionar moviendo el archivo a otro lugar (como el directorio raíz) antes de eliminarlo.

Hay, probablemente, muchos otros como ese que no conocemos. No hemos usado FILE_SHARE_DELETE en absoluto. ¿Usted propone que cambiemos Go libs para usar FILE_SHARE_DELETE de forma predeterminada y luego esperemos a que los usuarios se quejen de sus programas rotos?

Como sugiere @mattn , también vale la pena preguntarse qué hacen otros idiomas.

Solo verifiqué Mingw C: se comporta como Go. Una vez que el archivo se abre con fopen, no se puede eliminar hasta que se llame a fclose. Me sorprendería si alguna de las herramientas de desarrollo de Microsoft (C, C++, C#, VB y otras) fuera diferente.

Y no veo cómo podríamos cambiar el comportamiento predeterminado de esa manera. Es posible que algún programa tenga un archivo abierto específicamente, por lo que no puede ser eliminado por otro programa. Ese es un enfoque válido en Windows en mi opinión. Tenemos que apoyarlo en el futuro.

Alex

Añadido hace 7 años, creo:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858

Hasta ahora, nadie ha publicado ningún caso de aplicaciones Go que dependan del comportamiento actual de Windows no documentado. Pero hemos escuchado al menos 4 casos de aplicaciones Go que fallaron en Windows debido a esto.

Como se indicó anteriormente, hay una solución simple para la reutilización del nombre de archivo después de os.Remove(), que debemos documentar:
os.Rename(path, unique_path); os.Remove(unique_path)

Hasta ahora, nadie ha publicado ningún caso de aplicaciones Go que dependan del comportamiento actual de Windows no documentado.

Estas personas ni siquiera saben que es posible reportar errores de Go.

No debe esperar que estos usuarios controlen la lista de problemas de Go en busca de cambios con la esperanza de que algo los afecte. Tienen vida para vivir. Depende de nosotros no romper las herramientas que usan.

Alex

Alex, está claro que no estás convencido. Luego veo dos caminos posibles para que las personas puedan escribir código portátil en Go:

  1. un nuevo indicador de suscripción a Open que significa FILE_SHARE_DELETE para Windows y se ignora para los demás sistemas operativos. Entonces alentaríamos a cualquiera que escriba código esperando que la semántica POSIX pase esta bandera en todas partes; o,
  2. un nuevo paquete de Go posix (en algún repositorio en algún lugar) con una función, Open , que envuelve CreateFile con FILE_SHARE_DELETE pero, por lo demás, se comporta como os.Open . En plataformas que no son de Windows, simplemente llamaría a os.Open directamente. Entonces animaríamos a cualquiera que escriba código esperando que la semántica POSIX use posix.Open lugar de os.Open todas partes.

Ambos enfoques requieren que los desarrolladores de Linux tomen medidas adicionales para preservar el comportamiento de POSIX en Windows, pero al menos son mejores que escribir envoltorios CreateFile únicos (como acabamos de hacer para containerd).

Alex, está claro que no estás convencido.

No estoy convencido. Pero no necesitas persuadirme, necesitas persuadir a Go Team. Ellos tomarán la decisión.

  1. un nuevo indicador de suscripción a Open que significa FILE_SHARE_DELETE para Windows y se ignora para los demás sistemas operativos. Entonces alentaríamos a cualquiera que escriba código esperando que la semántica POSIX pase esta bandera en todas partes; o,

A mi tampoco me gusta esta opción.

En primer lugar, se supone que el paquete os proporciona una instalación disponible para cualquier sistema operativo. Cualquier cosa específica del sistema operativo debe ir a syscall, o golang.org/x/sys/windows, o cualquier otro paquete que nos guste.

En segundo lugar, me preocupa terminar con el tipo de archivo FILE_SHARE_DELETE en mi programa, incluso si no quiero. Imagínese, si importo un paquete externo que decide usar el modo de archivo FILE_SHARE_DELETE . ¿Cómo sabría que parte de mi código usa FILE_SHARE_DELETE ? Tendría que grep código fuente del paquete. O la documentación del paquete tendría que advertir a sus usuarios. Supongo que esto también podría suceder, si algún autor del paquete simplemente usa syscall.CreateFile con FILE_SHARE_DELETE directamente. Pero creo que si se bendice FILE_SHARE_DELETE para el paquete del sistema operativo, esto será más común. Sospecho que los usuarios de Linux, que no saben nada acerca de Windows, usarían esta bandera por defecto, para hacer que su programa de Linux sea "más fácil" para Windows.

Tercero, tendríamos que documentar la bandera FILE_SHARE_DELETE . No podemos simplemente decir POSIX. Tendríamos que describir lo que hace la bandera. En palabras simples. También recuerda:

En realidad, no es exactamente la misma semántica que Unix.

Después de eliminar el archivo, no puede crear un archivo con el mismo nombre ni eliminar el directorio que lo contiene hasta que se haya cerrado el identificador.

Sin embargo, se puede solucionar moviendo el archivo a otro lugar (como el directorio raíz) antes de eliminarlo.

de https://github.com/golang/go/issues/32088#issuecomment -504321027. Tendríamos que documentar eso también. Y todas las demás funcionalidades que trae el indicador FILE_SHARE_DELETE .

Cuarto, necesitaríamos agregar nuevas pruebas por FILE_SHARE_DELETE . ¿Cuáles serían estas pruebas? ¿Qué sucede si olvidamos probar alguna funcionalidad, solo para descubrir más tarde que la funcionalidad no se puede lograr usando el indicador FILE_SHARE_DELETE ? No podemos eliminar la bandera FILE_SHARE_DELETE , si no nos gusta. Una vez que está adentro, se queda. Y tenemos que apoyarlo en el futuro.

2. un nuevo paquete de Go posix (en algún repositorio en alguna parte) con una función, Open , que envuelve CreateFile con FILE_SHARE_DELETE pero por lo demás se comporta como os.Open

No veo cómo eso es posible. Pero, si es posible, debería poder crear el paquete usted mismo. Por ejemplo, como github.com/jstarks/posix. ¿No? Podríamos agregar el paquete en golang.org/x/sys/windows/posix o algo así, pero no veo mucha diferencia.

Alex

Se supone que el paquete os proporciona una instalación disponible para cualquier sistema operativo ...
tendríamos que documentar el indicador FILE_SHARE_DELETE. No podemos simplemente decir POSIX...
necesitaríamos agregar nuevas pruebas para FILE_SHARE_DELETE. ¿Cuáles serían estas pruebas?

Todos estos fueron abordados anteriormente.

si importo un paquete externo que decide usar el modo de archivo FILE_SHARE_DELETE. Cómo puedo saber

Ese es otro argumento para convertirlo en el predeterminado y proporcionar una bandera global para deshabilitarlo.

Publiqué en golang-nuts para buscar aplicaciones de Windows afectadas; Haré ping cada pocos días para mantenerlo visible. Si eso no surge mucho, convertirlo en el consejo predeterminado para 1.14 debería aclarar las cosas.
https://groups.google.com/d/topic/golang-nuts/8BiP_mPoCd4/discusión

Ese es otro argumento para convertirlo en el predeterminado y proporcionar una bandera global para deshabilitarlo.

Por cierto. ¿Qué debería hacer esa bandera en Linux? Afaiu, ¿realmente también extrañamos la funcionalidad para abrir archivos en Linux de manera que no puedan ser eliminados por otros procesos? Según este https://gavv.github.io/articles/file-locks/#open -file-description-locks-fcntl, debería ser posible implementarlo en versiones modernas de Linux.

El indicador (por ejemplo, var OpenWithout_FILE_SHARE_DELETE = false ) se definiría en syscall_windows.go, ya que afecta el comportamiento de syscall.Open().

Un indicador para algo específico de Linux tendría un nombre diferente y se definiría en syscall_linux.go. De improviso, no sé cómo evitar la eliminación de un archivo abierto en Linux que no sea a través de los permisos en su directorio. El enlace que proporcionó describe el bloqueo "consultivo".

@jstarks antes de considerar introducir el FILE_SHARE_DELETE , también debemos decidir qué hacer con la incapacidad de Go para eliminar el archivo ejecutable en ejecución. No creo que la bandera FILE_SHARE_DELETE pueda ayudarnos aquí. ¿Puede? Si no podemos implementar la eliminación del archivo ejecutable que aún se está ejecutando, no podemos reclamar ninguna compatibilidad con POSIX. Y la bandera FILE_SHARE_DELETE parece la mitad de la solución.

Más aún, a veces fallamos al eliminar el archivo ejecutable de un proceso que incluso terminó. Consulte, por ejemplo, #25965, #19491 y #32188 para obtener más información. Básicamente, llamamos a WaitForSingleObject en el identificador del proceso hasta que señale. Pero eso no garantiza que el archivo ejecutable se pueda eliminar. Tuvimos que agregar una espera de 5 milisegundos antes de intentar eliminar el archivo. E incluso eso no siempre funciona - https://github.com/golang/go/issues/25965#issuecomment -482037476

Además, no está relacionado con este problema, pero dado que tengo su atención, tal vez pueda ayudarnos a revivir el puerto de brazo de Windows. Ver #32135 para más detalles. Básicamente, @jordanrh1 portó Go https://github.com/golang/go/wiki/PortingPolicy No sé, si alguien está interesado en ejecutar programas Go en Windows 10 Iot, pero ya tenemos este soporte. Sería triste perderlo solo porque no tenemos un constructor en funcionamiento.

Gracias.

Alex

Tenga en cuenta esta solución sugerida para eliminar archivos ejecutables temporales: https://github.com/golang/go/issues/25965#issuecomment -495636291

No hay resultados de las publicaciones de golang-nuts; comencé un nuevo hilo:
https://groups.google.com/d/topic/golang-nuts/aRvSo3iKvJY/discusión

Al menos una preocupación no tan hipotética que creo que debería investigarse sería el efecto sobre el bloqueo de archivos y los programas que dependen de él.

Abrir cada identificador de archivo con FILE_SHARE_DELETE debilitaría efectivamente la semántica de LockFileEx a la de los bloqueos de advertencia de POSIX, porque cualquier archivo con FILE_SHARE_DELETE puede eliminarse, incluso si se activa un bloqueo. retenido en una región de ese archivo. 1

Por supuesto, esto solo se aplicaría a las llamadas LockFileEx realizadas con el descriptor devuelto desde os.File.Fd , pero eso es exactamente lo que se hace en la biblioteca de bloqueo de archivos Go más popular (que actualmente tiene 33 importadores conocidos , incluido Docker , gVisor y Kubernetes). También se hace en Terraform , y LockFileEx se usa en muchos otros códigos Go .

Sigo pensando que este cambio parece un esfuerzo desacertado en el mejor de los casos (y una fábrica de CVE en el peor de los casos). Realmente no veo cómo los beneficios turbios superan la clara ruptura del código que va a ocurrir.

También creo que la mayoría de los usuarios de golang-nuts no van a asimilar completamente las implicaciones de este cambio. Con el permiso del equipo de Go, una publicación en golang-dev podría ser más efectiva para generar debate (especialmente porque se trata de un cambio fundamental). En realidad, técnicamente afecta la cadena de herramientas de Go, que también usa LockFileEx de la manera mencionada anteriormente .


1 Sí, sé que parte de la aplicación de bloqueo de Windows aún sería más fuerte que en POSIX, pero si puede destruir el archivo, el uso de un archivo de bloqueo para la reserva de recursos se descarta.

Publiqué en golang-dev cuando abrí el problema.
https://groups.google.com/d/topic/golang-dev/R79TJAzsBfM/discusión

¿Alguna de las bibliotecas que enumeró _depende_ del comportamiento de GOOS=windows? Ha afirmado repetidamente que este cambio rompería el código existente, pero nunca proporcionó ninguna evidencia.

Amablemente deja de hacer declaraciones sensacionalistas insostenibles. No aclara nada, y lo encuentro ofensivo.

Publiqué en golang-dev cuando abrí el problema. https://groups.google.com/d/topic/golang-dev/R79TJAzsBfM/discusión

Parece que todos rechazaron la idea por sus propios motivos (todos ellos similares a los mencionados anteriormente en este hilo), pero si ahora se plantea la idea de aplicar este cambio en Go 1.14, un seguimiento de eso. hilo podría ser útil (con un enlace de recordatorio a esta discusión).

¿Alguna de las bibliotecas que enumeró depende del comportamiento de GOOS=windows? Ha afirmado repetidamente que este cambio rompería el código existente, pero nunca proporcionó ninguna evidencia.

Eso requeriría una evaluación de seguridad completa (de una gran cantidad de código) para decirlo con certeza. Creo que ciertamente proporcioné suficiente evidencia de que las personas confían en un comportamiento que cambiaría visiblemente con la introducción de esta bandera. Y eso es solo código disponible públicamente en GitHub.

Amablemente deja de hacer declaraciones sensacionalistas insostenibles. No aclara nada, y lo encuentro ofensivo.

Si los enlaces y el código no son lo suficientemente compatibles, entonces no sé qué habrá en su libro. No encontrará un código que deje de compilarse debido a este cambio, pero ciertamente encontrará un código que se comporte de manera diferente con él, y mucha gente consideraría que ese código está "roto" debido a esos cambios en el comportamiento.

No tome mi objeción personalmente, creo que todos aquí (incluyéndolo a usted) solo están tratando de agregar su opinión para mejorar el lenguaje. Si va a defender un cambio central, debe esperar una resistencia saludable.

Como expliqué anteriormente, eliminar un archivo con FILE_SHARE_DELETE no tiene la misma función que el de UNIX. Por ejemplo, intente estos pasos.

C Principal

#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;
}

ver.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;
}

Makefile

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

Al principio, ejecute main.exe y, a continuación, ejecute watch.exe. En UNIX, unlink(2) elimina el archivo inmediatamente. Entonces, otro proceso que está viendo que el archivo existe puede crear un nuevo archivo con el mismo nombre de archivo. Pero en Windows, notepad.exe muestra un cuadro de diálogo de error "infracción de acceso" ya que Windows no elimina el archivo inmediatamente. No puedo decirte lo que sucederá con esto. ¿Problema de seguridad? ¿Perdió archivos importantes? Lo siento, no lo sé.

@havoc-io Windows no eliminará un archivo abierto hasta que se cierre, y solo mantiene bloqueos para archivos abiertos. Por lo tanto, el uso del bloqueo no implica la dependencia de la falla de os.Remove(). Por favor proporcione ejemplos que dependan específicamente de esa falla.

Haré ping en el hilo golang-dev la próxima semana. Dos personas le respondieron; uno no había leído este número, y tampoco proporcionó ningún ejemplo.

Las afirmaciones sensacionalistas e insostenibles no son "resistencia sana".

@havoc-io Windows no eliminará un archivo abierto hasta que se cierre, y solo mantiene bloqueos para archivos abiertos. Por lo tanto, el uso del bloqueo no implica la dependencia de la falla de os.Remove(). Por favor proporcione ejemplos que dependan específicamente de esa falla.

@networkimprov Estás combinando dos tipos diferentes de bloqueo.

Los archivos de bloqueo (bloqueados con LockFileEx ) se usan normalmente para proteger un recurso. Un ejemplo podría ser asegurarse de que solo haya una única instancia de un demonio en ejecución. Si un demonio de Windows Go abre un archivo y luego ejecuta LockFileEx en el archivo y adquiere con éxito un bloqueo exclusivo, podría asumir con razón que el archivo de bloqueo no se eliminará de debajo. Pero, si FILE_SHARE_DELETE está habilitado de forma predeterminada, entonces todo el código existente que hace esa suposición ya no es correcto. Si algún agente adicional eliminara ese archivo de bloqueo (por ejemplo, un usuario que elimina un directorio sin darse cuenta), el daemon original aún podría estar ejecutándose cuando uno nuevo decida iniciarse.

Los archivos de bloqueo también se pueden usar para canalizar comandos si se omite el indicador LOCKFILE_FAIL_IMMEDIATELY , lo que hace que LockFileEx espere su turno para la adquisición del bloqueo. Si el archivo de bloqueo se elimina sin darse cuenta, podría hacer que se ejecutaran varios comandos a la vez que deberían haberse canalizado.

Las afirmaciones sensacionalistas e insostenibles no son "resistencia sana".

Nadie está haciendo afirmaciones sensacionalistas o insostenibles en este hilo. Todas las personas que no estuvieron de acuerdo con su propuesta, incluido yo mismo, proporcionaron argumentos sólidos respaldados por código. Al igual que las personas que han apoyado su propuesta.

Por favor revise los documentos de WinAPI; Windows no eliminará un archivo abierto hasta que se cierre, y solo mantiene bloqueos LockFileEx() para archivos abiertos, por lo que un bloqueo no puede persistir cuando se cierra el archivo, independientemente de la eliminación. Un intento de abrir un archivo bloqueado (es decir, abierto) después de eliminarlo vería un error, por lo que no se podría adquirir un segundo bloqueo. Los subprocesos que esperan un bloqueo tienen el archivo abierto, por lo que no se verían afectados por la eliminación.

A menos que haya leído mal los documentos, sus dos últimos escenarios no son compatibles. Y esto fue sensacional y sin apoyo: "este cambio parece... una fábrica de CVE en el peor de los casos. Realmente no veo cómo los beneficios turbios superan la clara ruptura del código que va a ocurrir".

@networkimprov El problema es que los archivos de bloqueo que restringen los recursos generalmente se basan en la ruta del archivo. Es posible que el objeto de archivo subyacente no se elimine hasta que se cierre el archivo, pero si se elimina a través de DeleteFile , no se podrá acceder a él en la ruta anterior en el sistema de archivos

No creo que haya sido un comentario sensacionalista: parece bastante claro que algo como esto podría causar un problema de seguridad si el código se basa en ciertas invariantes del sistema operativo que ya no son ciertas. Y los beneficios aquí parecen bastante turbios (como varias personas han comentado: esto no alinea el comportamiento de Windows con POSIX) y la ruptura parece bastante clara (al menos rompe ciertos comportamientos de bloqueo).

1 Al menos ese parece ser el caso cuando se prueba y en base a este comentario .

Re "en la versión más reciente de Windows, actualizamos DeleteFile (en NTFS) para realizar una eliminación 'POSIX', donde el archivo se elimina del espacio de nombres inmediatamente" desde https://github.com/golang/go/issues/ 32088#emitir comentario -502850674. AFAIK esa versión de Windows no se lanza. ¿A qué pruebas te refieres?

Re beneficios, creo que deberías revisar todo el hilo.

AFAIK esa versión de Windows no se lanza.

@networkimprov Creo que ya es el caso en Windows 10. Parece que puedo eliminar un archivo bloqueado con LockFileEx si se especifica FILE_SHARE_DELETE en CreateFileW al abrir el archivo.

DeleteFileW() no daría un error en ese escenario. ¿Cómo sabe que se eliminó inmediatamente del sistema de archivos, en lugar de eliminarlo al cerrar el archivo/aplicación? ¿Intentó abrir o volver a crear el archivo después de eliminarlo?

En realidad, técnicamente afecta la cadena de herramientas de Go, que _también_ usa LockFileEx de la manera antes mencionada.

Dentro del comando go queremos mucho la semántica POSIX, y no debemos confiar de ninguna manera en la ausencia de FILE_SHARE_DELETE . De hecho, agregué el paquete cmd/go/internal/robustio específicamente para _resolver_ las formas en que el bloqueo de archivos de Windows se desvía de POSIX.

@ianlancetaylor He publicado repetidamente en golang-nuts & -dev buscando comentarios de cualquier proyecto que dependa del comportamiento actual (no documentado) de Windows syscall.Open(). No ha surgido un solo caso de uso.

Dado que varios casos de uso que necesitan FILE_SHARE_DELETE han aparecido anteriormente (y dado que Microsoft solicitó específicamente que lo usemos), configuremos este indicador de forma predeterminada en la versión preliminar 1.14 y volvamos a evaluar si hay casos de uso para la superficie de comportamiento original durante el ciclo.

Como se sugirió anteriormente en este hilo, alguien que necesita una llamada open() sin esta bandera podría lanzar la suya, usando CreateFileW() & os.NewFile() .

¿Podemos al menos exponer una perilla para esto?

Esto es muy útil para implementar la rotación de registros en Windows.

Esto es muy útil para implementar la rotación de registros en Windows.

No estoy seguro, pero supongo que no funcionará ya que FILE_SHARE_DELETE no permite crear un nuevo archivo con el nombre del archivo original si el identificador del archivo no está cerrado.

@Random-Liu, ¿está solicitando una forma de habilitar fsd en Open () o deshabilitarlo?

@mattn Funciona para mí. Después de os.Rename el archivo de registro anterior, puedo crear un nuevo archivo de registro con el nombre original e informar al daemon para que vuelva a abrir el archivo de registro.

@networkimprov Quiero una forma de habilitar FILE_SHARE_DELETE . Actualmente estoy usando syscall.CreateFile directamente.

Mi versión de Windows:

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

@ Random-Liu Pero supongo que no podemos usar esto para simular el comportamiento de UNIX.

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

La aplicación externa no puede acceder al nombre de archivo original.

Mattn, no hay necesidad de seguir repitiendo ese punto. Ya se ha dicho que debemos documentar la necesidad (en Windows) de cambiar el nombre de un archivo antes de eliminarlo cuando el nombre del archivo debe ser reutilizable antes de cerrar el identificador.

Si está cambiando el nombre del archivo antes de cerrarlo por la propia aplicación, creo que no hay ventajas para los usuarios que quieren hacer logrotate.

@ianlancetaylor vuelve a intentar: https://github.com/golang/go/issues/32088#issuecomment -526074116

He marcado este problema como un bloqueador de versiones.

@bcmills , ¿puedo interesarte en publicar una CL?

Esta línea: https://golang.org/src/syscall/syscall_windows.go#L291
Solo necesita: sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)

Editar: una prueba para verificar que crearía un archivo, luego intentaría cambiarle el nombre antes de cerrarlo.

Necesitamos esto pronto para que haya tiempo para que cualquier problema surja antes de que se congele...

Además, los documentos os.Remove() deben mencionar que en Windows, se debe cambiar el nombre de un archivo abierto antes de eliminarlo si el nombre del archivo debe estar disponible para su reutilización antes de que se cierre el archivo. Windows no completa una eliminación en un archivo abierto hasta que se cierra.

Parece que la última versión de Windows cumple con https://github.com/golang/go/issues/32088#issuecomment -502850674. Consulte https://github.com/papertrail/go-tail/pull/10#issuecomment -529460973 para obtener más detalles. Ahora podemos reutilizar el nombre de un archivo eliminado, incluso si todavía hay identificadores abiertos en el archivo antiguo, siempre que esos identificadores se hayan abierto con el modo de uso compartido FILE_SHARE_DELETE . No es necesario cambiar el nombre antes de eliminar. ¿Quizás finalmente tenga sentido establecer FILE_SHARE_DELETE de forma predeterminada en Go?

Esa es la intención; solo necesitamos que alguien envíe una CL. (Lo haría, pero no puedo firmar un CLA).
Mis dos comentarios anteriores describen los cambios necesarios...

@jstarks @jordanrh1 @thaJeztah

Pregunta:
¿Cómo obtenemos un comportamiento consistente?
Si el programa se ejecuta en "la última versión de Windows", permitiría crear un archivo con un nombre similar a un archivo "pendiente de eliminación", la versión anterior se romperá por error.

Esto en realidad puede decir: "pasó las pruebas / qa pero no funciona en la mayoría de los objetivos" ahora, y
"generalmente funciona, y en algún momento no" en el futuro (un desarrollador dentro de 2 años que recibe un error de "archivo ya salió" no podrá reproducir).

Necesitamos descubrir cómo identificar las versiones 'antiguas' y cambiar el comportamiento predeterminado para ellas o mejorar el error si "hay una eliminación pendiente". No estoy seguro de cómo podemos hacer eso.
Y si no podemos, ¿cómo lidiamos con la inconsistencia?

@guybrand como se indicó anteriormente, documentaremos que en Windows "se debe cambiar el nombre de un archivo abierto antes de eliminarlo si el nombre del archivo debe estar disponible para su reutilización antes de cerrar el archivo".

Esto funciona para las versiones antiguas y nuevas de Windows, así como para POSIX.

Eso suena como un enfoque diferente: "Renombrar siempre antes de reutilizar, incluso en Windows o POSIX nuevos"
Me gusta: respalda una práctica común que tendrá éxito en todas partes.

Cambio https://golang.org/cl/194957 menciona este problema: syscall: allow FILE_SHARE_DELETE on syscall.Open on Windows

El hilo golang-nuts está aquí y no tuvo respuestas. Eso parece indicar que nadie que lea golang-nuts y conozca este comportamiento confía en él.

Los subprocesos golang-dev están aquí y aquí , y el último (el subproceso más reciente) tampoco obtuvo objeciones.

@alexbrainman -2'd el CL para implementarlo en base a una aparente falta de decisión, por lo que he vuelto a etiquetar este problema como NeedsDecision para que podamos obtener una dirección clara.

Personalmente, para mí, la decisión parece clara: dado que la gente de Microsoft en este hilo apoya cambiar el comportamiento predeterminado para usar FILE_SHARE_DELETE (https://github.com/golang/go/issues/32088 #issuecomment-502850674), y que a nadie en golang-nuts parece importarle de una forma u otra, creo que deberíamos hacerlo.

Las objeciones que he visto en este hilo parecen caer en dos categorías:

  1. La preocupación de @alexbrainman (https://github.com/golang/go/issues/32088#issuecomment-504321027) y @mattn (https://github.com/golang/go/issues/32088#issuecomment-494417146) parece ser que, dado que el comportamiento aún no coincidirá con POSIX, los usuarios que esperan la semántica de POSIX pueden confundirse. (Pero ya se da el caso de que Open en Windows no proporciona semántica POSIX, por lo que me parece que esa preocupación es independiente del cambio propuesto).

  2. La preocupación de @havoc-io (https://github.com/golang/go/issues/32088#issuecomment-510314947) parece ser que FILE_SHARE_DELETE disminuirá las invariantes proporcionadas por los paquetes de bloqueo de archivos cuando se ejecuta en Windows . Pero ese argumento me parece un poco confuso, porque el paquete concreto que se muestra como ejemplo ( github.com/gofrs/flock ) señala explícitamente que "no se garantiza que los comportamientos de bloqueo sean los mismos en cada plataforma", y de una auditoría rápida ninguno de los ejemplos concretos parece basarse en la interacción específica de Windows entre el bloqueo y la eliminación de archivos.

Sin embargo, el comentario en https://github.com/golang/go/issues/32088#issuecomment -498690757 me intriga:

Estoy de acuerdo con @ianlancetaylor, cambiar todo el comportamiento de la cadena de herramientas, como se propone en https://codereview.appspot.com/8203043/, no es una buena idea. Un problema inmediato que viene a la mente de esta propuesta cuando se utiliza un identificador de archivo como archivo de bloqueo. Usamos este comportamiento.

@maruel , ¿podría dar más detalles sobre cómo su programa se basa en la interacción específica de Windows entre el bloqueo de archivos y la eliminación o el cambio de nombre?

En Chrome infra utilizamos la presencia de archivos como mecanismo de bloqueo. Diría que no se preocupe demasiado por este caso de uso. En la práctica, por lo que entiendo, la semántica O_CREATE + FILE_FLAG_DELETE_ON_CLOSE es suficiente para esta necesidad y esto se puede reemplazar con un mutex en el espacio Global\\ nombres

Esta solución no se menciona:

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()

Tiene dos inconvenientes:

  • Cerrar () no está justo después de Abrir ()
  • Recuperar el código de error es más complicado

@iwdgo Esta solución es para uso de un solo proceso. La mayor parte de la discusión anterior es sobre el acceso simultáneo desde múltiples procesos. Eso es lo que hace que la bandera sea importante.

Para complementar la recapitulación del problema de

Mingw-w64 lo convirtió en predeterminado hace siete años:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858

Erlang lo convirtió en predeterminado hace más de seis años: erlang/ otp@0e02f48

Python no pudo adoptarlo debido a las limitaciones del tiempo de ejecución de MSVC en el momento en que lo consideraron: https://bugs.python.org/issue15244

Un segundo hilo de golang-nuts tampoco vio objeciones:
https://groups.google.com/d/topic/golang-nuts/aRvSo3iKvJY/discusión

¿Quieres hacer FILE_SHARE_DELETE por defecto para Open() ? Nuestra discusión no debería ser esa.

  1. La preocupación de @alexbrainman ( #32088 (comentario) )... parece ser que, dado que el comportamiento aún no coincide con POSIX...

Mi principal preocupación es romper los programas de la gente. Similar a lo que dijo @minux en https://codereview.appspot.com/8203043/#msg10

otra razón: las personas que usan Windows aceptan que no poder cambiar el nombre o
eliminar un archivo abierto es la norma y ya se ha acostumbrado a ese hecho.

No podemos cambiar silenciosamente el comportamiento del programa Go 1.0 aquí, tal vez la gente
en realidad depender de ello.

¿Qué pasó con la promesa de compatibilidad de Go 1.0 aquí? ¿En qué parte de https://golang.org/doc/go1compat dice que está bien cambiar el comportamiento de apertura de archivos si anuncia su intención de volverse loco?

¿Tal vez podamos cambiar este comportamiento una vez que presentemos Go 2? ¿Podemos usar módulos para evitar que se rompa el código existente? ¿Me equivoco al suponer que para eso se diseñaron los módulos?

Personalmente, para mí, la decisión parece clara: dado que la gente de Microsoft en este hilo apoya cambiar el comportamiento predeterminado para usar FILE_SHARE_DELETE ...

Si Microsoft es una autoridad en este tema para usted, entonces debería ver qué hacen las herramientas de desarrollo de Microsoft.

Usé este programa https://play.golang.org/p/4ZPmV6Df3SD similar a lo que @mattn publicó en https://github.com/golang/go/issues/32088#issuecomment -493753714 Lo construí con un compilador C reciente

Compilador de optimización de Microsoft (R) C/C++, versión 19.16.27030.1 para x64
Derechos de autor (C) Microsoft Corporation. Reservados todos los derechos.

Y, cuando ejecuto el programa, no pude eliminar el archivo C:\Users\Alex\AppData\Local\Temp\a.txt hasta que existió el programa.

Lo mismo con un programa C# similar https://play.golang.org/p/SuM2iWYpZir Usé reciente

Compilador de Microsoft (R) Visual C# versión 2.10.0.0 (b9fb1610)
Derechos de autor (C) Microsoft Corporation. Reservados todos los derechos.

De manera similar, este programa C https://play.golang.org/p/6HgxePzEW_W construido con Mingw reciente

gcc (x86_64-win32-seh-rev0, construido por el proyecto MinGW-W64) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.

tener el mismo efecto. Entonces, ninguna de esas herramientas tiene el indicador FILE_SHARE_DELETE establecido de manera predeterminada.

Y personalmente no veo ningún beneficio de ese cambio. Eso incluye mis contribuciones de Go. Todavía no podremos eliminar los archivos ejecutables mientras se ejecuta el proceso. Todavía tendremos problemas para eliminar directorios si algún proceso tiene sus directorios actuales configurados allí.

Alex

¿Quieres hacer FILE_SHARE_DELETE por defecto para Open() ?

La intención es hacer que os.Open use FILE_SHARE_DELETE de forma predeterminada. Este https://golang.org/cl/194957 es el plan.

Alex

Mi principal preocupación es romper los programas de las personas.

Hasta ahora, nadie puede señalar un solo ejemplo de un programa que fallaría porque se basa en recibir un error al intentar cambiar el nombre/eliminar un archivo abierto con Go. Pero hay casos documentados en los que el comportamiento actual rompe las aplicaciones de Go en Windows.

¿Qué pasó con la promesa de compatibilidad de Go 1.0 aquí?

Los documentos de Go para os.Rename() y .Remove() no mencionan esta característica de Windows, pero tienen 15 menciones de otro comportamiento específico de Windows. Por cierto, Go 2 se envió: https://blog.golang.org/go2-here-we-come

gcc (x86_64-win32-seh-rev0, construido por el proyecto MinGW-W64) 7.3.0

¿De quién C-lib está usando con gcc, quizás de Microsoft? Mingw-w64 está en v6.0 e incluye FILE_SHARE_DELETE en FILE_SHARE_VALID_FLAGS.

Como se documentó anteriormente con respecto a Python, en el pasado, el tiempo de ejecución de MSVC no permitía ciertas combinaciones de indicadores CreateFile() por razones desconocidas. No sé si ese sigue siendo el caso, pero puede ser la razón por la que MS no ha cambiado su C-lib fopen().

No veo ningún beneficio de ese cambio.

Los beneficios han sido explicados repetidamente arriba, por varias personas; especialmente permitiendo os.Rename() en un archivo abierto.

Marcar como propuesta porque la discusión no está convergiendo fácilmente.

Para tratar de resumir el problema y la discusión hasta ahora:

  • En los sistemas Unix, si un proceso abre un archivo, un segundo proceso puede eliminar ese archivo. El proceso original puede seguir usando el archivo (ya no tiene nombre), incluidas las lecturas y escrituras, que se realizan correctamente. El contenido del archivo no se elimina del disco hasta que desaparece la última referencia abierta.

  • En los sistemas Windows, de forma predeterminada, si un proceso abre un archivo, un segundo proceso _no_ puede_ eliminar ese archivo. Un archivo solo se puede eliminar cuando ningún otro proceso lo tiene abierto. El indicador FILE_SHARE_DELETE cambia este comportamiento: si todas las referencias abiertas al archivo se abrieron con FILE_SHARE_DELETE, entonces otro proceso puede llamar a DeleteFile y tendrá éxito en lugar de fallar.

El comportamiento de Windows con FILE_SHARE_DELETE parece ser aún sutilmente diferente al de Unix. La página de MSDN dice:

La función DeleteFile falla si una aplicación intenta eliminar un archivo que tiene otros identificadores abiertos para E/S normal o como un archivo asignado en memoria (se debe haber especificado FILE_SHARE_DELETE cuando se abrieron otros identificadores).

La función DeleteFile marca un archivo para su eliminación al cerrarlo. Por lo tanto, la eliminación del archivo no se produce hasta que se cierra el último identificador del archivo. Las llamadas posteriores a CreateFile para abrir el archivo fallan con ERROR_ACCESS_DENIED.

Es decir, el archivo _nombre_ no se elimina del sistema de archivos hasta que desaparezcan las referencias abiertas. Esto es diferente de Unix, donde el nombre desaparece antes de que se cierren las referencias abiertas. Entonces, si estaba eliminando el archivo en preparación para recrear un archivo con el mismo nombre, eso funciona en Unix pero no en Windows, ni siquiera con FILE_SHARE_DELETE.

La sugerencia original era cambiar syscall.Open para establecer este indicador siempre. Dado que se supone que syscall está cerca de la interfaz del kernel, agregar esa bandera parece fuera de lugar. Pero, en cambio, podríamos considerar si debería configurarse durante os.Open / os.OpenFile.

@alexbrainman argumentó que Unix y Windows son diferentes, que seguirían siendo diferentes incluso si establecemos esta marca, por lo que no debemos confundir el asunto al hacer este cambio a espaldas de los usuarios.

@jstarks de Microsoft dice que Microsoft preferiría que configuremos FILE_SHARE_DELETE automáticamente y que la "versión más reciente" de Windows (no está claro cuál, o si se lanza) cambia DeleteFile para que la semántica Unix del nombre desaparezca al regresar de DeleteFile. Entonces, si hiciéramos esto, en el último Windows, realmente obtendríamos el mismo comportamiento en Unix y Windows.

@networkimprov argumenta que deberíamos hacer el cambio y que no hay ejemplos reales de programas que fallarían.

@alexbrainman todavía parece no estar convencido.

@mattn también ha sugerido que no deberíamos establecer la bandera automáticamente, en parte porque (al menos hasta que todos usen la última versión de Windows) seguirá siendo diferente de Unix.

En general, el sistema operativo del paquete está tratando de proporcionar una API portátil, y parece que, especialmente con el cambio de Windows, establecer el indicador automáticamente ayudaría con eso. Hay otras banderas que configuramos automáticamente en os.Open para ayudar a que el comportamiento sea menos sorprendente para los usuarios, en particular O_CLOEXEC (cerrar al ejecutar). Cualquier programa que se estropee en Windows debido a la configuración de este indicador en os.Open también se estropearía necesariamente en Unix, ¿verdad?

Especialmente porque al menos un ingeniero de Microsoft dice que deberíamos configurarlo, parece que estaría bien configurarlo en el sistema operativo del paquete. Las personas que no quieren que se establezca la bandera aún podrían recurrir al paquete syscall.

@alexbrainman y @mattn , supongamos que configuramos el indicador en os.Open. Sabemos que algunos programas funcionarían mejor. ¿Tiene ejemplos específicos de programas reales (o probables) que fallarían? ¿Esos programas no estarían rotos en Unix ya? Gracias.

@rsc , gracias por tu aporte. Habilitar esto en el paquete "os" funciona para mí. Si se adopta, se debe proporcionar un indicador syscall.Open() para habilitar FILE_SHARE_DELETE para completar.

Para aclarar su resumen, los límites del proceso no son un factor; los efectos son los mismos dentro de un solo proceso. Y la bandera también afecta el cambio de nombre del archivo; con la bandera se completa inmediatamente a diferencia de la eliminación de archivos. (Y eso permite una solución alternativa para la disponibilidad del nombre de archivo inmediatamente después de la eliminación).

Gracias @networkimprov. Sí, entiendo que los límites del proceso no son estrictamente relevantes; simplemente hace que las situaciones sean más fáciles de describir.

Cambié el título y revisé el plan de acción en el texto del problema:

a) os.Create/Open/OpenFile() siempre debe habilitar file_share_delete en Windows,
b) syscall.Open() en Windows debe aceptar un indicador que habilite file_share_delete, y
c) syscall en Windows debería exportar una constante para la nueva bandera.

Los documentos para os.Remove() también deben tener en cuenta que para reutilizar un nombre de archivo después de eliminar un archivo abierto en Windows, se debe hacer: os.Rename(path, unique_path); os.Remove(unique_path) .

No se puede negar la posibilidad de que haya un usuario que implemente un procesamiento exclusivo utilizando el comportamiento os.Open actual de Go.

Pero hay casos documentados en los que el comportamiento actual rompe las aplicaciones de Go en Windows.

¿Qué son éstos? ¿Cuáles son los números de emisión? ¿Y qué quieres decir con la palabra break ? ¿Cuándo se rompieron?

¿De quién C-lib está usando con gcc, quizás de Microsoft? Mingw-w64 está en v6.0 e incluye FILE_SHARE_DELETE en FILE_SHARE_VALID_FLAGS.

No se. Acabo de descargar el archivo x86_64-7.3.0-release-win32-seh-rt_v5-rev0.7z de Internet. Puedes buscarlo en Google. Es de la versión 7.3.

No veo ningún beneficio de ese cambio.

Los beneficios se han explicado repetidamente arriba, ...

Dejó la palabra personally fuera de su cotización. Hablo de mis propias necesidades. Y eso incluye mis contribuciones al proyecto Go.

Para tratar de resumir el problema y la discusión hasta ahora:

No dijo que las herramientas de desarrollo de Microsoft en Windows no proporcionan este indicador de forma predeterminada. Ver mi comentario https://github.com/golang/go/issues/32088#issuecomment -531713562

También ignoraste mi comentario sobre la compatibilidad con Go 1

¿Qué pasó con la promesa de compatibilidad de Go 1.0 aquí? ¿En qué parte de https://golang.org/doc/go1compat dice que está bien cambiar el comportamiento de apertura de archivos si anuncia su intención de volverse loco?

¿Cómo encaja este cambio en la promesa de compatibilidad de Go 1.0?

En general, el sistema operativo del paquete está tratando de proporcionar una API portátil, y parece que, especialmente con el cambio de Windows, establecer el indicador automáticamente ayudaría con eso.

Acepto que este cambio tiene como objetivo que las personas transfieran el software de Unix a Windows.

Desafortunadamente a expensas de los usuarios de Windows. Este cambio sorprenderá a los usuarios y desarrolladores de Windows, porque ningún otro programa o herramienta en Windows funciona de esta manera. Tal vez en el futuro, pero todavía no.

E incluso para las personas que trasladan el software de Unix a Windows, este cambio está haciendo un trabajo bastante malo en mi humilde opinión. Ver 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. parte de mi comentario https://github.com/golang/go/issues/32088#issuecomment -531713562

Hay otras banderas que configuramos automáticamente en os.Open para ayudar a que el comportamiento sea menos sorprendente para los usuarios, en particular O_CLOEXEC (cerrar al ejecutar). Cualquier programa que se estropee en Windows debido a la configuración de este indicador en os.Open también se estropearía necesariamente en Unix, ¿verdad?

No sé a qué te refieres aquí. O_CLOEXEC en Windows significa evitar que los identificadores de archivos escapen al proceso secundario. Y O_CLOEXEC siempre está configurado en os.Open en Windows. Por lo tanto, ningún proceso secundario en Windows puede acceder a ningún archivo abierto con os.Open. ¿Y qué? Suena razonable.

¿Esos programas no estarían rotos en Unix ya?

no entiendo tu pregunta

¿Tiene ejemplos específicos de programas reales (o probables) que fallarían?

No sé, si tengo tales programas. Es imposible decirlo. No recuerdo todos los programas que he escrito. No recuerdo lo que hacen.

Pero podría imaginar que un servicio de Windows abre un archivo predefinido, digamos c:\mysvc.txt , y lo mantiene abierto. Imagine otro programa que verifique que el servicio esté activo y reinicie el servicio, si es necesario. Este otro programa puede intentar eliminar c:\mysvc.txt y, si el archivo desaparece, puede suponer con seguridad que el proceso de servicio está muerto.

Además, me imagino, algunos programas podrían querer evitar que los usuarios eliminen o muevan sus archivos: https://stackoverflow.com/questions/11318663/prevent-a-user-from-deleting-moving-or-renaming-a-file

Alex

Aquí hay una nueva implementación de puntos de datos de Go en Windows:
Win8 se envió hace 7 años.
Este error de Go que afecta a Win8+ se informó hace solo 5 meses: #31528

@rsc cualquier programa que espera obtener un error al intentar cambiar el nombre/eliminar un archivo abierto con Go no funciona en Unix. Por ejemplo, el escenario c:\mysvc.txt Alex.

Si uno se desarrolla en Windows y se implementa en Linux/etc. (o viceversa), esto podría ser una fuente de errores.

Nuestro caso de uso es que tenemos un registrador que registra en un archivo.
El archivo de registro admite la rotación (p. ej., escriba a continuación si el archivo de registro es > maxSize y luego gire).
También tenemos lectores que abrirán estos archivos y los mantendrán abiertos mientras se leen.

Sin siquiera la capacidad de configurar FILE_SHARE_DELETE no podemos realizar una rotación (renombrar y/o eliminar) mientras haya lectores activos.

@mattn :

No se puede negar la posibilidad de que haya un usuario que implemente un procesamiento exclusivo utilizando el comportamiento os.Open actual de Go.

Eso puede ser, pero dicho código es incorrecto en Unix de todos modos. Package os está destinado a ser una interfaz portátil. También estamos más interesados ​​en los programas _reales_ que en las posibilidades.

@alexbrainman :

No dijo que las herramientas de desarrollo de Microsoft en Windows no proporcionan este indicador de forma predeterminada. Ver mi comentario #32088 (comentario)

Sí, pero eso es C; estamos hablando de Ir. No estamos imitando las API de C en general. Estamos tratando de proporcionar una abstracción mayormente portátil en el paquete os. El punto sobre O_CLOEXEC fue que en los sistemas Unix os.Open establece O_CLOEXEC aunque no esté configurado de forma predeterminada en la llamada C correspondiente. Nosotros _sí_ tratamos de proporcionar valores predeterminados útiles que se comporten aproximadamente de la misma manera en todos los sistemas.

En cuanto a la compatibilidad con Go 1, parece correcto cambiar el comportamiento de una esquina en un sistema operativo para alinearse mejor con otros sistemas operativos. No prometemos preservar todas las incompatibilidades entre sistemas para siempre.

Los usuarios de Windows que insisten en el comportamiento específico de Windows siempre tienen la opción de usar el paquete syscall, lo que dejaría en claro que el comportamiento al que se llega realmente solo se aplica a Windows.

Nuevamente, ¿tiene ejemplos concretos de programas reales que fallarían?

@ cpuguy83 , gracias por el caso de uso del mundo real de un programa que sería ayudado.

@rsc Como dices, aún no he descubierto los efectos de ruptura de FILE_SHARE_DELETE. Del mismo modo, no se ha encontrado ninguna confirmación de que este cambio pueda implementar logrotate.

Sin embargo, una vez que Go haya incluido este cambio que puede eliminar el archivo, el usuario esperará este comportamiento aunque Go no pueda implementar logrotate en Windows con este cambio. Así que tenemos que pensar cuidadosamente acerca de este cambio. Básicamente, Windows no puede eliminar archivos que no estén marcados con FILE_SHARE_DELETE. Los usuarios pueden pensar que este cambio permitirá que Go elimine cualquier archivo. En particular, los archivos creados por os.NewFile con identificador pueden manipular los métodos de la estructura os.File aunque este indicador no esté establecido. El programador debe saber que el archivo puede eliminarse en Windows o no por sí mismo, creo. Prefiero un mecanismo que permita que FILE_SHARE_DELETE pase por bandera para os.OpenFile en lugar del valor predeterminado. Creo que se debe seguir el comportamiento predeterminado del sistema operativo.

Sí, pero eso es C; estamos hablando de Ir.

Y C# también. No investigué, pero estoy bastante seguro de que cualquier otra herramienta de desarrollo en Windows se comporta de esta manera.

En cuanto a la compatibilidad con Go 1, parece correcto cambiar el comportamiento de una esquina en un sistema operativo para alinearse mejor con otros sistemas operativos.

Es un caso de esquina para usuarios que no son de Windows. Para los usuarios de Windows, este comportamiento es estándar.

Nuevamente, ¿tiene ejemplos concretos de programas reales que fallarían?

Considere mi ejemplo de servicio sobre concreto.

@ cpuguy83 , gracias por el caso de uso del mundo real de un programa que sería ayudado.

@ cpuguy83, ¿ puede decir más sobre su problema? Me gustaría ver cómo este cambio resolverá su problema, por lo que me gustaría ver un código real. Si puede proporcionar algún programa pequeño, que ahora está roto, pero que funcionará una vez que se resuelva este problema, se lo agradeceríamos. No tiene que construir, pero al menos debería proporcionar lo suficiente para que todos puedan juzgar. Gracias.

Básicamente, Windows no puede eliminar archivos que no estén marcados con FILE_SHARE_DELETE. Los usuarios pueden pensar que este cambio permitirá que Go elimine cualquier archivo.

Estoy de acuerdo, este es un buen punto. Solo se pueden eliminar los archivos abiertos por los programas Go. Pero todos los demás archivos abiertos por programas que no sean de Go seguirán siendo imborrables. Eso hará que os.Remove sea impredecible: a veces funcionará y otras veces no.

Alex

Tengo un código que llama a un syscall.Open() parcheado que cambia el nombre de los archivos abiertos. Así es como implementa la rotación de registros; funciona.

// 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

Pero no necesitabas que nadie te lo dijera :-/

Naturalmente, un os.File de os.NewFile() podría tener muchas propiedades diferentes a las de os.Create/Open/OpenFile(), eso es una característica, no una sorpresa.

Usted afirma que el comportamiento fopen() de Microsoft C es el comportamiento predeterminado de Windows; eso no es cierto. No existe un modo de uso compartido predeterminado CreateFile(); no es FILE_SHARE_READ | FILE_SHARE_WRITE .

Usted afirma que los programas de Windows implementados comúnmente asumen que los __programas de otros proveedores__ siempre abren archivos de una manera específica. Si ese fuera el caso, Microsoft no nos habría pedido que habilitáramos file_share_delete de forma predeterminada.

@redimprov

Pero no necesitabas que nadie te lo dijera :-/

Quiero saber que el proceso externo puede eliminar y cambiar el nombre del archivo correctamente en cualquier caso.

Lo que quiero saber es: después de incluir ese cambio, el archivo de registro se genera desde Go, el logrotate ejecutado por el proceso externo funciona bien y funciona como producción lista sin ningún error.

@alexbrainman

Desafortunadamente, el código es complicado, pero este es un código del mundo real: https://github.com/moby/moby/blob/master/daemon/logger/loggerutils/logfile.go

Actualmente, la mitigación propuesta es simplemente bifurcar os.OpenFile y syscall.Open: https://github.com/moby/moby/pull/39974/files

Para ser claros, solo tener la opción de pasar FILE_SHARE_DELETE desde OpenFile o incluso a syscall.Open sería suficiente para manejar nuestro caso.

Desafortunadamente, el código es complicado, pero este es un código del mundo real: https://github.com/moby/moby/blob/master/daemon/logger/loggerutils/logfile.go

Actualmente, la mitigación propuesta es simplemente bifurcar os.OpenFile y syscall.Open: https://github.com/moby/moby/pull/39974/files

@ cpuguy83 gracias por la

Desafortunadamente, mi breve vistazo no proporciona suficiente información para juzgar si este problema actual es la solución. Tendría que comenzar con repro (probablemente este https://github.com/moby/moby/issues/39274#issuecomment-497966709) y continuar desde allí. No estoy seguro de cuándo tendré tiempo para gastar en esto. Si tienes una sugerencia mejor que mi plan, házmelo saber.

Alex

A continuación se muestra una demostración funcional de la práctica estándar de rotación de registros, con 100 gorutinas, cada una de las cuales registra 50 líneas a intervalos de 0,1 a 2 segundos cada vez que abren el archivo de registro, y otra gorutina que rota el registro a intervalos de 1 minuto. Ejecuté esto durante varias horas ayer en una computadora portátil Win7 sin errores.

Está construido con un parche para syscall.Open() que habilita FILE_SHARE_DELETE; falla de otra manera. Se podría inventar un esquema de registro más complejo sin esta bandera, pero hay muchos otros usos para ella; mi propio código cambia el nombre de los archivos abiertos por diferentes razones.

@rsc , creo que esto completa el caso de su sugerencia, si todavía hubiera alguna duda. (Y como se indicó anteriormente, publiqué repetidamente en golang-dev y golang-nuts solicitando proyectos que dependan del comportamiento actual, sin resultados).

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
      }
   }
}

Parche a 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 El proceso externo no cambia el nombre del archivo. Si el cambio de nombre del archivo se realiza en el mismo proceso, hay otras formas de implementarlo.

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

A continuación se muestra una demostración funcional de la práctica estándar de rotación de registros

Gracias por tu ejemplo, @networkimprov . Puedo ver ahora lo que estás tratando de hacer.

El único problema con su código es que, si algún proceso externo abre archivos en los que está escribiendo sin FILE_SHARE_DELETE, entonces tendría el mismo problema que tiene ahora, independientemente de los cambios que hagamos en Go.

@cpuguy83 No @networkimprov me da una imagen de lo que está intentando hacer.

Alex

Alex, uno puede repetir el intento de cambio de nombre si se permite el acceso externo, o restringir el archivo de registro activo si no.

Mattn, el cambio de nombre de rotación generalmente se realiza mediante un proceso separado.

Parece que no hay un consenso claro aquí. Los desarrolladores con experiencia en Unix parecen estar a favor de esto, pero dos de nuestros desarrolladores de Windows más activos ( @alexbranman y @mattn) no lo están. El cambio también unificaría realmente la última versión de Windows que tiene el nuevo comportamiento similar a Unix para FILE_SHARE_DELETE.

Puede valer la pena volver a revisar este tema en unos años, para ver qué tan ampliamente disponible está el comportamiento de la nueva bandera y si otros lenguajes han convergido en un comportamiento común. Si alguien quiere presentar un nuevo problema en, digamos, dos años más o menos para repensar esto, estaría bien.

Pero por ahora, dada la falta de consenso, esto parece un declive probable .

Dejando abierto durante una semana para comentarios finales.

¿Hay controversia sobre simplemente hacer que la opción esté disponible?

Si estamos buscando más voces para intervenir, definitivamente estaría a favor de hacer _algo_. En este momento, la única solución es copiar literalmente un montón de código de la biblioteca estándar de Go, cambiar una línea y luego mantener esa bifurcación para siempre. Cada implementación de un monitor de registro o "cola" en vivo eventualmente parece hacer esto.

Para ver un ejemplo, consulte https://github.com/elastic/beats/blob/master/libbeat/common/file/file_windows.go#L85 -L103

Si seguí correctamente, esta propuesta en realidad tiene dos partes:

  1. Permitir el uso de la bandera FILE_SHARE_DELETE al abrir un archivo -
    la implementación debería ser fácil y segura, el desarrollador tendría que
    solicitar este modo al abrir un nuevo archivo
  2. Activar esta marca de forma predeterminada: puede ser arriesgado y solo bien soportado
    al usar la última compilación de Windows 10.

Si todos estuvieran de acuerdo en 1, tal vez podamos permitir la bandera cuando el desarrollador
solicítelo explícitamente por ahora y vuelva a visitar 2 en un par de años.

El miércoles 2 de octubre de 2019 a las 19:32 Mark Dasher, [email protected] escribió:

Si estamos buscando más voces para intervenir, definitivamente estaría a favor.
de hacer algo En este momento, la única solución es copiar literalmente un
montón de código de la biblioteca estándar de Go, cambie una línea y luego
mantener ese tenedor para siempre. Cada implementación de un monitor de registro o en vivo
"tail" finalmente parece hacer esto.

Para ver un ejemplo, véase
https://github.com/elastic/beats/blob/master/libbeat/common/file/file_windows.go#L85 -L103


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4VFQLSYVI66ENT6G4LQMTLJ7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAFRRIQ#issuecomment-537598
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/ABNEY4U5L3WFZYNIUOSEY2TQMTLJ7ANCNFSM4HNPNYIA
.

Definitivamente a favor de _al menos_ tener 1. (Permitir el uso de la bandera FILE_SHARE_DELETE al abrir un archivo)

Solo para revisar mi propuesta anterior y hacer un seguimiento con una implementación de ejemplo :

El syscall.FILE_SHARE_DELETE bandera encajaría muy bien en el os.OpenFile 's flag parámetro y proporcionaría un mecanismo trivial para habilitar este comportamiento bajo demanda. No choca con ningún otro indicador os.O_* en Windows (y esto se puede aplicar/diseñar) y proporciona una forma idiomática de especificar el comportamiento de apertura de archivos específico del sistema (en la medida en que os ' La interfaz orientada a POSIX puede considerarse idiomática en Windows). Esta es la misma ruta que ya se usa para pasar el comportamiento de apertura de archivos específico del sistema en plataformas POSIX (por ejemplo, syscall.O_DIRECTORY en Linux).

Incluso si se toma la decisión de no activarlo de forma predeterminada (que creo que es la decisión correcta), estoy de acuerdo en que la bandera tiene sus casos de uso (incluidos los señalados por @networkimprov) y que sería útil estar capaz de encenderlo sin tener que recurrir a llamadas CreateFileW y copiar el código repetitivo.

Considere la alternativa [1] anterior que permite a los desarrolladores la opción de configurar FILE_SHARE_DELETE cuando sea necesario y evitar fragmentos bifurcados del código de la biblioteca estándar de Go.

__Rust__ también establece este indicador de forma predeterminada y le permite _desactivar_ cualquiera de las opciones de FILE_SHARE_*.
https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html

@rsc , ¿puedo preguntar cuáles de los argumentos técnicos presentados en contra de la propuesta no fueron refutados?

@networkimprov después de leer toda la propuesta y la discusión, creo que debe cerrar esto y volver a abrir un nuevo problema explícitamente sobre la adición de un nuevo indicador específico de Windows al paquete ossyscall. Esto garantizaría que el nuevo comportamiento sea de aceptación en lugar de exclusión. La propuesta actual implica cambiar el código existente, lo que preocupa a la gente.

@rsc , ¿puedo preguntar cuáles de los argumentos técnicos presentados en contra de la propuesta no fueron refutados?

Lo siento, pero no es así como funciona el proceso de propuesta . No basta con "refutar" los argumentos de otras personas. "El objetivo del proceso de propuesta es llegar a un consenso general sobre el resultado de manera oportuna". No hay un consenso claro sobre el camino a seguir aquí. Se han hecho argumentos técnicos y no convencieron a los desarrolladores clave de Go que han hecho contribuciones significativas al puerto de Windows (a saber, @alexbrainman y @mattn). Además de la falta de un consenso claro, no hay una señal clara de urgencia para hacer algo hoy: Go funcionó bien en Windows durante casi 10 años antes de que se presentara este problema. Según tengo entendido, dejar todo en paz significa que Go seguirá funcionando tan bien como siempre.

Archivé #34681 para proporcionar una forma más fácil de abrir archivos con FILE_SHARE_DELETE en el caso (aún probable) de que este sea rechazado. Parecía más útil comenzar un nuevo hilo limitado a esa idea que continuar con este, que se ha hecho muy largo.

@rsc , antes de rechazar esto, escuchemos lo que Alex piensa de su propuesta O_ALLOW_DELETE.

Al principio de esta discusión, estaba en contra de un nuevo indicador os.OpenFile() que establece file_share_delete, por las mismas razones por las que no estuvo de acuerdo con su sugerencia de os.Create/Open/OpenFile(). Le preocupa que otros programas supongan que nadie abre archivos de esa manera, porque MSVC fopen() no puede hacerlo. Esos programas (aún no especificados) romperán los programas Go que activan la bandera.

Alex, se puede repetir el intento de cambio de nombre si se permite el acceso externo...

Si está preparado para repetir el cambio de nombre, no necesita cambiar nada en el código Go repo. Su programa funcionará tan bien como funciona ahora.

En este momento, la única solución es copiar literalmente un montón de código de la biblioteca estándar de Go, cambiar una línea y luego mantener esa bifurcación para siempre.

Creo que copiar el código en un paquete separado está bien. Mientras investigaba https://github.com/moby/moby/pull/39974 tuve la misma idea. No creo que haya mucho código allí para mantener. De hecho, implementé solo eso.

https://github.com/alexbrainman/goissue34681

Siéntase libre de copiar o usar tal cual. Tal vez, dado que hay tanto interés en esta funcionalidad, en realidad crea un paquete adecuado que otros pueden compartir y usar. De esta manera puede mantenerlo actualizado y corregir errores.

@rsc , antes de rechazar esto, escuchemos lo que Alex piensa de su propuesta O_ALLOW_DELETE.

Por favor, inténtalo

https://github.com/alexbrainman/goissue34681

primero. Si no está satisfecho por alguna razón, podríamos discutir la adición de una nueva funcionalidad al repositorio de Go.

Gracias.

Alex

Estoy realmente decepcionado por esta respuesta.

Copiar esta gran franja de código, que en gran medida ni siquiera se entiende (como
en por qué esto está haciendo lo que está haciendo) solo para que podamos agregar una sola opción
que el propio sistema admite, solo que Go, lo más probable es que supongo que no
incluso intencionalmente, no proporciona un medio para pasar la opción incluso a
syscall.Open parece una situación ridícula.

¿Por qué tengo que volver a escribir syscall.Open para pasar esta opción?

El sábado 5 de octubre de 2019 a las 18:43, Alex Brainman [email protected] escribió:

Alex, se puede repetir el intento de cambio de nombre si se permite el acceso externo...

Si está preparado para repetir el cambio de nombre, no necesita cambiar nada
en el código Go repo. Su programa funcionará tan bien como funciona ahora.

En este momento, la única solución es copiar literalmente un montón de código de
Biblioteca estándar de Go, cambie una línea y luego mantenga esa bifurcación para siempre.

Creo que copiar el código en un paquete separado está bien. Mientras investigaba
moby/moby#39974 https://github.com/moby/moby/pull/39974 Tengo lo mismo
ocurrencia. No creo que haya mucho código allí para mantener. En efecto, yo
implementado solo eso

https://github.com/alexbrainman/goissue34681

Siéntase libre de copiar o usar tal cual. Tal vez, dado, hay tanto interés en
esta funcionalidad, en realidad crea un paquete adecuado que se puede compartir
y usado por otros. De esta manera puede mantenerlo actualizado y corregir errores.

@rsc https://github.com/rsc , antes de rechazar esto, escuchemos qué
Alex piensa en tu propuesta O_ALLOW_DELETE.

Por favor, inténtalo

https://github.com/alexbrainman/goissue34681

primero. Si no está satisfecho por alguna razón, podríamos discutir la adición de nuevos
funcionalidad para ir al repositorio.

Gracias.

Alex


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=AAGDCZXHULQEMHAPTO6ZUJTQNFGE7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAN7QIQ#issuecomment-53870338
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AAGDCZWYUNMCTAGO567AV73QNFGE7ANNCFSM4HNPNYIA
.

>

  • Brian Goff

Estoy realmente decepcionado por esta respuesta.

Lamento que te sientas así. Pero, ¿realmente intentaste usar mi paquete?

Alex

@rsc , dado que Alex también se opone a un indicador os.OpenFile(), ¿no deberíamos hacer nada?

¿Qué tal poner esta función detrás de un indicador de compilación?

En cuanto a si "Go funcionó bien en Windows durante casi 10 años", definitivamente no lo hizo, en caso de que necesitara cambiar el nombre de un archivo abierto. (También se rompió en las computadoras portátiles con Windows 8/10 durante los últimos 7 años).

Ya tengo mi propia bifurcación de os.OpenFile y syscall.Open en moby/moby.

¿Por qué estamos siendo tan desdeñosos aquí?

El martes 8 de octubre de 2019 a las 01:47 Alex Brainman [email protected] escribió:

Estoy realmente decepcionado por esta respuesta.

Lamento que te sientas así. Pero, ¿realmente intentaste usar mi paquete?

Alex


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=AAGDCZRV72GY4IJQJVJWMYTQNRCIHA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEATNATA#issuecomment-53941466
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AAGDCZRNYSMIE6BX77XJVWDQNRCIHANCNFSM4HNPNYIA
.

>

  • Brian Goff

Sugeriría que se rechazara esta propuesta específica. No vamos a presentar un montón de errores de seguridad de TOCTOU para los usuarios de Windows que confiaron en el comportamiento existente de os.OpenFile.

La pregunta más importante aquí es, "¿cómo podemos exponer a los usuarios de Go la gran variedad de banderas interesantes para las funciones CreateFile y NtCreateFile de Windows?" Espero que podamos abordarlos en diferentes propuestas, pero ciertamente no encendiéndolos todos de forma predeterminada como sugiere aquí.

@ zx2c4 ¿

Ha pasado una semana desde https://github.com/golang/go/issues/32088#issuecomment -537590826, y todavía no hay un consenso claro, lo que significa que debemos rechazar esto.

Rechazado.

He publicado un resumen de opciones con pros y contras en https://github.com/golang/go/issues/34681#issuecomment -565853605.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

bbodenmiller picture bbodenmiller  ·  3Comentarios

natefinch picture natefinch  ·  3Comentarios

OneOfOne picture OneOfOne  ·  3Comentarios

longzhizhi picture longzhizhi  ·  3Comentarios

rsc picture rsc  ·  3Comentarios