Go: proposition: os: Create/Open/OpenFile() définit FILE_SHARE_DELETE sur Windows

Créé le 16 mai 2019  ·  194Commentaires  ·  Source: golang/go

Sous Linux & MacOS, nous pouvons écrire ceci ; sur Windows, il échoue avec une "violation de partage":

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 vous développez sur Windows et déployez sur Linux, etc., et que votre code repose sur ce comportement _non documenté_ GOOS=windows de os.Rename() & .Remove(), il est cassé et peut-être vulnérable. Notez que le package "os" contient _quinze mentions_ d'autres comportements spécifiques à Windows.

Pour résoudre ce problème, syscall.Open() sur https://golang.org/src/syscall/syscall_windows.go#L272
a besoin de sharemode |= FILE_SHARE_DELETE

Microsoft recommande que cela soit défini par défaut : https://github.com/golang/go/issues/32088#issuecomment -502850674
Rust en a fait une valeur par défaut : https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html
Mingw-w64 en a fait une valeur par défaut il y a sept ans :
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858
Erlang en a fait une valeur par défaut il y a plus de six ans : erlang/ otp@0e02f48
Python n'a pas pu l'adopter en raison des limitations du runtime MSVC : https://bugs.python.org/issue15244

~ Par conséquent, syscall.Open() doit utiliser file_share_delete par défaut et syscall doit fournir les deux :
a) un commutateur global pour le désactiver (pour toutes les applications existantes qui dépendent de son absence), et
b) un indicateur à utiliser avec os.OpenFile() pour le désactiver sur un descripteur de fichier spécifique.~

Mise à jour après https://github.com/golang/go/issues/32088#issuecomment-532784947 par @rsc :
a) os.Create/Open/OpenFile() doit toujours activer file_share_delete sous Windows,
b) syscall.Open() sous Windows doit accepter un indicateur qui active file_share_delete, et
c) syscall sur Windows devrait exporter une constante pour le nouveau drapeau.

Les docs pour os.Remove() devraient également noter que pour réutiliser un nom de fichier après avoir supprimé un fichier ouvert sous Windows, il faut faire : os.Rename(path, unique_path); os.Remove(unique_path) .

Gagner des documents d'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

S'il y a une raison de ne pas le faire, cela doit être documenté dans os.Remove() & .Rename().

cc @alexbrainman
@gopherbot ajoute OS-Windows

FrozenDueToAge OS-Windows Proposal Proposal-FinalCommentPeriod release-blocker

Commentaire le plus utile

Je vais essayer de représenter ici le point de vue de l'équipe Windows... nous préférons toujours définir FILE_SHARE_DELETE, sans aucune option. En général, pour faciliter le portage vers/depuis Windows, nous préférerions un comportement cohérent avec Linux, et je ne vois pas pourquoi les utilisateurs ou les logiciels Windows préféreraient suffisamment le comportement par défaut !FILE_SHARE_DELETE pour que ce soit une exception à la règle.

Une note intéressante, contrairement au commentaire de

En d'autres termes, si vous exécutez le programme de test de mattn et la séquence de del, dir, etc. sur la dernière version de Windows, vous verrez le fichier disparaître de l'espace de noms immédiatement après la suppression du fichier, pas après la fermeture du programme de test. Tout comme Linux.

Ainsi, même dans Windows lui-même, avec son risque de compatibilité d'application considérablement plus important, nous apportons de petits changements pour faciliter le portage de logiciels non Windows vers Windows. J'encourage vraiment Go à faire de même.

Tous les 194 commentaires

/cc @alexbrainman

syscall.Open() sur https://golang.org/src/syscall/syscall_windows.go#L272
devrait utiliser sharemode := ... | FILE_SHARE_DELETE

Pourquoi devrait-il?

Alexis

FILE_SHARE_DELETE active l'exemple de code ci-dessus, qui fonctionne sous MacOS et Linux mais échoue sous Windows. Il est également nécessaire pour :

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 active l'exemple de code ci-dessus, qui fonctionne sous MacOS et Linux mais échoue sous Windows.

Pourquoi n'ajustons-nous pas le code MacOS et Linux à la place ?

Il est également nécessaire pour :

Je ne comprends pas ce que vous essayez de dire.

Alexis

@networkimprov Vous devez appeler Remove après Close() sous 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 lorsque vous effectuez des E/S de fichiers fiables (comme pour les bases de données), il est de pratique courante de créer un fichier avec un nom temporaire, de l'écrire, de le fsync et de le renommer.

Les fichiers ouverts peuvent être renommés ou supprimés par défaut sous Unix. Cela semble être un oubli que l'indicateur Windows pour cette capacité ne soit pas défini. Je doute que nous convainquions l'équipe Go de changer la façon dont cela fonctionne pour Linux et MacOS :-)

@mattn veuillez appliquer le correctif que j'ai décrit et essayez le code que j'ai publié.

Je doute que nous convainquions l'équipe Go de changer la façon dont cela fonctionne pour Linux et MacOS :-)

Je vais bien comme les choses sont maintenant.

Alexis

Existe-t-il une justification pour l'omission de cette fonctionnalité commune dans Windows ?

Pouvez-vous fournir un commutateur dans syscall_windows.go afin que nous puissions sélectionner le comportement Unix au démarrage du programme ?

Je suis d'accord pour que nous ajoutions de nouvelles API ou de nouveaux indicateurs. Mais j'ai une objection à changer le comportement actuel. Étant donné que FILE_SHARE_DELETE n'est pas le même que le comportement 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;
}

Complétez ce code sous Windows et essayez de l'exécuter en tant que test.exe . Pendant que cette application attend une touche, ouvrez un nouveau cmd.exe, supprimez "test.txt" comme suit.

image

Le fichier peut être supprimé mais y rester tant que le processus existe. Donc, ce changement ne fonctionnera pas bien pour vos attentes.

Je me rends compte que l'entrée du répertoire n'est pas supprimée mais les docs disent

Les appels suivants à CreateFile pour ouvrir le fichier échouent avec ERROR_ACCESS_DENIED.

Je ne comprends donc pas votre journal d'écran.

Quoi qu'il en soit, un commutateur comme celui-ci serait bien:

syscall.OpenFileShareDelete = true

J'aimerais mentionner que j'ai déjà été mordu par cela au travail.

En gros on avait :

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

// lots of code

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

Le code fonctionnait correctement sur nos plates-formes CI basées sur Unix, mais a explosé sous Windows.

En supposant qu'il n'y ait pas d'effets secondaires étranges, ce serait bien si les choses fonctionnaient .

IIRC, nous avons apporté quelques ajustements au comportement de GOOS=windows & plan9 dans le passé pour correspondre plus étroitement à la sémantique Unix. Cela ne me dérangerait pas d'en faire un autre cas de ce genre si la sémantique est suffisamment proche. Le commentaire de @mattn , cependant, suggère que le comportement n'est pas assez proche, donc cela pourrait ne pas en valoir la peine.

Je ne veux pas voir une option globale, cependant. Cela ressemble à un cauchemar de débogage.

@bradfitz
Ce ne serait probablement pas le cas, veuillez vous référer à mon commentaire ici
https://groups.google.com/forum/#!topic/golang -dev/R79TJAzsBfM
ou si vous préférez, je peux copier le contenu ici, car il existe également une solution possible, bien que je ne l'implémente pas comme comportement par défaut , car cela ne serait pas cohérent avec le comportement des programmes Windows, mais plutôt une solution de contournement pour créer une croix similaire -OS expérience.

@guybrand écrit dans le fil de discussion golang-dev :

mon programme écrit un fichier appelé "my.data", puis appelle un autre programme et n'attend pas qu'il se termine ... ce programme disons télécharge ce fichier qui prend 10 secondes (appelons cet autre programme "uploader") .
...
Lorsque mon programme appelle .Remove sous Windows (FILE_SHARE_DELETE est activé) :
...
l'uploader recevrait une erreur lui indiquant que le fichier est supprimé (probablement EOF).

En supposant que "uploader" ouvre le fichier avant que "mon programme" n'appelle os.Remove(), je pense que vous avez contredit la documentation de l'API Win :

_DeleteFile marque un fichier pour suppression à la fermeture. Par conséquent, la suppression de fichier ne se produit pas tant que le dernier handle du fichier n'est pas fermé. Les appels suivants à CreateFile pour ouvrir le fichier échouent avec ERROR_ACCESS_DENIED._

Concernant "l'association d'un _open_osfhandle() du CreateFile à un _fdopen", pouvez-vous pointer vers un exemple de code ?

Si nous ajoutons FILE_SHARE_DELETE, de nombreux programmeurs l'utiliseront par erreur pour simuler le comportement d'Unix. Dans ce cas, vous pouvez créer une fonction séparée par des contraintes de construction pour chaque système d'exploitation. Et il devrait retourner os.NewFile() , utilisez FILE_SHARE_DELETE sous Windows.

créer une fonction séparée par des contraintes de construction pour chaque système d'exploitation... renvoyer os.NewFile(), utiliser FILE_SHARE_DELETE sous Windows

Pour conserver la fonctionnalité de os.OpenFile(), ce plan semble nécessiter la réimplémentation de tous :

os.OpenFile()
openFileNolog()
fichier ouvert()
openDir()
nouveau fichier()
fixLongPath()
syscall.Open()
makeInheritSa()

@improvisationréseau

@guybrand écrit dans le fil de discussion golang-dev :

mon programme écrit un fichier appelé "my.data", puis appelle un autre programme et n'attend pas qu'il se termine ... ce programme disons télécharge ce fichier qui prend 10 secondes (appelons cet autre programme "uploader") .
...
Lorsque mon programme appelle .Remove sous Windows (FILE_SHARE_DELETE est activé) :
...
l'uploader recevrait une erreur lui indiquant que le fichier est supprimé (probablement EOF).

En supposant que "uploader" ouvre le fichier avant que "mon programme" n'appelle os.Remove(), n'avez-vous pas contredit la documentation de l'API Win ?

DeleteFile marque un fichier pour suppression à la fermeture. Par conséquent, la suppression de fichier ne se produit pas tant que le dernier handle du fichier n'est pas fermé. Les appels suivants à CreateFile pour ouvrir le fichier échouent avec ERROR_ACCESS_DENIED.

Concernant "l'association d'un _open_osfhandle() du CreateFile à un _fdopen", pouvez-vous pointer vers un exemple de code ?

Je ne vois pas de contradiction avec l'API Windows, veuillez indiquer ce qui ressemble à une contradiction.

En ce qui concerne un exemple de code, j'ai un peu cherché sur Google et j'ai trouvé ceci :
http://blog.httrack.com/blog/2013/10/05/creating-deletable-and-movable-files-on-windows/

MAIS
Veuillez noter que cela ne vous donnera qu'un comportement de type nix en interne, tout autre programme externe fonctionnant avec le cassera (car il utilise à 99% l'API Windows standard

@improvisationréseau
Si vous voulez dire "cela ressemble à "dir my.data" afficherait le fichier, pour autant que je me souvienne, c'était le comportement jusqu'à ce que Windows .... XP ou 7 (je ne me souviens pas lequel) et a changé depuis (peut-être que l'explorateur cache juste en quelque sorte) - Je peux retester la prochaine fois que je lance mon environnement Windows (cela arrive toutes les quelques semaines...)

Si vous voulez dire "cela ressemble à d'autres processus avec un handle sur le fichier seraient toujours capables de lire et d'écrire dans le fichier", je parierais que le déjeuner la seconde où vous lisez écrire dans un tel fichier, vous obtenez une erreur de style "EOF" - mais encore une fois besoin de confirmer pour être 100% positif.

Dans les deux cas, mon point serait (à ce stade - je prends "côté" dans le point "natif comme" vs " os-agnostique") - même si vous implémentez une solution de style _fdopen, vous obtiendriez une incohérence entre votre service et tous les autres exécutables avec lesquels vous collaborez, de sorte que l'utilisation ne peut être qu'en interaction avec d'autres exécutables go (ou des services rares qui utilisent les fd directement).

En d'autres termes - votre application serait "l'enfant le plus intelligent de la classe" - qu'aucun autre enfant ne peut comprendre.
Deux exemples parmi tant d'autres auxquels je peux penser :
Entrées :
Mon application télécharge un fichier, l'antivirus identifie son harfull et le supprime/le met en quarantaine (== en quelque sorte le renommer), si j'utilise fd - mon application serait toujours capable de faire ce qu'elle veut avec (ce qui est coll, mais peut se terminer jusqu'à frapper un virus...)
sorties :
mon application dirige un fichier vers un autre service ("téléchargement") et le supprime, j'ai même pris la peine d'écrire un testeur pour voir que tout fonctionne bien - et que le test réussit.
Maintenant, au lieu de mon go test, j'utilise filezilla, nous transférons, l'API dropbx, peu importe
il échouerait/ne se comporterait pas de la même manière que mon test fonctionne...

Pensez-vous toujours que changer ce comportement par défaut a du sens ?

Existe-t-il une justification pour l'omission de cette fonctionnalité commune dans Windows ?

Je n'ai jamais pensé à cette question. Je sais pas.

La façon dont les fichiers Go fonctionnent sous Windows est cohérente avec tous les autres outils de développement que j'ai utilisés dans ma vie. Cela me surprendrait si les fichiers Go fonctionnaient comme vous le proposez. Je soupçonne également que cela casserait de nombreux programmes existants.

Alexis

Je soupçonne également que cela casserait de nombreux programmes existants.

@alexbrainman J'ai également suggéré un commutateur pour l'activer, au lieu de modifier la valeur par défaut.

@bradfitz il y a syscall.SocketDisableIPv6, ce n'est pas vraiment différent d'un indicateur pour ajuster le comportement de syscall.Open().

Depuis les états syscall_windows*.go
func Open(chemin chaîne, mode int, perm uint32) (fd Handle, err error) {
....
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE)

et type_windows*.go a
FILE_SHARE_DELETE = 0x00000004

Tout est tout à fait prêt, la seule question est de savoir comment implémenter le drapeau.
Je n'aime pas l'option globale et elle n'est pas explicite, et même si un seul développeur peut se souvenir qu'il a défini os.DeleteUponLastHandleClosed = 1, ce n'est pas une bonne pratique pour les développeurs à long terme ou multiples.
D'autres options consisteraient soit à définir un numéro réservé spécifique pour les drapeaux, comme dans :
fd, err := os.OpenFile(chemin, os.O_RDWR|os.O_CREATE|os.DELETE_WHEN_FREED, 0600)
alors que DELETE_WHEN_FREED peut même être égal à 0 pour env. autres que les fenêtres,

Une autre option serait d'utiliser le paramètre perm, qui n'est pas pris en charge pour Windows, cela peut devenir un peu gênant
fd, err := os.OpenFile(chemin, os.O_RDWR|os.O_CREATE, 777)
777 est réservé, nous aurions donc besoin d'un 1777 ou -777 pour prendre en charge les deux systèmes
faire est lisible DELETE_WHEN_FREED | 777

la dernière option à laquelle je peux penser est os.OpenDeletableFile(
Quel serait os.OpenFile sur nix et
tour
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
sur les fenêtres

toutes les solutions ci-dessus sont simples à mettre en oeuvre, un peu plus de temps pour tester (cross os), juste besoin d'un votant...

Une façon de prendre en charge ce comportement sans modifier les valeurs par défaut ou étendre la surface de l'API pourrait être d'accepter simplement syscall.FILE_SHARE_DELETE dans le paramètre flag de os.OpenFile sous Windows et de le plier dans le sharemode valeur. Étant donné que les indicateurs syscall.O_* (et donc os.O_* ) sur Windows utilisent de toute façon des valeurs composées , ils pourraient être conçus pour éviter d'entrer en collision avec les indicateurs spécifiques à Windows que l'on souhaitait inclure. Heureusement, ils évitent déjà les collisions avec syscall.FILE_SHARE_DELETE .

En tout état de cause, je m'opposerais fermement à la modification du comportement par défaut. Faire de FILE_SHARE_DELETE la valeur par défaut vous placerait dans la même position que les systèmes POSIX en termes d'impossibilité d'assurer une traversée du système de fichiers sans course. Le fait que ce drapeau soit facultatif est la raison pour laquelle Windows n'a pas besoin de l'équivalent de openat , renameat , readlinkat , etc.

Je n'ai pas vraiment réfléchi à ce que nous devrions faire ici, mais je tiens à préciser une chose :

Pouvez-vous fournir un commutateur dans syscall_windows.go afin que nous puissions sélectionner le comportement Unix au démarrage du programme ?

Nous ne le ferons pas. Cela rendrait impossible pour un même programme d'utiliser différents packages qui s'attendent à un comportement différent.

@havoc-io
Votre suggestion créerait des programmes sujets aux erreurs car syscall.FILE_SHARE_DELETE diffère du comportement std de POSIX.
Veuillez noter que les auteurs de syscall_windows*.go connaissaient le drapeau FILE_SHARE_DELETE (il y est) et ont décidé que son utilisation ne serait pas le comportement préféré.
Pourquoi?

prenons le code de Liam, il
différer fd.Fermer()
supprime/renomme
puis lecture/écriture

donc l'ordre de tri réel est
ouvert
marquer comme supprimé
lecture/écriture (et probablement aussi routine de lecture/écriture croisée/processus croisé)
Fermer

Le comportement actuel de Windows alerte le développeur "c'est faux", le développeur pousserait probablement la suppression pour qu'elle soit également différée
Si vous ajoutez FILE_SHARE_DELETE, Windows vous permettrait de "marquer comme supprimé même si le fichier est ouvert" MAIS un autre processus/goroutine s'exécutant simultanément qui essaierait d'accéder au fichier entre la suppression et la fermeture (ce qui est probablement la tâche la plus longue dans ce code ) échouerait
Résultat : au lieu d'un comportement cohérent « vous pouvez faire ça » - vous obtiendrez un « parfois - surtout quand il y a beaucoup d'utilisateurs simultanés, cela échoue et je ne sais pas pourquoi.
Donc, vous ne résolvez pas le problème, vous gérez simplement un cas d'utilisation spécifique sur lequel le développeur "ne l'exécute qu'une seule fois"
Mon vote est pour le comportement cohérent bien sûr...

Comment est-ce diff de Posix?
Une fois marqué comme supprimé, le fichier n'est plus dans la table des fichiers, il n'existe que sous forme de fd, permettant à une autre routine/processus de créer un fichier avec le même nom.

Je pense que nous devrions arrêter de chercher une "même solution" car il y a des différences que nous ne résoudrons pas dans go (noms de fichiers sensibles à la casse ? :) nous laisserons Linux 5.2 faire ça...)

Au total, cela ressemble à la recherche d'une solution pour un fichier temporaire, si c'est le cas, Windows prend en charge GetTempFileName qui peut être considéré comme une solution standard pour un fichier qui est "utilisé puis mis à la corbeille".

Si, d'un autre côté, nous souhaitons permettre à un développeur de différer les suppressions dans Windows, c'est possible, voir mes suggestions ci-dessus, mais le développeur doit en assumer la responsabilité et comprendre la conscience - doit donc être un indicateur avec une bonne convention de nom.

L'utilisation principale de cette fonctionnalité est de renommer un fichier ouvert après l'avoir créé :

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

Je ne sais pas pourquoi vous ne voudriez pas ajouter un indicateur os.O_WRNDEL (renommer/supprimer les fenêtres ; un no-op sur d'autres plates-formes) et documenter les différences de ce mode avec Unix ; qu'un fichier supprimé reste dans son répertoire, mais ne peut pas être ouvert, jusqu'à os.Close(). Mentionnez également que déplacer le fichier vers un répertoire temporaire avant de le supprimer offre un comportement plus semblable à Unix.

@guybrand Je pense que vous comprenez peut-être mal ma proposition. Je ne préconise pas que FILE_SHARE_DELETE soit activé par défaut - en fait, je m'y oppose pour les raisons mentionnées dans le deuxième paragraphe de mon commentaire. Ma proposition concernait un mécanisme permettant aux utilisateurs d'opter pour le comportement FILE_SHARE_DELETE (fichier par fichier) tout en utilisant l'infrastructure de fichiers du package os . Cette proposition fournit un mécanisme pour le faire sans étendre la surface API d'un package.

Je ne sais pas pourquoi vous n'ajouteriez pas d'indicateur os.O_WRNDEL (renommer/supprimer les fenêtres ; un no-op sur d'autres plates-formes)

@networkimprov C'est essentiellement ce que je propose, sauf qu'il ne syscall.FILE_SHARE_DELETE . Le seul changement d'implémentation serait de surveiller ce drapeau dans syscall.Open sous Windows et d'ajouter des vérifications pour s'assurer qu'aucun ajout futur aux drapeaux syscall.O_* / os.O_* n'entre en collision avec ce drapeau. La documentation de os.OpenFile pourrait alors être mise à jour pour refléter l'acceptation de ce drapeau sur Windows.

@havoc-io désolé pour le malentendu, c'était mon plat à emporter de :
" ... pour simplement accepter syscall.FILE_SHARE_DELETE ... dans le sharemode calculé ..."

L'ajout de os.O_WRNDEL correspond à ma deuxième suggestion, en plus de ne pas être assez explicite pour le développeur en termes de "quel serait le comportement", peut-être que os.WADRNDEL - Windows autorise un renommage/suppression différé).

@alexbrainman J'ai également suggéré un commutateur pour l'activer, au lieu de changer la valeur par défaut

Je ne suis pas intéressé. Merci.

Alexis

@guybrand renommer un fichier ouvert n'est pas différé, j'ai vérifié.

Excusez-moi, je vous demande à nouveau quelle est votre suggestion ? supprimer le fichier d'ouverture ? renommer le fichier d'ouverture ? les deux?

Trois d'entre nous proposent maintenant un nouveau drapeau, par exemple

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)

Trois d'entre nous proposent maintenant un nouveau drapeau

Je pense qu'un nouveau drapeau est inapproprié, en particulier celui qui n'est pas facilement analysable par quelqu'un qui lit le code. L'utilisation de syscall.FILE_SHARE_DELETE serait beaucoup plus explicite et claire, signalant immédiatement au lecteur que quelque chose de spécifique à la plate-forme se produit.

Ceci est déjà autorisé sur les plateformes POSIX. Il existe de nombreux indicateurs open spécifiques à POSIX (et même spécifiques à la plate-forme) qui ne sont pas incorporés dans le package os . Par exemple, il ne serait pas logique d'ajouter quelque chose comme os._DEVTONLY pour soutenir Darwin O_EVTONLY drapeau, parce que vous pouvez juste passer le drapeau de la syscall forfait directement à os.OpenFile , par exemple :

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

Dans ce cas, l'indicateur spécifique à la plate-forme sera transmis à l'appel système open sous-jacent.

Si le comportement de FILE_SHARE_DELETE est vraiment quelque chose dont les gens ont besoin, alors je pense que la réponse est simplement de faire en sorte que os.OpenFile puisse de même enfiler des indicateurs jusqu'à l'appel sous-jacent CreateFileW sur Windows. Au moins pour FILE_SHARE_DELETE .

Un exemple d'implémentation peut être trouvé ici .

@improvisationréseau

Trois d'entre nous proposent maintenant un nouveau drapeau, par exemple

Qu'attendez-vous du drapeau?

@guybrand renommer un fichier ouvert n'est pas différé, j'ai vérifié.

D'accord, mais les deux ont besoin de FILE_SHARE_DELETE pour être autorisés pendant que le fichier est ouvert
Si vous faites référence à ma terminologie suggérée, nous pouvons
os.WARNDFDEL - Windows autorise le renommage et la suppression différée est un peu trop long, j'utiliserais moi-même un acronyme WINDOWS_ALLOW_OPENNED_FILE_RENAME_OR_DEFFERED_DELETE
est mieux OMI.

@guybrand Je ne vois vraiment pas l'intérêt d'ajouter un nouveau drapeau spécifique à la plate-forme avec un nom complexe et un comportement opaque au package os alors qu'un drapeau parfaitement bon pour cela existe depuis des décennies ( FILE_SHARE_DELETE ). Utiliser syscall.FILE_SHARE_DELETE comme indicateur pour cela est beaucoup plus clair et explicite. Comme je l'ai mentionné ci - os mais sont toujours acceptés par celui-ci.

syscall.FILE_SHARE_DELETE est Windows uniquement ; nous avons besoin de quelque chose qui est un no-op ailleurs. Peut-être devrait-il apparaître dans pkg os, avec un préfixe WIN ou WINDOWS ?

@mattn pls voir le patch ci-dessus de @havoc-io

nous avons besoin de quelque chose qui est un no-op ailleurs.

@networkimprov Le drapeau n'a pas besoin d' exister ailleurs. Votre code Windows ressemblera simplement à :

import (
    "syscall"
    "os"
)

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

Oui, ce code devra être bloqué sur la plate-forme, mais vous devrez probablement le faire de toute façon car le comportement avec ce drapeau ne correspondra pas à celui des systèmes POSIX. Ou, si vous voulez la commodité d'avoir le même code sur toutes les plateformes, vous pouvez définir votre propre constante (avec une valeur de syscall.FILE_SHARE_DELETE sur Windows et 0 sur les systèmes non Windows) et passer à os.OpenFile . C'est exactement ce que vous feriez si vous vouliez apporter un indicateur O_* spécifique os (par exemple O_EVTONLY sur Darwin ou O_ASYNC sous Linux).

Lorsque j'écris du code, je m'attends à ce qu'il soit multiplateforme, FILE_SHARE_DELETE n'existe pas dans syscall_* juste celui de Windows (anf ne sera donc pas compilé sur d'autres systèmes).

Pouvons-nous utiliser 0x4 const, comme :
FILE_SHARE_DELETE = 0x00000004

En plus d'être moche,
netbsd_arm
O_NDELAY = 0x4
O_NON BLOC = 0x4

Donc, l'utilisation de 0x4 créerait le code différemment dans netbsd_arm.

Donc, soit nous ajoutons FILE_SHARE_DELETE à toutes les plates-formes, celles de Windows seront 0x4, les autres seront 0x0 et donc "le rejetteront", ou créer un indicateur spécifique pour ce problème.

ET de toute façon nous devons changer syscall_windows*
fonction ouverte(
...
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | isDeleteOn )
où isDeleteOn vérifiera si le mode inclut le nouveau drapeau

@guybrand

Quand j'écris du code, je m'attends à ce qu'il soit multiplateforme

Je vous préviens que ce n'est pas parce que le même appel os.OpenFile est compilé sur plusieurs plates-formes que votre code est multiplateforme. Il y a un certain nombre de considérations de sécurité sur Windows que vous devez prendre en compte lors de l'utilisation de FILE_SHARE_DELETE , et la sémantique des cycles de vie des fichiers est si différente entre les systèmes POSIX et Windows qu'il est vraiment difficile d'imaginer un scénario où votre le code de gestion des fichiers ne serait pas au moins un peu différent entre les plates-formes. L'utilisation de FILE_SHARE_DELETE vous met essentiellement dans une situation pré-POSIX.1-2008, sans aucun moyen d'assurer une traversée du système de fichiers sans course (et sans le bénéfice de l'accès aux fichiers post- unlink de POSIX pour open des dossiers).

Quoi qu'il en soit, si vous voulez un indicateur qui a une valeur de syscall.FILE_SHARE_DELETE sur Windows et 0 sur d'autres plates-formes, vous pouvez toujours le faire facilement avec votre propre définition constante qui est contrôlée par les balises de construction, puis votre code principal qui appelle os.OpenFile peut être le même sur toutes les plateformes (en utilisant ce drapeau personnalisé).

Il existe de nombreux indicateurs spécifiques à la plate-forme qui existent dans les packages syscall autres plates-formes, mais ils ne peuvent pas tous être exposés par le package os et no-op'd sur les plates-formes où ils se trouvent' t pris en charge. Si vous voulez un code et un comportement spécifiques à la plate-forme, il est nécessaire d'atteindre le package syscall .

La seule chose que la bibliothèque standard Go devrait (potentiellement) faire ici est de prendre en charge syscall.FILE_SHARE_DELETE dans syscall.Open (et par conséquent os.OpenFile ) sous Windows.

Comme je l'ai mentionné ci -

@mattn Je suis d'accord avec vos préoccupations - les gens peuvent essayer d'utiliser le drapeau pour plus de commodité pour faire en sorte que le code Windows "fonctionne de la même manière" que les systèmes POSIX sans en comprendre les implications subtiles. Cependant, je pense que s'ils doivent aller au package syscall pour accéder à ce drapeau, ils auront peut-être pris le temps de comprendre ce qu'il fait.

@mattn , pour un comportement de type Unix, les applications auraient besoin de 2 lignes supplémentaires de code

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)

Documenter cela nécessite une phrase dans la documentation pour os.OpenFile().

Veuillez ne pas me faire distribuer un correctif à pkg syscall que tous ceux qui travaillent sur mon code Windows doivent appliquer ! Voir aussi https://github.com/golang/go/issues/32088#issuecomment -493876119.

Je pense que nous sommes tous d'accord sur la plupart des faits, il suffit de le formuler différemment, donc je vais essayer de résumer et de mettre en place un plan d'action :

  1. D'accord : Windows et POSIX diffèrent par le comportement ci-dessus et nous ne devons pas essayer de viser le même comportement.
  2. D'accord : la demande d'autoriser FILE_SHARE_DELETE comme indicateur est utile
  3. Suggestion : laissez changer le ticket en "support FILE_SHARE_DELETE" afin que cela ressemble à une demande de fonctionnalité plutôt qu'à un bogue
  4. Plutôt d'accord : la fonctionnalité doit être un indicateur de développeur au niveau du programme, et non un package de base.

Éléments d'action:

  1. changer syscall_windows.go
    fonction ouverte(
    ...
    sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | (mode & FILE_SHARE_DELETE) )
  2. Le développeur qui souhaite utiliser ce comportement doit soit
    file, err := os.OpenFile(..., os.O_RDWR | syscall.FILE_SHARE_DELETE, ...)
    ou s'ils souhaitent que leur programme soit multiplateforme, ils créeraient un indicateur interne de :

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

puis utilisez :
file, err := os.OpenFile(..., os.O_RDWR | OPENFILEFLAG, ...)

Si cela est convenu, le correctif est minime (une doublure), avec un risque très faible

Votons là-dessus, et nous pourrons rapidement résoudre ce problème

D'accord : la demande d'autoriser FILE_SHARE_DELETE comme indicateur est utile

Je ne suis pas encore d'accord avec cela car je ne comprends pas quel est le but de ce problème?

Objectif : permettre aux développeurs de renommer ou de supprimer les fichiers ouverts.

Ce qui manque : syscall_windows.go actuellement codé en dur :
func Open(path string, mode int, perm uint32) (fd Handle, err error) {
...
sharemode := uint32(**FILE_SHARE_READ | FILE_SHARE_WRITE**)

Modification proposée :
Lorsque le développeur passe un paramètre de mode avec bitwise 4 (FILE_SHARE_DELETE) activé, le sharemode changera en conséquence en
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | **(mode & FILE_SHARE_DELETE)** )

Je me demande toujours pourquoi vous n'appelez pas Close() avant Rename() ou Remove(). Vous pouvez lire le code de la bibliothèque standard de Go. Les fichiers sont fermés par Close() avant Rename() ou Remove(). Pourquoi devez-vous appeler Close() après Renamoe/Rename ?

Je ne sais pas quelle est l'analyse de rentabilisation envisagée par @networkimprov , mon retrait à ce sujet serait un fichier partagé temporaire - mon application et une autre application utilisent ce fichier, je veux m'assurer (même si mon application plante) ce fichier n'existera plus une fois que j'aurai terminé - donc je le crée, le supprime () et lorsque je le ferme ou que mon application est fermée - le fichier est supprimé.

mais il semble que @networkimprov veuille utiliser Rename(), donc probablement un cas d'utilisation diff.

Comme mentionné dans https://github.com/golang/go/issues/32088#issuecomment -494305074, nous n'aurons peut-être pas besoin de fermer un fichier renommé avant la sortie du programme.

@mattn , n'insistez pas sur le fait que nous ne devrions jamais utiliser une fonctionnalité du système d'exploitation que les développeurs C et C++ peuvent utiliser.

Je veux m'assurer (même si mon application plante) que ce fichier n'existera plus une fois que j'aurai terminé

@guybrand Une defer n'est pas garantie de s'exécuter dans tous les types de plantage, donc si vous essayez d' assurer la suppression d'un fichier, alors différer une opération os.Remove n'est pas un moyen fiable de allez-y. Si vous souhaitez qu'un fichier soit automatiquement supprimé, un fichier temporaire que le système supprime est une meilleure option. Si vous voulez un emplacement de fichier partagé par deux programmes, il doit être coordonné par des verrous (par exemple, des verrous fcntl sur POSIX et LockFileEx / UnlockFileEx sur Windows), pas l'existence de fichiers .

Comme mentionné dans #32088 (commentaire), nous n'aurons peut-être pas besoin de fermer un fichier renommé avant la sortie du programme.
@mattn , n'insistez pas sur le fait que nous ne devrions jamais utiliser une fonctionnalité du système d'exploitation que les développeurs C et C++ peuvent utiliser.

@networkimprov Une opération de renommage après ouverture est un cas d'utilisation valide, mais si vous êtes le programme contenant le fichier HANDLE , ne pouvez-vous pas déjà le faire ? Le seul but de FILE_SHARE_DELETE serait de permettre à d'autres processus de renommer le fichier pendant que vous le maintenez ouvert. Dans tous les cas, vous pouvez utiliser FILE_SHARE_DELETE maintenant, il vous suffit d'invoquer manuellement CreateFileW et de transmettre le résultat à os.NewFile . Vous pouvez trouver un exemple de ce à quoi cela ressemble ici . Le HANDLE résultant peut simplement être passé à os.NewFile .

@mattn Je commence à être d'accord avec vous pour dire que ce drapeau sera plus une arme à pied qu'un outil utile. Juste pour être clair, je ne veux pas réellement de ce drapeau, et les seuls arguments que je peux voir pour son existence sont l'exhaustivité et la parité de contrôle entre POSIX et Windows. D'un autre côté, comme je l'ai mentionné ci-dessus, il est également relativement facile d'invoquer manuellement CreateFileW (avec cet indicateur spécifié), puis de transmettre le résultat à os.NewFile , il n'est donc peut-être pas nécessaire de ajouter un support pour cela.

@havoc-io il n'y a pas d'os.File.Rename(). Ce serait une bonne solution, mais c'est relativement lourd.

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

@havoc-io

Je veux m'assurer (même si mon application plante) que ce fichier n'existera plus une fois que j'aurai terminé
@guybrand Une déclaration de report n'est pas garantie de s'exécuter dans tous les types de crash
C'est exactement ce que je voulais dire, quand j'ai dit "même si mon application plante".

les seuls arguments que je peux voir pour son existence sont l'exhaustivité et la parité de contrôle entre POSIX et Windows
Je ne suis pas d'accord, cela apporterait "l'exhaustivité et la parité du contrôle entre POSIX et Windows" - OMI, cela ne ferait que confondre le développeur de penser que le comportement est le même alors que ce n'est pas le cas, j'ai donné quelques exemples ci-dessus.

facile à appeler manuellement CreateFileW
C'est vraiment un tweak, facile ou pas, et en d'autres termes dire : "nous ne voulons pas supporter cela en go", ce qui est d'ailleurs une bonne décision IMO, mais le demandeur est @networkimprov pas moi.

Pour info, voici pourquoi nous avons renoncé à utiliser FILE_SHARE_DELETE.

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

S'engager dans erlang/otp : https://github.com/erlang/otp/commit/0e02f488971b32ff9ab88a3f0cb144fe5db161b2

Python : https://bugs.python.org/issue15244

@havoc-io il n'y a pas d'os.File.Rename(). Ce serait une bonne solution, mais c'est relativement lourd.
Re CreateFileW + os.NewFile(), voir #32088 (commentaire).

@networkimprov Ne pouvez-vous pas simplement utiliser os.Rename(file.Name(), <target>) ? C'est la beauté de Go ne pas avoir FILE_SHARE_DELETE activé par défaut - vous savez que file.Name() pointera toujours vers quelque chose de valide puisque vous tenez le fichier HANDLE (quelque chose que vous pouvez' t garantie sur POSIX, même avec renameat ). En ce qui concerne CreateFileW + os.NewFile , la plupart de la pile décrite dans votre commentaire deviendrait non pertinente, et makeInheritSa est presque certainement quelque chose que vous ne voulez fixLongPath , qui ne sera probablement pas nécessaire pour les cas d'utilisation que vous avez décrits.

@guybrand Juste pour clarifier ce que je veux dire...

complétude : complétude de la surface de l'API (c'est-à-dire la possibilité d'utiliser FILE_SHARE_DELETE si vous souhaitez l'utiliser pour une raison quelconque)
parité de contrôle: La possibilité de spécifier des drapeaux à CreateFileW par os.OpenFile l » flag paramètre. Par exemple, je peux router syscall.O_NOFOLLOW via os.OpenFile sur POSIX, mais je ne peux pas router syscall.FILE_FLAG_OPEN_REPARSE_POINT via os.OpenFile sur Windows. Cependant, encore une fois, je pense que la parité totale de contrôle ici est une chimère puisque le package os est modélisé autour des API POSIX. Je suis assez content d'atteindre syscall.CreateFileW quand c'est nécessaire.

@mattn merci pour les liens. Le Go CL devait patcher le syscall.Open() _default_. La discussion ne donne aucune raison générale d'interdire un _flag_ pour Open(). Il note qu'un package tiers peut renvoyer un fichier os.File sans indicateur, mais ce n'est un problème que si l'appelant essaie de renommer/supprimer ce fichier - un cas rare.

Le fil Python note que vous devez renommer un fichier ouvert avant de le supprimer, comme je l'ai écrit ci-dessus.

Erlang a eu file_share_delete comme _default_ pendant plus de SIX ans .

En réfléchissant à l'expérience Erlang et au fil Python lié ci-dessus, il est évident que :

  1. File_share_delete par défaut fonctionne bien dans un environnement multiplateforme ; le seul inconvénient est que le code utilisateur doit renommer un fichier en un nom unique immédiatement avant de le supprimer (un changement trivial) si le nom de fichier d'origine peut être réutilisé.
  2. La plupart des programmes Windows ne peuvent pas utiliser file_share_delete simplement parce que le runtime MSVC ne le permet qu'en combinaison avec O_TEMPORARY.

Par conséquent, je conclurais que syscall.Open() devrait utiliser file_share_delete par défaut et que syscall devrait fournir un indicateur global pour le désactiver si nécessaire (par exemple, le débogage).

Si des problèmes apparaissent avec cette approche au cours du cycle 1.14, la méthode d'indicateur explicite peut être appliquée à la place.

Par conséquent, je conclurais que syscall.Open() devrait utiliser file_share_delete par défaut, et syscall devrait fournir un indicateur global pour le désactiver si nécessaire (par exemple, le débogage).

@networkimprov En plus de casser un grand nombre de programmes existants de manière subtile et pas si subtile, le fait est que l' introduction de ce drapeau maintenant serait un problème de sécurité pour les programmes reposant sur l'absence de FILE_SHARE_DELETE . En particulier, cela ouvrirait un certain nombre de vulnérabilités entre l' heure de la vérification et l'heure d'utilisation pour les programmes reposant sur l'immuabilité des fichiers et des répertoires qu'ils maintiennent ouverts.

Edit: Pour en savoir plus sur les raisons pour lesquelles changer la valeur par défaut est une mauvaise idée et qu'il est presque certain de casser les programmes existants, veuillez consulter la loi d'Hyrum .

Le cas le plus courant de Go sur Windows est le développement, pour un déploiement sous Linux ou autre Unix.

Si votre code dépend d'un comportement de GOOS=windows, il est cassé et peut-être vulnérable sous Linux. Nous avons donc déjà un problème de sécurité possible.

os.Rename() & .Remove() ne documentent pas les différences sous Windows, donc personne ne devinerait qu'elles existent. Les documenter maintenant ne corrigera pas le code existant. Corriger l'incompatibilité et publier un article de blog dessus aiderait beaucoup plus.

Notez qu'Erlang existait depuis un certain temps avant d'adopter file_share_delete par défaut.

Le cas le plus courant de Go sur Windows est le développement, pour un déploiement sous Linux ou autre Unix.

Si votre code dépend d'un comportement de GOOS=windows, il est cassé et peut-être vulnérable sous Linux. Nous avons donc déjà un problème de sécurité possible.

@improvisationréseau

Selon cette logique, l'infrastructure d'ouverture de fichiers sous Windows doit également être modifiée pour rendre les descripteurs de fichiers héritables par défaut lors de la création de processus, afin de correspondre au comportement par défaut de POSIX. Les programmes existants peuvent être blâmés pour s'être appuyés sur le comportement par défaut précédent de la bibliothèque d'exécution/standard, et un modèle CVE peut être inclus dans les notes de version pour que les utilisateurs puissent déposer leurs programmes.

Mais bien sûr, ce n'est pas ce qui est fait, et à la place, le runtime et la bibliothèque standard de Go se mettent en quatre pour contourner le comportement par défaut et mal conçu de POSIX, essayant de refléter ce que fait Windows.

La situation est la même avec l'accès aux fichiers partagés. Windows a sans doute eu la bonne conception, et POSIX.1-2008 a dû ajouter un certain nombre de fonctions pour contourner ses limitations de conception.

De plus, comme @mattn l' a mentionné à plusieurs reprises ci-dessus, y compris FILE_SHARE_DELETE lors de l'ouverture de fichiers ne fait PAS que Windows se comporte comme POSIX - il y aura toujours des différences de comportement significatives qui deviendront beaucoup plus apparentes avec ce drapeau que différent os.Remove et os.Rename comportement.

Je dirais que si votre code repose universellement sur le comportement de n'importe quelle plate-forme, il est cassé et peut-être vulnérable sur d'autres plates-formes (et cela inclut le comportement POSIX sous Windows). Si vous ne prenez pas le temps de comprendre et de traiter les variations de plate-forme dans votre code, alors vous ne faites pas de développement multiplateforme.

Quoi qu'il en soit, @ianlancetaylor a déjà opposé son veto à l'idée d'utiliser un indicateur global pour contrôler son activation, donc je doute sérieusement que vous en ayez un pour le désactiver après avoir modifié la valeur par défaut.

À mon avis, le seul insécable (et non la sécurité compromettre) l' option est ici pour ajouter le support pour syscall.FILE_SHARE_DELETE en syscall.Open s » flag argument ou la force juste les utilisateurs qui souhaitent que la fonctionnalité FILE_SHARE_DELETE utilise CreateFileW + os.NewFile . Oui, cela nécessite un peu de travail de la part des développeurs qui veulent ce comportement, mais Win32 et POSIX sont des bêtes fondamentalement différentes et il est étonnant que le runtime et la bibliothèque standard fassent un aussi bon travail qu'ils le font pour aplanir leurs différences. Il y a beaucoup plus de différences significatives entre ces plates-formes qui se manifestent dans la bibliothèque standard, par exemple des modèles d'autorisations complètement différents, le fait que os.File.Chmod ne fonctionne pas sur Windows, os.Chown ne fonctionne pas sur Windows, le fait que le poller interne prend en charge différents types de fichiers sur différentes plates-formes, etc. Le runtime et la bibliothèque standard de Go font de leur mieux (et le font bien), mais ils ne sont pas une panacée pour les problèmes de développement multiplateforme. À un moment donné, les développeurs doivent gérer eux-mêmes ces différences. L'ajout d'un changement décisif pour modifier (remarquez que je n'ai pas dit correct ) un cas de contour comportemental obscur n'a aucun sens pour moi.

Je voulais partager un autre cas d'utilisation pour cela.

Actuellement, lorsque la logique de rotation du journal Docker fonctionne, le moteur Docker renomme le journal actif en .1, créez-en un nouveau avec l'ancien nom.
Tout client qui suit activement le journal avec la commande docker logs --follow <container>
remarquerez que d'après les événements du système de fichiers :
https://github.com/moby/moby/blob/916eabd459fe707b5c4a86377d12e2ad1871b353/daemon/logger/loggerutils/logfile.go#L552 -L593

Il y a aussi cette logique qui fait que Windows remarque ces événements immédiatement :
https://github.com/moby/moby/blob/916eabd459fe707b5c4a86377d12e2ad1871b353/daemon/logger/loggerutils/logfile.go#L670 -L676

Tout cela fonctionne bien sur Linux, mais sur Windows, la rotation du journal échoue à l'erreur The process cannot access the file because it is being used by another process. s'il y a des clients qui suivent le journal (problème : https://github.com/moby/moby/issues/39274 )

Comme il est possible d'avoir n nombre de ces clients qui suivent le journal, il serait très difficile de collecter d'abord tous ces descripteurs de fichiers ouverts et de les fermer avant la rotation du fichier, donc j'aimerais vraiment voir bien d'avoir un support pour syscall.FILE_SHARE_DELETE drapeau.

Salut! Présentation rapide : je travaille sur l'infrastructure Chrome CI. Nous portons notre code de travail de bas niveau vers Go. Nous exécutons des centaines de milliers d'appels de tests sur Windows par jour.

J'ai lu tous les commentaires et j'espère que ce qui suit est un résumé clair et correct de l'opinion de chacun.

Notre cas d'utilisation consiste à créer des exécutables Go sur Windows pour s'exécuter sur Windows, contrairement à la caractérisation surprenante de @networkimprov . J'ignorerai ce cas d'utilisation "assumer le comportement POSIX sous Windows" dans ce commentaire, car il s'agit simplement d'une hypothèse incorrecte et @mattn pour mettre en évidence les différences, ce qui, à

Dans le cadre de notre migration, nous avons besoin de FILE_SHARE_DELETE, en partie à cause de la rotation des fichiers journaux, similaire à #39274. Il se trouve que c'est aussi un problème pour le problème #25965.

Je suis d'accord avec @ianlancetaylor pour changer tout le comportement de la chaîne d'outils, comme proposé dans https://codereview.appspot.com/8203043/ n'est pas une bonne idée. Un problème immédiat qui vient à l'esprit de cette proposition lors de l'utilisation d'un descripteur de fichier comme fichier de verrouillage. Nous utilisons ce comportement. Et ce que @havoc-io a dit. Alors ignorons cela aussi.

Pour reformuler, je vais ignorer ces propositions dans la suite de ce commentaire :

  • Essayer de se comporter exactement comme POSIX, ça ne marchera pas
  • Changer globalement le comportement en opt-out (ou pas d'opt-out !), qui casse les utilisateurs

Cela nous laisse avec un indicateur par appel avec un nom clairement spécifique à Windows. C'est exactement ce que propose

Avec tout le respect que je vous dois, je ne comprends pas l'objection de @mattn . Ce que @guybrand propose est raisonnable. Oui, Windows se comporte différemment en ce qui concerne le partage de fichiers. C'est comme les droits de suppression de fichiers, c'est très différent sous Windows . Il diverge déjà de plusieurs façons. Avoir une objection pour un indicateur spécifique à Windows sur la position selon laquelle "les développeurs pourraient mal comprendre son comportement" n'est pas un fait, c'est une opinion. Vous supposez que les développeurs ne savent pas ce qu'ils font ou ne liront pas la documentation sur MSDN. C'est une objection juste pour le cas général 😎, mais pas une objection forte sur la base du blocage d'un cas d'utilisation légitime en tant qu'indicateur facultatif.

Ironiquement, cela signifie que jusqu'à ce que ce problème soit résolu, pour corriger #25965, je devrai changer 'go run' pour appeler directement CreateFileW 🙃 car nous avons également besoin de FILE_FLAG_DELETE_ON_CLOSE .

Merci!

Je veux juste savoir comment existe-t-il pour savoir si nous devons implémenter une nouvelle fonction (golang.org/x/sys?) Pour ouvrir/créer un fichier avec de nouveaux attributs, ou changer le comportement d'origine. Actuellement, pour autant que je sache d'après les commentaires ci-dessus :

  1. créer un fichier supprimable (renommer) pour la journalisation.
  2. création d'un fichier pour le fichier temporaire qui sera supprimé à la fermeture.

Pour 1, il peut être fourni à partir d'une nouvelle fonction pour créer/ouvrir un fichier avec les attributs. Et nous pouvons définir le fichier en utilisant logger.SetOutput(file).

Pour 2, peut également créer/ouvrir avec une nouvelle fonction.

BTW, je suppose que le numéro 25965 n'est pas lié à ce problème. C'est probablement une limitation de Windows.

(En aucun cas, nous ne pouvons supprimer le répertoire où existe le fichier ouvert avec cet attribut)

@mattn Pouvez-vous expliquer le compromis entre :

  • ajout d'une toute nouvelle fonction dans golang.org/x/sys
  • prenant en charge un nouveau drapeau (déjà défini !) vers OpenFile(). [1]

Je ne comprends pas pourquoi on devrait privilégier le premier au lieu du second.

[1] Je viens de réaliser que j'ai supposé ici changer le comportement de os.OpenFile() mais le problème appelle Open().

Tout d'abord, le package syscall est déjà verrouillé. Je cherche donc un moyen de résoudre ce problème sans modifier le package syscall.

BTW, je suppose que le numéro 25965 n'est pas lié à ce problème. C'est probablement une limitation de Windows.

(En aucun cas, nous ne pouvons supprimer le répertoire où existe le fichier ouvert avec cet attribut)

@mattn ce n'est pas tout à fait vrai. FILE_SHARE_DELETE drapeau

# 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

Tout d'abord, le package syscall est déjà verrouillé. Je cherche donc un moyen de résoudre ce problème sans modifier le package syscall.

@mattn S'il s'agit uniquement d'un problème de gel de l'API, alors comme je l'ai mentionné / démo ci-dessus, syscall.Open pourrait simplement être ajusté pour comprendre syscall.FILE_SHARE_DELETE , résolvant le problème sans changer l'API.

S'il s'agit d'un gel de l'implémentation, la modification pourrait être apportée à la fonction golang.org/x/sys/windows de Open , puis intégrée au package internal/syscall . Le code file_windows.go utilise déjà les fonctions de internal/syscall . La seule incohérence serait alors que syscall.Open ne comprendrait pas ce drapeau, mais la plupart des personnes cherchant à accéder aux fonctionnalités de l'API Windows de bas niveau utilisent de toute façon golang.org/x/sys/windows , et probablement en utilisant CreateFile au lieu de Open .

La proposition qui a initié le "lock-down" du package syscall indique :
_Le référentiel principal ne dépendra pas des packages go.sys_

Donc changer x/sys n'aiderait pas les appelants de os.OpenFile(). Mais internal/syscall pourrait ajouter Open(), et os.OpenFile() l'appellerait.

@maruel , le projet Docker, et vraisemblablement beaucoup d'autres, ont supposé le comportement Unix pour os.Rename() & .Remove() des fichiers ouverts car le package "os" ne mentionne pas le comportement spécifique à Windows pour eux, mais a __quinze mentions__ de autre comportement de Windows.

Le référentiel principal ne dépendra pas des packages go.sys
Mais internal/syscall pourrait ajouter Open(), et os.OpenFile() l'appellerait.

@networkimprov D'accord, j'avais l'impression erronée que internal/syscall était un sous-ensemble de golang.org/x/sys , mais dans tous les cas, je pense que c'est la bonne stratégie (en ajoutant internal/syscall/windows.Open ). Bien sûr, cela supposerait que le comportement de syscall.Open est immuable en raison du gel. Idéalement, il pourrait être modifié directement, potentiellement avec des changements correspondants dans golang.org/x/sys/windows.Open .

Bien que le package syscall soit gelé, nous pouvons le modifier pour corriger des bogues ou corriger de graves lacunes. En particulier, nous pourrions le modifier pour mieux prendre en charge le package os.

Mais si les gens appellent directement syscall.Open , alors ils devraient utiliser le package x/sys/windows, pas le package syscall.

Mais si les gens appellent directement syscall.Open , alors ils devraient utiliser le package x/sys/windows, pas le package syscall.

Je ne pense pas que quiconque cherche à appeler directement syscall.Open - ce n'est que la cible de la discussion car il sous-tend os.OpenFile (de sorte que l'ajout de la prise en charge de FILE_SHARE_DELETE dans syscall.Open ajoute la prise en charge de l'utilisation du drapeau avec os.OpenFile ).

Merci. Vous pouvez modifier le package syscall pour prendre en charge les modifications apportées au package os.

Merci @ianlancetaylor et @

  • Modifiez syscall.Open() avec l' intention explicite d'autoriser os.OpenFile() à accepter FILE_SHARE_DELETE (et éventuellement FILE_FLAG_DELETE_ON_CLOSE en tant que suivi potentiel)
  • Mettez à jour la documentation os.OpenFile() pour décrire le nouvel indicateur Windows uniquement.
  • Mettez à jour la documentation os.OpenFile(), os.Rename() et os.Remove() pour clarifier la différence de comportement entre POSIX et Windows.

Le troisième point est de répondre à la préoccupation de @networkimprov . Je comprends le défi là-bas, et bien qu'il ne soit pas du ressort du langage de décrire le fonctionnement du système d'exploitation, je commence à être d'accord avec @mattn pour dire que ces cas sont suffisamment subtils pour justifier davantage de documentation. Je pense que la doc pour Renommer et Supprimer peut être améliorée tout de suite, pas besoin d'attendre un changement de comportement.

Je serai prêt à contribuer à cela s'il y a approbation.

Cela nous laisse avec un indicateur par appel avec un nom clairement spécifique à Windows.

Proposez-vous un nouveau drapeau os.Open ? Ensuite, le drapeau doit être pris en charge par tous les systèmes d'exploitation - c'est tout l'intérêt du package os - être indépendant du système d'exploitation. Alors, quelle serait la sémantique de ce drapeau ? Et vous auriez besoin de nouveaux tests pour vérifier que le drapeau fonctionne comme indiqué.

Je ne vois pas pourquoi vous ne pouvez pas écrire le code dont vous avez besoin en utilisant le package syscall. Je ne pense pas que votre code coexistera bien avec d'autres programmes Windows. Et peut-être que c'est OK dans des circonstances spéciales. Mais je ne veux pas que les gens utilisent cette fonctionnalité à la légère - elle ne devrait donc pas faire partie de la bibliothèque standard.

Alexis

@alexbrainman
s'il vous plaît voir ceci

J'ai regardé. Je ne vois toujours pas comment toutes vos modifications proposées fonctionneront sur d'autres systèmes d'exploitation ? Quels tests devrez-vous effectuer pour mettre en œuvre le changement que vous proposez ?

Alexis

en citant ceci

alors que DELETE_WHEN_FREED peut même être égal à 0 pour env. autre que les fenêtres

Donc - rien à tester sur d'autres OS, l'appel à :
fd, err := os.OpenFile(chemin, os.O_RDWR|os.O_CREATE|os.DELETE_WHEN_FREED, 0600)
dans Windows traduirait le const en 4
fd, err := os.OpenFile(chemin, os.O_RDWR|os.O_CREATE| 4 , 0600)
Tandis que d'autres ( 0 )
fd, err := os.OpenFile(chemin, os.O_RDWR|os.O_CREATE| 0 , 0600)

rien à tester sur d'autres OS

Je ne vois pas comment nous pourrions ajouter un nouveau package os const ou une variable utilisable uniquement sous 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.

Alexis

Alex, la réponse à votre objection a d'abord été indiquée ici https://github.com/golang/go/issues/32088#issuecomment -494157514

Un test est trivial

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

Nous sommes au point où quelqu'un pourrait soumettre un correctif ; Je soupçonne que @ianlancetaylor l'accepterait.

J'ai étudié le paramètre FILE_SHARE_DELETE dans le cadre de #32188, mais j'ai découvert que ce paramètre ne réduisait pas empiriquement le taux d'erreurs des appels os.Rename et io.ReadFile ; une boucle de réessai était toujours nécessaire de toute façon.

Je serais très intéressé de voir un test qui démontre une différence observable significative par rapport à la définition de ce drapeau, car je ne comprends pas vraiment ses implications moi-même.

J'ai expérimenté FILE_SHARE_DELETE il y a quelques années, et il s'est bien comporté
différemment.
Je peux probablement relancer mes fenêtres env. et regarde, si ça peut t'aider,
mais ce serait ~ Go 1,3 ou plus.

Le lun. 10 juin 2019 à 17:44, Bryan C. Mills [email protected]
a écrit:

J'ai enquêté sur le paramètre FILE_SHARE_DELETE dans le cadre de #32188
https://github.com/golang/go/issues/32188 , mais
n'a pas réduit empiriquement le taux d'erreurs de os.Rename et
appels io.ReadFile ; une boucle de réessai était toujours nécessaire de toute façon.

Je serais très intéressé de voir un test qui démontre une
différence observable par rapport à la définition de ce drapeau, car je n'ai pas vraiment
comprendre ses implications moi-même.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4VVIVJ2IEWQPWCWBZTPZZSETA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJZcom#KTDN5WW2CGY4
ou couper le fil
https://github.com/notifications/unsubscribe-auth/ABNEY4STTW7U526HPO6TUHDPZZSETANCNFSM4HNPNYIA
.

@bcmills , lecture #32188 Je suppose que vous voyiez des erreurs de os.Rename() sur un fichier qui _n'était pas ouvert_ lors du changement de nom ? J'imagine que l'on pourrait dépasser le journal NTFS, qui, je crois, est écrit pour toutes les opérations de changement de répertoire, et ainsi induire une erreur; mais je n'ai aucune idée de quelle erreur ce serait.

La demande ici d'activer FILE_SHARE_DELETE dans syscall.Open() est d'autoriser os.Rename() à fonctionner sur les fichiers ouverts. J'ai testé ce drapeau avec 1.12 sur Win7, cela fonctionne ; et échoue sans elle.

Je n'ai pas spécifiquement testé os.Rename() sur Windows "sous charge", mais je n'ai vu aucune erreur inattendue de os.Rename() sur les fichiers ouverts depuis l'activation de fsd, je le signalerai si je le fais.

Je suppose que vous voyiez des erreurs de os.Rename() sur un fichier qui n'était pas ouvert lors du changement de nom ?

Oui.

Je n'ai aucune idée de quelle erreur ce serait.

Empiriquement, les erreurs que je vois (de MoveFileEx , ReplaceFile , et/ou sont CreateFile ) sont ERROR_ACCESS_DENIED , ERROR_SHARING_VIOLATION , et ERROR_FILE_NOT_FOUND .

La demande ici d'activer FILE_SHARE_DELETE dans syscall.Open() est d'autoriser os.Rename() à fonctionner sur les fichiers ouverts. J'ai testé ce drapeau avec 1.12 sur Win7, cela fonctionne ; et échoue sans elle.

À droite; les problèmes connexes que j'essaie de résoudre sont d'autoriser les appels simultanés à os.Rename à réussir, et d'autoriser les appels à io.ReadFile simultanés à os.Rename à réussir. Obtenir un seul os.Rename pour fonctionner avec les poignées existantes ouvertes peut être un pas dans la bonne direction, mais je ne pense pas que ce soit suffisant pour le type de cas d'utilisation pour lesquels nous utilisons le POSIX rename .

Je serais très intéressé de voir un test qui démontre une différence observable significative par rapport à la définition de ce drapeau, car je ne comprends pas vraiment ses implications moi-même.

Exemple @bcmills PowerShell car j'écris cela beaucoup plus rapidement 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
}

Une fois cette opération terminée, il y aura des milliers test.txt.??? fichiers C:\folder1 mais lorsque vous fermerez PowerShell, ces fichiers seront supprimés.

Alors, quel indicateur FILE_SHARE_DELETE permet-il réellement de renommer/déplacer/supprimer les fichiers ouverts, mais vous devez toujours vous assurer que les noms de fichiers de destination sont uniques car ils existeront sur le disque tant que les descripteurs seront ouverts.

"employer le renommage POSIX" est impossible dans Windows, et c'est peut-être la raison des erreurs que vous obtenez.

POSIX :

créer un fichier "nom de fichier"
lecture/écriture à partir de "nom de fichier"
supprimer "nom de fichier"

créer un fichier "nom de fichier"
...
supprimer "nom de fichier"

fonctionnerait, car chaque fichier de création aurait un diff fd.

Les fenêtres:
créer un fichier "nom de fichier"
lecture/écriture à partir de "nom de fichier"
supprimer "nom de fichier"

créer un fichier "nom de fichier" - boom, violation de partage

Cela devrait être très facile à reconstruire...

Que peut faire FILE_SHARE_DELETE alors ?
autoriser la suppression différée pendant que le fichier est ouvert.

C'est ça.

Le lun. 10 juin 2019 à 19h32, Olli Janatuinen [email protected]
a écrit:

Je serais très intéressé de voir un test qui démontre une
différence observable par rapport à la définition de ce drapeau, car je n'ai pas vraiment
comprendre ses implications moi-même.

@bcmills https://github.com/bcmills Exemple PowerShell au moment où j'écris cela
beaucoup plus rapide que Go

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

Après cela, il y aura mille test.txt.??? fichiers sous
C:folder1 mais lorsque vous fermez PowerShell, ces fichiers seront supprimés.

Alors, quel indicateur FILE_SHARE_DELETE vous permet-il réellement
renommer/déplacer/supprimer les fichiers ouverts mais vous devez toujours vous assurer que la destination
les noms de fichiers sont uniques car ils existeront sur le disque tant que les descripteurs
sont ouverts.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4WSTWIUVNVHLYMBFY3PZZ62DA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVX500XHJKTDN5WW2ZZLO
ou couper le fil
https://github.com/notifications/unsubscribe-auth/ABNEY4VS5XF3EXB6QPBWVQ3PZZ62DANCNFSM4HNPNYIA
.

autoriser les appels à io.ReadFile simultanés avec os.Rename pour réussir

@bcmills Je pense qu'il faut toujours travailler avec l'indicateur fsd défini, en supposant qu'il ne s'exécute pas simultanément avec d'autres threads tentant la même séquence sur des fichiers non liés. Et il devrait généralement échouer sans le drapeau.

Cet aspect du script de test a correctement trouvé un bogue dans Windows syscall.Open(). Mais personne d'autre ne commentant dans ce fil ne veut que cela soit corrigé :-( alors oui, corrigez le script.

TLDR :
modification mineure sûre de syscal_windows.go et fonctionne comme prévu.

expliqué :
Alors, voici ce que j'ai fait, fonctionne comme prévu, et sans risque (IMO)

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

l'ajout est : "| (mode & FILE_SHARE_DELETE)"
si le développeur n'enverra pas le mode & 4 set (pourquoi devrait-il - cela ne fonctionnait jamais auparavant...) - rien n'a changé, donc un code comme

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

se comportera EXACTEMENT comme avant le changement, seuls les développeurs qui

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

aura "sentir" le changement

  1. a exécuté le code
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())
    }
}

et ça a marché

  1. a exécuté le code sans le |4 et cela a échoué
  2. a exécuté le code avec un petit ajout :
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()
    }
}

et bien sûr il a échoué sur le secondF
Mais c'est le comportement attendu dans Windows - pas besoin de "charger le test" - il n'y a pas d'option de simultanéité sur deferred .Remove, donc je pense que nous pouvons ajouter en toute sécurité cette demi-ligne à syscal_windows, sans compromettre le code existant, et autorisant uniquement les développeurs qui voulez vraiment utiliser ce mode pour pouvoir différer les suppressions.

tout ce que nous devons faire (en gras):
syscal_windows.go :
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | (mode & FILE_SHARE_DELETE) )

J'ai étudié le paramètre FILE_SHARE_DELETE dans le cadre de #32188, mais j'ai découvert que ce paramètre ne réduisait pas empiriquement le taux d'erreurs des appels os.Rename et io.ReadFile ; une boucle de réessai était toujours nécessaire de toute façon.

Je serais très intéressé de voir un test qui démontre une différence observable significative par rapport à la définition de ce drapeau, car je ne comprends pas vraiment ses implications moi-même.

@bcmills maintenant, vous pouvez trouver des tests unitaires/de régression pour cela à partir de https://github.com/olljanat/go/commit/3828f1a5d0ebb69b4c459d5243799ded36ac1ee8 actuellement, il ne parvient pas à l'erreur The process cannot access the file because it is being used by another process. sur Windows et commence à fonctionner après FILE_SHARE_DELETE drapeau

Noter! que sous Windows, vous avez toujours besoin de cette étape qui déplace le fichier de journal example.log.1 vers un nom aléatoire/unique parce que d'autres raisons pour lesquelles renommer example.log en example.log.1 échoueraient à l'erreur Access is denied. même lorsque le drapeau FILE_SHARE_DELETE est activé. C'est une chose spécifique à la plate-forme dont le développeur doit s'occuper.

/cc @jhowardmsft @jterry75 @jstarks @ddebroy Pour info ; celui-ci pourrait également vous intéresser.

@kevpar - Pour

Si des personnes rejoignant le fil peuvent exprimer des raisons (par exemple, le code existant est cassé) pour en faire la valeur par défaut, vs disponible via le drapeau Windows uniquement dans os.OpenFile(), faites-le!

@networkimprov IMO, il doit être géré avec l'indicateur Windows uniquement, car le développeur doit également gérer d'autres éléments spécifiques à Windows, comme dans mon exemple de rotation de journal.

Cependant, comme je peux le voir, quelques employés de Microsoft ont été invités à une discussion, il est donc intéressant de voir s'ils voient les choses différemment.

Quelqu'un a-t-il commencé à développer une nouvelle solution pour celle-ci ? Des volontaires?

@olljanat
S'il s'agit d'un correctif en tant que drapeau, alors le correctif est une ligne, veuillez lire :
https://github.com/golang/go/issues/32088#issuecomment-500562223

Je vais essayer de représenter ici le point de vue de l'équipe Windows... nous préférons toujours définir FILE_SHARE_DELETE, sans aucune option. En général, pour faciliter le portage vers/depuis Windows, nous préférerions un comportement cohérent avec Linux, et je ne vois pas pourquoi les utilisateurs ou les logiciels Windows préféreraient suffisamment le comportement par défaut !FILE_SHARE_DELETE pour que ce soit une exception à la règle.

Une note intéressante, contrairement au commentaire de

En d'autres termes, si vous exécutez le programme de test de mattn et la séquence de del, dir, etc. sur la dernière version de Windows, vous verrez le fichier disparaître de l'espace de noms immédiatement après la suppression du fichier, pas après la fermeture du programme de test. Tout comme Linux.

Ainsi, même dans Windows lui-même, avec son risque de compatibilité d'application considérablement plus important, nous apportons de petits changements pour faciliter le portage de logiciels non Windows vers Windows. J'encourage vraiment Go à faire de même.

@jstarks , merci de m'avoir justifié. J'ai pris une tonne de chaleur dans ce fil pour cette position.

Je suggérerais une option globale pour désactiver fsd au cas où des déploiements Windows reposent sur le comportement non documenté actuel.

@ianlancetaylor êtes-vous d'accord ?

Comme le comportement par défaut de CreateFile est qu'il ne peut pas supprimer le fichier d'ouverture, je pense que nous devrions conserver le comportement par défaut. Quelqu'un peut vouloir le comportement original. Par exemple, il peut y avoir des programmeurs qui vérifient que leur application fonctionne en utilisant un moyen impossible pour supprimer le fichier.

Le comportement de Python est le même que le comportement actuel de Go. Et je n'ai jamais entendu dire que Python avait l'intention de modifier le comportement qui ne peut pas supprimer les fichiers ouverts.

Je n'ai pas pris le temps de développer une opinion sérieuse sur cette question, mais je ne pense pas qu'une option globale soit un bon choix. Nous devons soit conserver le comportement actuel (et peut-être ajouter un indicateur pour sélectionner l'autre comportement), soit modifier le comportement (et peut-être ajouter un indicateur pour sélectionner le comportement d'origine, actuel).

Quant à savoir s'il faut ou non modifier le comportement par défaut, je pense que nous devons nous demander s'il est plus probable que le changement de comportement répare un programme Go écrit pour s'exécuter sur les systèmes Unix et Windows ou s'il est plus probable que le changement de comportement cassera un programme Go écrit pour s'exécuter sur les systèmes Windows. Je ne connais pas la réponse à cela.

Comme @mattn le suggère, il vaut également la peine de se demander ce que font les autres langues.

S'il devient la valeur par défaut et que tous les déploiements Windows en dépendent, j'imagine qu'ils voudraient à la fois un indicateur os.OpenFile () et une option globale pour revenir à des fins de transition.

Il existe une option globale pour désactiver IPv6, la dernière fois que j'ai vérifié.

Une façon de découvrir l'impact est de le modifier sur le pourboire (et de l'annoncer sur le blog ?) et de voir ce qui est rapporté.

Python et Erlang sont tous deux mentionnés dans le texte du problème :
Erlang en a fait une valeur par défaut il y a plus de six ans : erlang/ otp@0e02f48
Python voulait mais n'a pas pu, en raison des limitations du runtime MSVC : https://bugs.python.org/issue15244

Je vais essayer de représenter ici le point de vue de l'équipe Windows... nous préférons toujours définir FILE_SHARE_DELETE, sans aucune option. En général, pour faciliter le portage vers/depuis Windows, nous préférerions un comportement cohérent avec Linux, et je ne vois pas pourquoi les utilisateurs ou les logiciels Windows préféreraient suffisamment le comportement par défaut !FILE_SHARE_DELETE pour que ce soit une exception à la règle.

dans la version la plus récente de Windows, nous avons mis à jour DeleteFile (sur NTFS) pour effectuer une suppression « POSIX », où le fichier est immédiatement supprimé de l'espace de noms au lieu d'attendre la fermeture de tous les descripteurs ouverts du fichier. Il respecte toujours FILE_SHARE_DELETE, mais se comporte désormais autrement comme POSIX unlink. Cette fonctionnalité a été ajoutée pour WSL et a également été considérée comme utile par défaut pour les logiciels Windows.

@jstarks c'est un détail très intéressant. Est-ce quelque chose que Microsoft peut également s'attendre à ce que Microsoft rétroporte vers les anciennes versions du système d'exploitation ? Je pense notamment à Windows Server 2019 qui est la dernière version, vient de sortir la version LTSC qui sera supportée encore longtemps.

Comme si Microsoft le faisait, je préférerais également activer FILE_SHARE_DELETE par défaut sur Go.

Java, C# et tous les autres langages majeurs que je connais prennent le comportement par défaut du système d'exploitation. À mon avis, le comportement par défaut devrait être laissé à l'implémenteur du système d'exploitation. Si Windows veut vraiment adopter l'approche POSIX concernant l'ouverture de fichiers, il doit prendre ce risque et le faire comme il l'a fait pour DeleteFile.

Je pense que la bibliothèque Go devrait permettre de créer/ouvrir des fichiers de suppression de partage. Je peux donner un exemple pratique en ce qui concerne le mode fichier. Lors du portage de Hadoop vers Windows, nous devons faire des efforts supplémentaires pour créer une méthode spéciale dans JNI pour obtenir le descripteur ou le flux de fichier de suppression partagé.

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

@jstarks

En disant

dans la version la plus récente de Windows, nous avons mis à jour DeleteFile (sur NTFS) pour effectuer une suppression « POSIX », où le fichier est immédiatement supprimé de l'espace de noms

voulez-vous dire que le fichier ouvert reste uniquement en tant que fd , et le code ci-dessous :

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

n'échouera pas à

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

après avoir appliqué le correctif suggéré ?

Si oui, c'est vraiment cool !

Quant à savoir s'il faut ou non modifier le comportement par défaut, je pense que nous devons nous demander s'il est plus probable que le changement de comportement répare un programme Go écrit pour s'exécuter sur les systèmes Unix et Windows ou s'il est plus probable que le changement de comportement cassera un programme Go écrit pour s'exécuter sur les systèmes Windows. Je ne connais pas la réponse à cela.

Il n'y a pas de problem to be fixed exécutant un programme Unix sous Windows. Windows gère la suppression des fichiers ouverts différemment d'Unix. C'est similaire à la sensibilité à la casse des noms de fichiers : Unix est sensible à la casse et Windows est insensible à la casse. Nous ne pouvons rien faire à propos des deux différences.

Et, tandis que John assure que Microsoft essaie de changer les choses, j'attendrais que le changement soit là. Et beaucoup d'utilisateurs de Windows utilisent encore Windows 7 et toutes les autres versions de Windows. John, voudriez-vous aussi mettre à jour Windows 7 ?

Je pense que nous devrions laisser Microsoft gérer le changement. Si nous réparons les utilisateurs Go, il y aura toujours des développeurs non-Go. Microsoft devrait donc gérer cela de toute façon.

De plus, FILE_SHARE_DELETE a ses propres violons. Par exemple, en lisant https://bugs.python.org/issue15244

En fait, ce n'est pas tout à fait la même sémantique qu'Unix.

Après avoir supprimé le fichier, vous ne pouvez pas créer un fichier du même nom ou supprimer le répertoire qui le contient tant que le handle n'a pas été fermé.

Cependant, on peut contourner ce problème en déplaçant le fichier ailleurs (comme le répertoire racine) avant de le supprimer.

Il y en a probablement beaucoup d'autres comme ça que nous ne connaissons pas. Nous n'avons pas du tout utilisé FILE_SHARE_DELETE. Proposez-vous de modifier les bibliothèques Go pour utiliser FILE_SHARE_DELETE par défaut, puis d'attendre que les utilisateurs se plaignent de leurs programmes cassés ?

Comme @mattn le suggère, il vaut également la peine de se demander ce que font les autres langues.

J'ai seulement vérifié Mingw C - il se comporte exactement comme Go. Une fois le fichier ouvert avec fopen, il ne peut pas être supprimé tant que fclose n'est pas appelé. Je serais surpris si l'un des outils de développement Microsoft (C, C++, C#, VB et autres) est différent.

Et je ne vois pas comment on pourrait changer le comportement par défaut comme ça. Certains programmes peuvent avoir un fichier ouvert spécifiquement, il ne peut donc pas être supprimé par un autre programme. C'est une approche valide sur Windows à mon avis. Nous devons le soutenir à l'avenir.

Alexis

Ajouté il y a 7 ans, je crois :
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858

Jusqu'à présent, personne n'a publié de cas d'applications Go qui dépendent du comportement actuel de Windows non documenté. Mais nous avons entendu au moins 4 cas d'applications Go qui ont échoué sous Windows à cause de cela.

Comme indiqué ci-dessus, il existe une solution simple pour la réutilisation des noms de fichiers après os.Remove(), que nous devons documenter :
os.Rename(path, unique_path); os.Remove(unique_path)

Jusqu'à présent, personne n'a publié de cas d'applications Go qui dépendent du comportement actuel de Windows non documenté.

Ces personnes ne savent même pas qu'il est possible de signaler des bugs de Go.

Vous ne devez pas vous attendre à ce que ces utilisateurs surveillent la liste des problèmes de go pour les changements dans l'espoir que quelque chose les affectera. Ils ont la vie à vivre. C'est à nous de ne pas casser les outils qu'ils utilisent.

Alexis

Alex, il est clair que vous n'êtes pas convaincu. Ensuite, je vois deux chemins possibles pour que les gens puissent écrire du code portable dans Go :

  1. un nouveau drapeau d'opt-in à Open qui signifie FILE_SHARE_DELETE pour Windows et est ignoré pour les autres systèmes d'exploitation. Nous encouragerions alors quiconque écrivant du code s'attendant à ce que la sémantique POSIX passe ce drapeau partout ; ou,
  2. un nouveau package Go posix (dans un dépôt quelque part) avec une fonction, Open , qui enveloppe CreateFile avec FILE_SHARE_DELETE mais se comporte autrement comme os.Open . Sur les plates-formes non Windows, il appellerait directement os.Open . Nous encouragerions alors quiconque écrivant du code s'attendant à ce que la sémantique POSIX utilise posix.Open au lieu de os.Open partout.

Ces deux approches nécessitent que les développeurs Linux prennent des mesures supplémentaires pour préserver le comportement de POSIX sous Windows, mais au moins c'est mieux que d'écrire des wrappers CreateFile uniques (comme nous venons de le faire pour containerd).

Alex, il est clair que vous n'êtes pas convaincu.

Je ne suis pas persuadé. Mais vous n'avez pas besoin de me persuader, vous devez persuader Go Team. Ils prendront la décision.

  1. un nouveau drapeau d'opt-in à Open qui signifie FILE_SHARE_DELETE pour Windows et est ignoré pour les autres systèmes d'exploitation. Nous encouragerions alors quiconque écrivant du code s'attendant à ce que la sémantique POSIX passe ce drapeau partout ; ou,

Je n'aime pas non plus cette option.

Tout d'abord, le package os est censé fournir des fonctionnalités disponibles pour n'importe quel système d'exploitation. Tout ce qui est spécifique au système d'exploitation doit aller dans syscall, ou golang.org/x/sys/windows, ou tout autre package que nous aimons.

Deuxièmement, je crains de me retrouver avec le type de fichier FILE_SHARE_DELETE dans mon programme, même si je ne le souhaite pas. Imaginez, si j'importe un package externe qui décide d'utiliser le mode de fichier FILE_SHARE_DELETE . Comment saurais-je qu'une partie de mon code utilise FILE_SHARE_DELETE ? Je devrais grep le code source du package. Ou la documentation du paquet devrait avertir ses utilisateurs. Je suppose que cela pourrait également arriver si un auteur de package utilise simplement syscall.CreateFile avec FILE_SHARE_DELETE directement. Mais, je pense, si FILE_SHARE_DELETE est béni pour le package os, ce sera plus courant. Je soupçonne que les utilisateurs de Linux, qui ne connaissent rien à Windows, utiliseraient ce drapeau par défaut, pour rendre leur port de programme Linux "plus facile" vers Windows.

Troisièmement, nous aurions à documenter le drapeau FILE_SHARE_DELETE . Nous ne pouvons pas simplement dire POSIX. Il faudrait décrire ce que fait le drapeau. En mots simples. Rappelez-vous également :

En fait, ce n'est pas tout à fait la même sémantique qu'Unix.

Après avoir supprimé le fichier, vous ne pouvez pas créer un fichier du même nom ou supprimer le répertoire qui le contient tant que le handle n'a pas été fermé.

Cependant, on peut contourner ce problème en déplaçant le fichier ailleurs (comme le répertoire racine) avant de le supprimer.

à partir de https://github.com/golang/go/issues/32088#issuecomment -504321027. Nous devrons également documenter cela. Et toutes les autres fonctionnalités FILE_SHARE_DELETE drapeau

Quatrièmement, nous aurions besoin d'ajouter de nouveaux tests pour FILE_SHARE_DELETE . Quels seraient ces tests ? Que se passe-t-il, si nous oublions de tester certaines fonctionnalités, pour découvrir plus tard que la fonctionnalité ne peut pas être atteinte en utilisant le drapeau FILE_SHARE_DELETE ? Nous ne pouvons pas supprimer le drapeau FILE_SHARE_DELETE , si nous ne l'aimons pas. Une fois entré, il reste. Et nous devons le soutenir à l'avenir.

2. un nouveau package Go posix (dans un dépôt quelque part) avec une fonction, Open , qui enveloppe CreateFile avec FILE_SHARE_DELETE mais se comporte autrement comme os.Open

Je ne vois pas comment c'est possible. Mais, si c'est possible, vous devriez pouvoir créer le package vous-même. Par exemple, comme github.com/jstarks/posix. Non? Nous pourrions ajouter le paquet sous golang.org/x/sys/windows/posix ou quelque chose, mais je ne vois pas beaucoup de différence.

Alexis

Le package os est censé fournir des fonctionnalités disponibles pour tout système d'exploitation ...
nous aurions à documenter le drapeau FILE_SHARE_DELETE. On ne peut pas se contenter de dire POSIX...
nous aurions besoin d'ajouter de nouveaux tests pour FILE_SHARE_DELETE. Quels seraient ces tests ?

Tout cela a été traité ci-dessus.

si j'importe un package externe qui décide d'utiliser le mode de fichier FILE_SHARE_DELETE. Comment pourrais-je savoir

C'est un autre argument pour en faire la valeur par défaut et fournir un indicateur global pour le désactiver.

J'ai posté sur golang-nuts pour rechercher les applications Windows concernées ; Je vais le ping tous les quelques jours pour le garder visible. Si cela ne fait pas beaucoup surface, en faire le conseil par défaut pour la 1.14 devrait clarifier les choses.
https://groups.google.com/d/topic/golang-nuts/8BiP_mPoCd4/discussion

C'est un autre argument pour en faire la valeur par défaut et fournir un indicateur global pour le désactiver.

D'ailleurs. Que devrait faire ce drapeau sur Linux ? Afaiu, nous manquons également de fonctionnalités pour ouvrir des fichiers sous Linux de manière à ce qu'ils ne puissent pas être supprimés par d'autres processus ? Sur la base de ce https://gavv.github.io/articles/file-locks/#open -file-description-locks-fcntl, il devrait être possible de l'implémenter sur les versions modernes de Linux.

Le drapeau (par exemple var OpenWithout_FILE_SHARE_DELETE = false ) serait défini dans syscall_windows.go, car il affecte le comportement de syscall.Open().

Un indicateur pour quelque chose de spécifique à Linux aurait un nom différent et serait défini dans syscall_linux.go. Désinvolte, je ne sais pas comment empêcher la suppression d'un fichier ouvert sous Linux autrement que via des autorisations sur son répertoire. Le lien que vous avez fourni décrit le verrouillage « conseiller ».

@jstarks avant d'envisager d'introduire l' FILE_SHARE_DELETE , nous devons également décider quoi faire à propos de l'incapacité de Go à supprimer le fichier exécutable en cours d'exécution. Je ne pense pas que le drapeau FILE_SHARE_DELETE puisse nous aider ici. Peut-il? Si nous ne pouvons pas implémenter la suppression du fichier exécutable qui est toujours en cours d'exécution, nous ne pouvons prétendre à aucune compatibilité POSIX. Et le drapeau FILE_SHARE_DELETE ressemble à une demi-solution.

Encore plus, parfois nous ne parvenons pas à supprimer le fichier exécutable d'un processus qui s'est même terminé. Voir, par exemple, #25965, #19491 et #32188 pour plus de détails. Fondamentalement, nous appelons WaitForSingleObject sur le handle de processus jusqu'à ce qu'il signale. Mais cela ne garantit pas que le fichier exécutable peut être supprimé. Nous avons dû attendre 5 millisecondes avant de tenter de supprimer le fichier. Et même cela ne fonctionne pas toujours - https://github.com/golang/go/issues/25965#issuecomment -482037476

Aussi, sans rapport avec ce problème, mais, étant donné que j'ai votre attention, vous pouvez peut-être nous aider à relancer le port Windows-arm. Voir #32135 pour plus de détails. Fondamentalement @jordanrh1 porté Allez sur windows-arm, mais nous n'avons plus de constructeur windows-arm. Donc, nous ne savons même pas si le port fonctionne toujours ou est cassé. Si vous pouviez nous aider à lancer le constructeur, nous pourrions peut-être essayer de continuer à prendre en charge le port Windows-am. Sinon, le port pourrait être supprimé - https://github.com/golang/go/wiki/PortingPolicy Je ne sais pas, si quelqu'un est intéressé par l'exécution de programmes Go sur Windows 10 Iot, mais nous avons déjà ce support. Ce serait triste de le perdre simplement parce que nous n'avons pas de constructeur qui fonctionne.

Merci.

Alexis

Notez cette solution suggérée pour supprimer les exécutables temporaires : https://github.com/golang/go/issues/25965#issuecomment -495636291

Aucun résultat pour les publications sur les noix de golang ; commencé un nouveau fil :
https://groups.google.com/d/topic/golang-nuts/aRvSo3iKvJY/discussion

Au moins une préoccupation pas si hypothétique que je pense devrait être étudiée serait l'effet sur le verrouillage des fichiers et les programmes qui en dépendent.

L'ouverture de chaque descripteur de fichier avec FILE_SHARE_DELETE affaiblirait effectivement la sémantique de LockFileEx à celle des verrous consultatifs POSIX, car tout fichier avec FILE_SHARE_DELETE spécifié peut être supprimé, même si un verrou est détenu sur une région de ce fichier. 1

Bien sûr, cela ne s'appliquerait qu'aux appels LockFileEx effectués avec le descripteur renvoyé par os.File.Fd , mais c'est exactement ce qui est fait dans la bibliothèque de verrouillage de fichiers Go la plus populaire (qui compte actuellement 33 importateurs connus , dont Docker , gVisor et Kubernetes). C'est également fait dans Terraform , et LockFileEx est utilisé dans de nombreux autres codes Go .

Je pense toujours que ce changement semble être une entreprise peu judicieuse dans le meilleur des cas (et une usine CVE dans le pire des cas). Je ne vois vraiment pas en quoi les avantages obscurs l'emportent sur la rupture de code claire qui va se produire.

Je pense également que la plupart des utilisateurs de golang-nuts ne comprendront pas pleinement les implications de ce changement. Avec la permission de l'équipe Go, un post sur golang-dev pourrait être plus efficace pour générer une discussion (d'autant plus qu'il s'agit d'un changement fondamental). En fait, cela affecte techniquement la chaîne d'outils Go, qui utilise également LockFileEx de la manière susmentionnée .


1 Oui, je sais qu'une partie de l'application du verrouillage Windows serait toujours plus forte que sur POSIX, mais si vous pouvez supprimer le fichier, l'utilisation d'un fichier de verrouillage pour la réservation de ressources disparaîtra.

J'ai posté sur golang-dev quand j'ai ouvert le problème.
https://groups.google.com/d/topic/golang-dev/R79TJAzsBfM/discussion

Est-ce que l'une des bibliothèques que vous avez répertoriées _dépend_ du comportement de GOOS=windows ? Vous avez affirmé à plusieurs reprises que ce changement briserait le code existant, mais n'avez jamais fourni de preuves.

Veuillez cesser de faire des déclarations sensationnelles insupportables. Cela n'éclaire rien et je trouve cela choquant.

J'ai posté sur golang-dev quand j'ai ouvert le problème. https://groups.google.com/d/topic/golang-dev/R79TJAzsBfM/discussion

Il semble que tout le monde a rejeté l'idée pour ses propres raisons (toutes similaires à celles mentionnées précédemment dans ce fil), mais si l'idée est maintenant lancée pour appliquer ce changement dans Go 1.14, un suivi à cela peut être utile (avec un lien de rappel vers cette discussion).

L'une des bibliothèques que vous avez répertoriées dépend-elle du comportement de GOOS=windows ? Vous avez affirmé à plusieurs reprises que ce changement briserait le code existant, mais n'avez jamais fourni de preuves.

Cela nécessiterait une évaluation de sécurité complète (de beaucoup de code) pour être certain. Je pense que j'ai certainement fourni suffisamment de preuves que les gens comptent sur un comportement qui changerait visiblement avec l'introduction de ce drapeau. Et ce n'est que du code accessible au public sur GitHub.

Veuillez cesser de faire des déclarations sensationnelles insupportables. Cela n'éclaire rien et je trouve cela choquant.

Si les liens et le code ne sont pas suffisamment pris en charge, alors je ne sais pas ce qu'il y aurait dans votre livre. Vous n'allez pas trouver de code qui arrête de compiler à cause de ce changement, mais vous trouverez certainement du code qui se comporte différemment avec, et beaucoup de gens considéreraient ce code comme "cassé" à cause de ces changements de comportement.

Ne prenez pas mon objection personnellement, je pense que tout le monde ici (y compris vous) essaie juste d'ajouter sa contribution pour améliorer la langue. Si vous voulez défendre un changement fondamental, vous devez vous attendre à une résistance saine.

Comme je l'ai expliqué ci-dessus, la suppression d'un fichier avec FILE_SHARE_DELETE n'a pas la même fonctionnalité que celle d'UNIX. Par exemple, veuillez essayer ces étapes.

principal c

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

regarder.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

Dans un premier temps, exécutez main.exe et ensuite watch.exe. Sous UNIX, unlink(2) supprime immédiatement le fichier. Ainsi, un autre processus qui surveille l'existence du fichier peut créer un nouveau fichier avec le même nom de fichier. Mais sous Windows, notepad.exe affiche une boîte de dialogue d'erreur "violation d'accès" car Windows ne supprime pas le fichier immédiatement. Je ne peux pas vous dire ce qui va se passer avec ça. Problème de sécurité? Fichiers importants perdus ? Désolé, je ne sais pas.

@havoc-io Windows ne supprimera pas un fichier ouvert tant qu'il n'est pas fermé et ne verrouille que les fichiers ouverts. Ainsi, l'utilisation du verrouillage n'implique pas de dépendance à l'échec de os.Remove(). Veuillez fournir des exemples qui dépendent spécifiquement de cet échec.

Je vais envoyer un ping au fil de discussion golang-dev la semaine prochaine. Deux personnes y ont répondu ; on n'avait pas lu ce numéro, et ni l'un ni l'autre n'a fourni d'exemples.

Des allégations sensationnelles et insoutenables ne sont pas une « résistance saine ».

@havoc-io Windows ne supprimera pas un fichier ouvert tant qu'il n'est pas fermé et ne verrouille que les fichiers ouverts. Ainsi, l'utilisation du verrouillage n'implique pas de dépendance à l'échec de os.Remove(). Veuillez fournir des exemples qui dépendent spécifiquement de cet échec.

@networkimprov Vous

Les fichiers de verrouillage (comme verrouillés avec LockFileEx ) sont généralement utilisés pour protéger une ressource. Un exemple pourrait être de s'assurer qu'il n'y a qu'une seule instance d'un démon en cours d'exécution. Si un démon Windows Go ouvre un fichier, puis exécute LockFileEx sur le fichier et réussit à acquérir un verrou exclusif, il peut à juste titre supposer que le fichier de verrouillage ne sera pas supprimé sous celui-ci. Mais, si FILE_SHARE_DELETE est activé par défaut, alors tout le code existant faisant cette hypothèse n'est plus correct. Si un autre agent supprimait ce fichier de verrouillage (par exemple, un utilisateur supprimant par inadvertance un répertoire), le démon d'origine pourrait toujours être en cours d'exécution lorsqu'un nouveau décide de démarrer.

Les fichiers de verrouillage peuvent également être utilisés pour pipeliner les commandes si l'indicateur LOCKFILE_FAIL_IMMEDIATELY est omis, ce qui oblige LockFileEx à attendre son tour pour l'acquisition du verrou. Si le fichier de verrouillage est supprimé par inadvertance, cela peut entraîner l'exécution simultanée de plusieurs commandes qui auraient dû être mises en pipeline.

Des allégations sensationnelles et insoutenables ne sont pas une « résistance saine ».

Personne ne fait d'affirmations sensationnalistes ou insoutenables dans ce fil. Chaque personne qui a été en désaccord avec votre proposition, moi y compris, a fourni des arguments solides soutenus par le code. De même que les personnes qui ont soutenu votre proposition.

Veuillez consulter la documentation WinAPI ; Windows ne supprimera pas un fichier ouvert tant qu'il n'est pas fermé et ne détient que les verrous LockFileEx() pour les fichiers ouverts, de sorte qu'un verrou ne peut pas persister lorsque le fichier est fermé, quelle que soit la suppression. Une tentative d'ouverture d'un fichier verrouillé (c'est-à-dire ouvert) après la suppression entraînerait une erreur, de sorte qu'aucun deuxième verrou ne pourrait être acquis. Les threads en attente d'un verrou ont le fichier ouvert, donc ne seraient pas perturbés par la suppression.

À moins que j'aie mal lu la documentation, vos deux derniers scénarios ne sont pas pris en charge. Et c'était sensationnel et non pris en charge : « ce changement semble être… une usine CVE dans le pire des cas. Je ne vois vraiment pas en quoi les avantages obscurs l'emportent sur la rupture de code claire qui va se produire. »

@networkimprov Le problème est que les fichiers de verrouillage limitant les ressources sont généralement basés sur le chemin du fichier. L'objet fichier sous-jacent ne peut pas être supprimé tant que le fichier n'est pas fermé, mais s'il est supprimé via DeleteFile , il ne sera pas accessible au chemin précédent sur le système de fichiers 1 , et cela permettra la création d'autres fichiers de verrouillage à ce chemin, annulant le but du fichier de verrouillage.

Je ne pense pas que c'était un commentaire sensationnaliste - il semble assez clair que quelque chose comme ça pourrait causer un problème de sécurité si le code s'appuie sur certains invariants du système d'exploitation qui ne sont plus vrais. Et les avantages ici semblent assez obscurs (comme plusieurs personnes l'ont commenté : cela n'aligne pas le comportement de Windows avec POSIX) et la casse semble assez claire (elle casse au moins certains comportements de verrouillage).

1 Du moins, cela semble être le cas lors des tests et sur la base de ce commentaire .

Re "dans la version la plus récente de Windows, nous avons mis à jour DeleteFile (sur NTFS) pour effectuer une suppression" POSIX ", où le fichier est immédiatement supprimé de l'espace de noms" à partir de https://github.com/golang/go/issues/ 32088#issuecommentaire -502850674. AFAIK cette version de Windows n'est pas publiée. De quel test parlez-vous ?

Re avantages, je pense que vous devriez revoir l'ensemble du fil.

AFAIK cette version de Windows n'est pas publiée.

@networkimprov Je crois que c'est déjà le cas sur Windows 10. Il me semble pouvoir supprimer un fichier verrouillé avec LockFileEx si FILE_SHARE_DELETE est spécifié à CreateFileW lors de l'ouverture du fichier.

DeleteFileW() ne donnerait pas d'erreur dans ce scénario. Comment savez-vous qu'il a été supprimé immédiatement du système de fichiers, par rapport à la fermeture du fichier/de l'application ? Avez-vous essayé d'ouvrir/recréer le fichier après l'avoir supprimé ?

En fait, cela affecte techniquement la chaîne d'outils Go, qui utilise _également_ LockFileEx de la manière susmentionnée.

Dans la commande go nous voulons vraiment la sémantique POSIX et ne devons en aucun cas compter sur l'absence de FILE_SHARE_DELETE . En fait, j'ai ajouté le package cmd/go/internal/robustio spécifiquement pour _contourner_ les façons dont le verrouillage de fichiers Windows s'écarte de POSIX.

@ianlancetaylor J'ai posté à plusieurs reprises sur golang-nuts & -dev à la recherche de commentaires de tout projet qui repose sur le comportement actuel (non documenté) de Windows syscall.Open(). Aucun cas d'utilisation n'a émergé.

Étant donné que plusieurs cas d'utilisation nécessitant FILE_SHARE_DELETE sont apparus ci-dessus (et puisque Microsoft nous a spécifiquement demandé de l'utiliser), définissons cet indicateur par défaut dans la pré-version 1.14 et réévaluons s'il existe des cas d'utilisation pour le comportement d'origine pendant le cycle.

Comme suggéré plus tôt dans ce fil, quelqu'un qui a besoin d'un appel open() sans ce drapeau pourrait lancer le sien, en utilisant CreateFileW() & os.NewFile() .

Pouvons-nous au moins exposer un bouton pour cela?

C'est super utile pour implémenter la rotation des journaux sur Windows.

C'est super utile pour implémenter la rotation des journaux sur Windows.

Je ne suis pas sûr mais je suppose que cela ne fonctionnera pas car FILE_SHARE_DELETE ne permet pas de créer un nouveau fichier avec le nom de fichier d'origine si le handle du fichier n'est pas fermé.

@Random-Liu demandez-vous un moyen d'activer fsd sur Open() ou de le désactiver ?

@mattn Cela fonctionne pour moi. Après os.Rename l'ancien fichier journal, je peux créer un nouveau fichier journal avec le nom d'origine et informer le démon de rouvrir le fichier journal.

@networkimprov Je veux un moyen d'activer FILE_SHARE_DELETE . J'utilise actuellement syscall.CreateFile directement.

Ma version Windows :

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

@Random-Liu Mais je suppose que nous ne pouvons pas l'utiliser pour simuler le comportement UNIX.

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

L'application externe ne peut pas accéder au nom de fichier d'origine.

mattn, il n'est pas nécessaire de répéter ce point. Il a déjà été dit qu'il faut documenter la nécessité (sous Windows) de renommer un fichier avant de le supprimer alors que le nom du fichier doit être réutilisable avant la fermeture du handle.

S'il s'agit de renommer le fichier avant de le fermer par l'application elle-même, je pense qu'il n'y a pas d'avantages pour les utilisateurs qui souhaitent faire logrotate.

@ianlancetaylor réessayez https://github.com/golang/go/issues/32088#issuecomment -526074116

J'ai marqué ce problème comme bloqueur de version.

@bcmills pourrais-je vous intéresser à publier un CL ?

Cette ligne : https://golang.org/src/syscall/syscall_windows.go#L291
Juste besoin de : sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)

Edit : Un test pour vérifier qu'il créerait un fichier, puis tenterait de le renommer avant de le fermer.

Nous en avons besoin bientôt afin que les problèmes aient le temps de faire surface avant de geler...

La documentation os.Remove() doit également mentionner que sous Windows, un fichier ouvert doit être renommé avant d'être supprimé si le nom de fichier doit être disponible pour être réutilisé avant la fermeture du fichier. Windows ne termine pas la suppression d'un fichier ouvert tant qu'il n'est pas fermé.

On dirait que la dernière version de Windows remplit https://github.com/golang/go/issues/32088#issuecomment -502850674. Voir https://github.com/papertrail/go-tail/pull/10#issuecomment-529460973 pour plus de détails. Nous sommes désormais en mesure de réutiliser le nom d'un fichier supprimé, même s'il reste des descripteurs ouverts sur l'ancien fichier, à condition que ces descripteurs aient été ouverts avec le mode de partage FILE_SHARE_DELETE . Pas besoin de renommer avant de supprimer. Peut-être qu'il est enfin logique de définir FILE_SHARE_DELETE par défaut dans Go ?

C'est l'intention; nous avons juste besoin de quelqu'un pour soumettre un CL. (Je le ferais, mais je ne peux pas signer un CLA.)
Mes deux commentaires précédents décrivent les changements nécessaires...

@jstarks @jordanrh1 @thaJeztah

Question:
Comment obtenir un comportement cohérent ?
Si le programme s'exécute sur la "dernière version de Windows", cela permettrait de créer un fichier avec un nom similaire à un fichier "en attente de suppression", l'ancienne version se brisera en cas d'erreur.

Cela peut en fait dire : "tests réussis / qa mais ne fonctionne pas sur la plupart des cibles" maintenant, et
"fonctionne généralement, et parfois non" à l'avenir (un développeur dans 2 ans qui recevra une erreur "fichier déjà sorti" ne pourra pas se reproduire).

Nous devons trouver comment identifier les "anciennes" versions et soit modifier le comportement par défaut de celles-ci, soit améliorer l'erreur s'il y a "une suppression en attente" - je ne sais pas comment faire cela.
Et si nous ne le pouvons pas, comment gérer l'incohérence .

@guybrand comme indiqué ci-dessus, nous documenterons que sous Windows "un fichier ouvert doit être renommé avant d'être supprimé si le nom de fichier doit être disponible pour être réutilisé avant la fermeture du fichier".

Cela fonctionne pour les anciennes et nouvelles versions de Windows ainsi que pour POSIX.

Cela ressemble à une approche différente : "Toujours renommer avant de réutiliser, même sur un nouveau Windows ou POSIX"
Je l'aime - il approuve une pratique courante qui réussira partout.

Le changement https://golang.org/cl/194957 mentionne ce problème : syscall: allow FILE_SHARE_DELETE on syscall.Open on Windows

Le fil golang-nuts est ici , et n'a eu aucune réponse. Cela semble indiquer que personne qui lit golang-nuts et connaît ce comportement ne s'y fie.

Les fils golang-dev discussion ici et ici , et ce dernier (le fil le plus récent) n'a également suscité aucune objection.

@alexbrainman -2'd le CL pour le mettre en œuvre sur la base d'un manque apparent de décision, j'ai donc renommé ce problème en NeedsDecision afin que nous puissions obtenir une orientation claire.

Personnellement, la décision me semble claire : étant donné que les gens de Microsoft sur ce fil sont en faveur de la modification du comportement par défaut pour utiliser FILE_SHARE_DELETE (https://github.com/golang/go/issues/32088 #issuecomment-502850674), et que personne sur golang-nuts semble s'en soucier d'une manière ou d'une autre, je pense que nous devrions le faire.

Les objections que j'ai vues sur ce fil semblent se diviser en deux catégories:

  1. L'inquiétude de @alexbrainman (https://github.com/golang/go/issues/32088#issuecomment-504321027) et @mattn (https://github.com/golang/go/issues/32088#issuecomment-494417146) semble être que, puisque le comportement ne correspond toujours pas à POSIX, les utilisateurs qui attendent la sémantique POSIX peuvent être confus. (Mais c'est déjà le cas que Open sur Windows ne fournit pas de sémantique POSIX, donc pour moi cette préoccupation semble indépendante du changement proposé.)

  2. La préoccupation de @havoc-io (https://github.com/golang/go/issues/32088#issuecomment-510314947) semble être que FILE_SHARE_DELETE diminuera les invariants fournis par les packages de verrouillage de fichiers lors de l'exécution sur Windows . Mais cet argument me semble un peu déroutant, car le package concret présenté en exemple ( github.com/gofrs/flock ) note explicitement que « les comportements de verrouillage ne sont pas garantis d'être les mêmes sur chaque plate-forme », et d'après un audit rapide aucun des exemples concrets ne semble reposer sur l'interaction spécifique à Windows entre le verrouillage et la suppression de fichiers.

Le commentaire dans https://github.com/golang/go/issues/32088#issuecomment -498690757 m'intrigue cependant :

Je suis d'accord avec @ianlancetaylor pour changer tout le comportement de la chaîne d'outils, comme proposé dans https://codereview.appspot.com/8203043/ n'est pas une bonne idée. Un problème immédiat qui vient à l'esprit de cette proposition lors de l'utilisation d'un descripteur de fichier comme fichier de verrouillage. Nous utilisons ce comportement.

@maruel , pouvez-vous donner plus de détails sur la façon dont votre programme repose sur l'interaction spécifique à Windows entre le verrouillage de fichiers et la suppression ou le renommage de fichiers ?

Chez Chrome infra, nous utilisons la présence de fichiers comme mécanisme de verrouillage. Je dirais de ne pas trop s'inquiéter de ce cas d'utilisation. En pratique, d'après ce que je comprends, la sémantique O_CREATE + FILE_FLAG_DELETE_ON_CLOSE est suffisante pour ce besoin et cela peut être remplacé par un mutex dans l'espace Global\\ noms

Cette solution n'est pas mentionnée :

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

Il a deux inconvénients :

  • Close () n'est pas juste après Open ()
  • La récupération du code d'erreur est plus compliquée

@iwdgo Cette solution est destinée à l'utilisation d'un seul processus. La plupart des discussions ci-dessus concernent l'accès simultané à partir de plusieurs processus. C'est ce qui rend le drapeau important.

Pour compléter le récapitulatif @bcmills du problème, nous avons examiné trois autres outils de développement multiplateformes :

Mingw-w64 en a fait une valeur par défaut il y a sept ans :
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858

Erlang en a fait une valeur par défaut il y a plus de six ans : erlang/ otp@0e02f48

Python n'a pas pu l'adopter en raison des limitations de l'environnement d'exécution MSVC au moment où ils l'ont envisagé : https://bugs.python.org/issue15244

Un deuxième fil de golang-nuts n'a également vu aucune objection:
https://groups.google.com/d/topic/golang-nuts/aRvSo3iKvJY/discussion

Voulez-vous que FILE_SHARE_DELETE soit par défaut pour Open() ? Notre discussion ne devrait pas être cela.

  1. Le souci de @alexbrainman ( #32088 (commentaire) ) ... semble être que puisque le comportement ne correspond toujours pas à POSIX ...

Ma principale préoccupation est de briser les programmes des gens. Semblable à ce que @minux a dit à https://codereview.appspot.com/8203043/#msg10

une autre raison : les personnes utilisant Windows acceptent que ne pas pouvoir renommer ou
supprimer un fichier ouvert est la norme et s'y est déjà habitué.

Nous ne pouvons pas changer silencieusement le comportement du programme Go 1.0 ici, peut-être que les gens
en dépendent en fait.

Qu'est-il arrivé à la promesse de compatibilité Go 1.0 ici ? Où est-il dit dans https://golang.org/doc/go1compat qu'il est acceptable de modifier le comportement des fichiers ouverts si vous annoncez votre intention à go-nuts ?

Peut-être que nous pourrons changer ce comportement une fois que nous aurons introduit Go 2 ? Pouvons-nous utiliser des modules pour protéger le code existant de la rupture ? Ai-je tort de supposer que c'est pour cela que les modules ont été conçus ?

Personnellement, la décision me semble claire : étant donné que les gens de Microsoft sur ce fil sont en faveur de la modification du comportement par défaut pour utiliser FILE_SHARE_DELETE ...

Si Microsoft fait autorité sur ce sujet pour vous, alors vous devriez regarder ce que font les outils de développement Microsoft.

J'ai utilisé ce programme https://play.golang.org/p/4ZPmV6Df3SD similaire à ce que @mattn a posté sur https://github.com/golang/go/issues/32088#issuecomment -493753714 Je l'ai construit avec un compilateur C récent

Compilateur d'optimisation Microsoft (R) C/C++ version 19.16.27030.1 pour x64
Copyright (C) Microsoft Corporation. Tous les droits sont réservés.

Et, lorsque j'exécute le programme, je ne peux pas supprimer le fichier C:\Users\Alex\AppData\Local\Temp\a.txt tant que le programme n'existe pas.

Idem avec un programme C# similaire https://play.golang.org/p/SuM2iWYpZir J'ai utilisé récemment

Compilateur Microsoft (R) Visual C# version 2.10.0.0 (b9fb1610)
Copyright (C) Microsoft Corporation. Tous les droits sont réservés.

De même ce programme C https://play.golang.org/p/6HgxePzEW_W construit avec le récent Mingw

gcc (x86_64-win32-seh-rev0, construit par le projet MinGW-W64) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.

avoir le même effet. Ainsi, aucun de ces outils n'a l'indicateur FILE_SHARE_DELETE défini par défaut.

Et personnellement, je ne vois aucun avantage à ce changement. Cela inclut mes contributions Go. Nous ne pourrons toujours pas supprimer les fichiers exécutables pendant l'exécution du processus. Nous aurons toujours des problèmes pour supprimer des répertoires si un processus y a défini ses répertoires actuels.

Alexis

Voulez-vous que FILE_SHARE_DELETE soit par défaut pour Open() ?

L'intention est de faire en sorte que os.Open utilise FILE_SHARE_DELETE par défaut. Ce https://golang.org/cl/194957 est le plan.

Alexis

Ma principale préoccupation est de casser les programmes des gens

Jusqu'à présent, personne ne peut citer un seul exemple de programme qui se briserait car il repose sur l'obtention d'une erreur lors de la tentative de renommer/supprimer un fichier ouvert avec Go. Mais il existe des cas documentés où le comportement actuel interrompt les applications Go sous Windows.

Qu'est-il arrivé à la promesse de compatibilité Go 1.0 ici ?

Les documents Go pour os.Rename() et .Remove() sont silencieux sur cette fonctionnalité Windows, mais contiennent 15 mentions d'autres comportements spécifiques à Windows. BTW, Go 2 a été expédié : https://blog.golang.org/go2-here-we-come

gcc (x86_64-win32-seh-rev0, construit par le projet MinGW-W64) 7.3.0

Quelle C-lib utilisez-vous avec gcc -- peut-être celle de Microsoft ? Mingw-w64 est à la version 6.0 et inclut FILE_SHARE_DELETE dans FILE_SHARE_VALID_FLAGS.

Comme indiqué ci-dessus concernant Python, dans le passé, l'environnement d'exécution MSVC interdisait certaines combinaisons d'indicateurs CreateFile() pour des raisons inconnues. Je ne sais pas si c'est toujours le cas, mais c'est peut-être la raison pour laquelle MS n'a pas changé sa C-lib fopen().

Je ne vois aucun avantage à ce changement.

Les avantages ont été expliqués à plusieurs reprises ci-dessus, par plusieurs personnes ; permettant notamment os.Rename() sur un fichier ouvert.

Marquer comme une proposition car la discussion ne converge pas facilement.

Pour essayer de résumer le problème et la discussion jusqu'à présent :

  • Sur les systèmes Unix, si un processus ouvre un fichier, un deuxième processus peut supprimer ce fichier. Le processus d'origine peut continuer à utiliser le fichier (il n'a plus de nom), y compris les lectures et les écritures, qui réussissent. Le contenu du fichier n'est pas supprimé du disque tant que la dernière référence ouverte n'a pas disparu.

  • Sur les systèmes Windows, par défaut, si un processus ouvre un fichier, un deuxième processus _ne peut pas_ supprimer ce fichier. Un fichier ne peut être supprimé que lorsqu'aucun autre processus ne l'a ouvert. L'indicateur FILE_SHARE_DELETE modifie ce comportement : si toutes les références ouvertes au fichier ont été ouvertes avec FILE_SHARE_DELETE, un autre processus peut appeler DeleteFile et il réussira au lieu d'échouer.

Le comportement de Windows avec FILE_SHARE_DELETE semble toujours légèrement différent de celui d'Unix. La page MSDN dit :

La fonction DeleteFile échoue si une application tente de supprimer un fichier qui a d'autres descripteurs ouverts pour les E/S normales ou en tant que fichier mappé en mémoire (FILE_SHARE_DELETE doit avoir été spécifié lorsque d'autres descripteurs ont été ouverts).

La fonction DeleteFile marque un fichier à supprimer à la fermeture. Par conséquent, la suppression de fichier ne se produit pas tant que le dernier handle du fichier n'est pas fermé. Les appels suivants à CreateFile pour ouvrir le fichier échouent avec ERROR_ACCESS_DENIED.

C'est-à-dire que le fichier _name_ n'est pas supprimé du système de fichiers tant que les références ouvertes ne disparaissent pas. Ceci est différent d'Unix, où le nom disparaît avant que les références ouvertes ne soient fermées. Donc, si vous supprimiez le fichier en vue de recréer un fichier du même nom, cela fonctionne sous Unix mais pas sous Windows, même pas avec FILE_SHARE_DELETE.

La suggestion initiale était de modifier syscall.Open pour toujours définir ce drapeau. Étant donné que syscall est censé être proche de l'interface du noyau, l'ajout de cet indicateur semble déplacé. Mais nous pourrions plutôt considérer s'il doit être défini pendant os.Open / os.OpenFile.

@alexbrainman a fait valoir qu'Unix et Windows sont différents, ils resteraient différents même si nous définissons cet indicateur, et nous ne devrions donc pas confondre les choses en effectuant ce changement dans le dos des utilisateurs.

@jstarks de Microsoft dit que Microsoft préférerait que nous définissions FILE_SHARE_DELETE automatiquement et que la "version la plus récente" de Windows (on ne sait pas laquelle ou si elle est publiée) modifie DeleteFile pour que la sémantique Unix du nom disparaisse au retour de DeleteFile. Donc, si nous faisions cela, sur ce dernier Windows, nous obtiendrions vraiment le même comportement sous Unix et Windows.

@networkimprov soutient que nous devrions effectuer le changement et qu'il n'y a pas d'exemples réels de programmes qui pourraient tomber en

@alexbrainman ne semble toujours pas convaincu.

@mattn a également suggéré que nous ne devrions pas définir le drapeau automatiquement, en partie parce que (au moins jusqu'à ce que tout le monde utilise la dernière version de Windows), il sera toujours différent d'Unix.

Dans l'ensemble, le package os essaie de fournir une API portable, et il semble que - en particulier avec le changement de Windows - la définition automatique du drapeau aiderait à cela. Il existe d'autres indicateurs que nous définissons automatiquement dans os.Open pour aider à rendre le comportement moins surprenant pour les utilisateurs, en particulier O_CLOEXEC (close-on-exec). Tout programme qui se bloque sous Windows en raison de la définition de ce drapeau dans os.Open se briserait également nécessairement sous Unix, n'est-ce pas ?

D'autant plus qu'au moins un ingénieur chez Microsoft dit que nous devrions le configurer, il semble qu'il serait acceptable de le définir dans le package os. Les personnes qui ne veulent pas que l'indicateur soit défini peuvent toujours se rabattre sur le package syscall.

@alexbrainman et @mattn , supposons que nous placions le drapeau dans os.Open. Nous savons que certains programmes fonctionneraient mieux. Avez-vous des exemples précis de programmes réels (ou probables) qui échoueraient ? Ces programmes ne seraient-ils pas déjà cassés sur Unix ? Merci.

@rsc , merci pour votre contribution. L'activation de cela dans le package "os" fonctionne pour moi. Si cela est adopté, un indicateur syscall.Open() pour activer FILE_SHARE_DELETE doit être fourni pour être complet.

Pour clarifier votre résumé, les limites du processus ne sont pas un facteur ; les effets sont les mêmes au sein d'un même processus. Et l'indicateur affecte également le renommage du fichier ; avec le drapeau, il se termine immédiatement contrairement à la suppression de fichier. (Et cela permet une solution de contournement pour la disponibilité du nom de fichier immédiatement après la suppression.)

Merci @networkimprov. Oui, je comprends que les limites du processus ne sont pas strictement pertinentes ; cela rend simplement les situations plus faciles à décrire.

J'ai changé le titre et révisé le plan d'action dans le texte du problème :

a) os.Create/Open/OpenFile() doit toujours activer file_share_delete sous Windows,
b) syscall.Open() sous Windows doit accepter un indicateur qui active file_share_delete, et
c) syscall sur Windows devrait exporter une constante pour le nouveau drapeau.

Les docs pour os.Remove() devraient également noter que pour réutiliser un nom de fichier après avoir supprimé un fichier ouvert sous Windows, il faut faire : os.Rename(path, unique_path); os.Remove(unique_path) .

La possibilité qu'un utilisateur implémente un traitement exclusif en utilisant le comportement os.Open actuel de Go ne peut être niée.

Mais il existe des cas documentés où le comportement actuel interrompt les applications Go sous Windows.

Qu'est-ce que c'est? Quels sont les numéros d'émission ? Et qu'entendez-vous par le mot break ? Quand ont-ils rompu ?

Quelle C-lib utilisez-vous avec gcc -- peut-être celle de Microsoft ? Mingw-w64 est à la version 6.0 et inclut FILE_SHARE_DELETE dans FILE_SHARE_VALID_FLAGS.

Je sais pas. Je viens de télécharger le fichier x86_64-7.3.0-release-win32-seh-rt_v5-rev0.7z sur Internet. Vous pouvez google pour cela. Il s'agit de la version 7.3.

Je ne vois aucun avantage à ce changement.

Les avantages ont été expliqués à plusieurs reprises ci-dessus, ...

Vous avez oublié le mot personally dans votre devis. Je parle de mes propres besoins. Et cela inclut mes contributions au projet Go.

Pour essayer de résumer le problème et la discussion jusqu'à présent :

Vous n'avez pas dit que les outils de développement Microsoft sous Windows ne fournissaient pas cet indicateur par défaut. Voir mon commentaire https://github.com/golang/go/issues/32088#issuecomment -531713562

Vous avez également ignoré mon commentaire sur la compatibilité Go 1

Qu'est-il arrivé à la promesse de compatibilité Go 1.0 ici ? Où est-il dit dans https://golang.org/doc/go1compat qu'il est acceptable de modifier le comportement des fichiers ouverts si vous annoncez votre intention à go-nuts ?

Comment ce changement s'intègre-t-il dans la promesse de compatibilité Go 1.0 ?

Dans l'ensemble, le package os essaie de fournir une API portable, et il semble que - en particulier avec le changement de Windows - la définition automatique du drapeau aiderait à cela.

Je suis d'accord que ce changement vise les personnes qui portent des logiciels Unix vers Windows.

Malheureusement au détriment des utilisateurs de Windows. Ce changement surprendra les utilisateurs et les développeurs Windows, car aucun autre programme ou outil sur Windows ne fonctionne de cette façon. Peut-être dans le futur, mais pas encore.

Et même pour les personnes portant le logiciel Unix sur Windows, ce changement fait du très mauvais travail à mon humble avis. Voir 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. partie de mon commentaire https://github.com/golang/go/issues/32088#issuecomment -531713562

Il existe d'autres indicateurs que nous définissons automatiquement dans os.Open pour aider à rendre le comportement moins surprenant pour les utilisateurs, en particulier O_CLOEXEC (close-on-exec). Tout programme qui se bloque sous Windows en raison de la définition de ce drapeau dans os.Open se briserait également nécessairement sous Unix, n'est-ce pas ?

Je ne sais pas ce que vous voulez dire ici. O_CLOEXEC sous Windows signifie empêcher les descripteurs de fichier de s'échapper dans le processus enfant. Et O_CLOEXEC est toujours défini dans os.Open sous Windows. Ainsi, aucun fichier ouvert avec os.Open n'est accessible par un processus enfant sous Windows. Et alors? Cela semble raisonnable.

Ces programmes ne seraient-ils pas déjà cassés sur Unix ?

Je ne comprends pas votre question.

Avez-vous des exemples précis de programmes réels (ou probables) qui échoueraient ?

Je ne sais pas si j'ai de tels programmes. C'est impossible à dire. Je ne me souviens pas de tous les programmes que j'ai écrits. Je ne me souviens pas de ce qu'ils font.

Mais je pourrais imaginer qu'un service Windows ouvre un fichier prédéfini, disons c:\mysvc.txt , et le garde ouvert. Imaginez un autre programme qui vérifie que le service est actif et redémarre le service, si nécessaire. Cet autre programme peut simplement essayer de supprimer c:\mysvc.txt , et, si le fichier a disparu, il peut supposer en toute sécurité que le processus de service est mort.

De plus, je peux imaginer, certains programmes pourraient vouloir empêcher les utilisateurs de supprimer ou de déplacer leurs fichiers - https://stackoverflow.com/questions/11318663/prevent-a-user-from-deleting-moving-or-renaming-a-file

Alexis

Voici un point de données concernant le déploiement de Go sur Windows :
Win8 est sorti il ​​y a 7 ans.
Ce bug de Go affectant Win8+ a été signalé il y a à peine 5 mois : #31528

@rsc tout programme qui s'attend à obtenir une erreur en essayant de renommer/supprimer un fichier ouvert avec Go est cassé sous Unix. Par exemple, le scénario c:\mysvc.txt Alex.

Si l'on développe sur Windows et déploie sur Linux/etc (ou vice versa), cela pourrait être une source de bugs.

Notre cas d'utilisation est que nous avons un enregistreur qui enregistre dans un fichier.
Le fichier journal prend en charge la rotation (par exemple, la prochaine écriture si le fichier journal est > maxSize puis la rotation).
Nous avons également des lecteurs qui ouvriront ces fichiers et les garderont ouverts pendant leur lecture.

Sans même la possibilité de définir FILE_SHARE_DELETE nous ne pouvons pas effectuer une rotation (renommer et/ou supprimer) tant qu'il y a des lecteurs actifs.

@mattn :

La possibilité qu'un utilisateur implémente un traitement exclusif en utilisant le comportement os.Open actuel de Go ne peut être niée.

C'est peut-être le cas, mais ce code est de toute façon incorrect sous Unix. Le package os est censé être une interface portable. Nous sommes également plus intéressés par les programmes _réels_ que par les possibilités.

@alexbrainman :

Vous n'avez pas dit que les outils de développement Microsoft sous Windows ne fournissaient pas cet indicateur par défaut. Voir mon commentaire #32088 (commentaire)

Oui mais c'est C; nous parlons de Go. Nous n'imitons pas les API C en général. Nous essayons de fournir une abstraction principalement portable dans le package os. Le point sur O_CLOEXEC était que sur les systèmes Unix, os.Open définit O_CLOEXEC même s'il n'est pas défini par défaut dans l'appel C correspondant. Nous essayons de fournir des valeurs par défaut utiles qui se comportent à peu près de la même manière sur tous les systèmes.

En ce qui concerne la compatibilité Go 1, modifier un comportement de cas d'angle sur un système d'exploitation pour mieux l'aligner avec d'autres systèmes d'exploitation semble OK. Nous ne promettons pas de préserver toutes les incompatibilités inter-systèmes pour toujours.

Les utilisateurs de Windows qui insistent sur le comportement spécifique à Windows ont toujours la possibilité d'utiliser le package syscall, ce qui indiquerait clairement que le comportement atteint ne s'applique vraiment qu'à Windows.

Encore une fois, avez-vous des exemples concrets de vrais programmes qui tomberaient en panne ?

@ cpuguy83 , merci pour le cas d'utilisation réel d'un programme qui serait aidé.

@rsc Comme vous le dites, je n'ai pas encore compris les effets de rupture de FILE_SHARE_DELETE. De même, aucune confirmation n'a été trouvée que logrotate peut être mis en œuvre par ce changement.

Cependant, une fois que Go a inclus cette modification qui peut supprimer le fichier, l'utilisateur s'attend à ce comportement même si Go ne peut pas implémenter logrotate sur Windows par cette modification. Nous devons donc bien réfléchir à ce changement. Fondamentalement, Windows ne peut pas supprimer les fichiers qui ne sont pas marqués avec FILE_SHARE_DELETE. Les utilisateurs peuvent penser que ce changement permettra à Go de supprimer tous les fichiers. En particulier, les fichiers créés par os.NewFile avec un handle peuvent manipuler les méthodes de la structure os.File même si cet indicateur n'est pas défini. Le programmeur doit savoir que le fichier peut être supprimé sous Windows ou non par lui-même, je pense. Je préfère un mécanisme qui permet à FILE_SHARE_DELETE d'être passé par indicateur pour os.OpenFile au lieu de la valeur par défaut. Je pense que le comportement par défaut doit être suivi de celui du système d'exploitation.

Oui mais c'est C; nous parlons de Go.

Et C# aussi. Je n'ai pas enquêté, mais je suis presque sûr que tout autre outil de développement sur Windows se comporte de cette façon.

En ce qui concerne la compatibilité Go 1, modifier un comportement de cas d'angle sur un système d'exploitation pour mieux l'aligner avec d'autres systèmes d'exploitation semble OK.

C'est un cas pour les non-utilisateurs de Windows. Pour les utilisateurs de Windows, ce comportement est standard.

Encore une fois, avez-vous des exemples concrets de vrais programmes qui tomberaient en panne ?

Considérez mon exemple de service ci-dessus concret.

@ cpuguy83 , merci pour le cas d'utilisation réel d'un programme qui serait aidé.

@ cpuguy83 pouvez-vous en dire plus sur votre problème. J'aimerais voir comment ce changement résoudra votre problème, donc j'aimerais voir du vrai code. Si vous pouvez fournir un petit programme, qui est cassé maintenant, mais qui fonctionnera une fois ce problème résolu, ce serait apprécié. Il n'a pas à construire, mais au moins, il devrait en fournir suffisamment pour que tout le monde puisse en juger. Merci.

Fondamentalement, Windows ne peut pas supprimer les fichiers qui ne sont pas marqués avec FILE_SHARE_DELETE. Les utilisateurs peuvent penser que ce changement permettra à Go de supprimer tous les fichiers.

Je suis d'accord c'est un bon point. Seuls les fichiers ouverts par les programmes Go peuvent être supprimés. Mais tous les autres fichiers ouverts par des programmes non Go resteront toujours non supprimables. Cela rendra os.Remove imprévisible - parfois cela fonctionnera, et parfois non.

Alexis

J'ai du code appelant un syscall.Open () corrigé qui renomme les fichiers ouverts. C'est ainsi que vous implémentez la rotation des journaux ; Ça marche.

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

Mais tu n'avais besoin de personne pour te le dire :-/

Naturellement, un os.File de os.NewFile() peut avoir de nombreuses propriétés différentes de celles de os.Create/Open/OpenFile(), c'est une fonctionnalité, pas une surprise.

Vous prétendez que le comportement de Microsoft C fopen() est le comportement par défaut de Windows ; ce n'est pas vrai. Il n'y a pas de mode de partage par défaut CreateFile() ; ce n'est pas FILE_SHARE_READ | FILE_SHARE_WRITE .

Vous affirmez que les programmes Windows déployés supposent généralement que les __programmes d'autres fournisseurs__ ouvrent toujours les fichiers d'une manière spécifique. Si tel avait été le cas, Microsoft ne nous aurait pas demandé d'activer file_share_delete par défaut.

@improvisationréseau

Mais tu n'avais besoin de personne pour te le dire :-/

Je veux savoir que le processus externe peut supprimer et renommer le fichier correctement dans tous les cas.

Ce que je veux savoir, c'est : après avoir inclus cette modification, le fichier journal est sorti de Go, le logrotate exécuté par le processus externe fonctionne correctement et il fonctionne comme prêt pour la production sans aucune erreur.

@alexbrainman

Malheureusement, le code est compliqué, mais il s'agit d'un code réel : https://github.com/moby/moby/blob/master/daemon/logger/loggerutils/logfile.go

L'atténuation proposée consiste actuellement à simplement forker os.OpenFile et syscall.Open : https://github.com/moby/moby/pull/39974/files

Pour être clair, il suffirait d'avoir la possibilité de passer par FILE_SHARE_DELETE depuis OpenFile ou même vers syscall.Open pour gérer notre cas.

Malheureusement, le code est compliqué, mais il s'agit d'un code réel : https://github.com/moby/moby/blob/master/daemon/logger/loggerutils/logfile.go

L'atténuation proposée consiste actuellement à simplement forker os.OpenFile et syscall.Open : https://github.com/moby/moby/pull/39974/files

@ cpuguy83 merci pour le pointeur.

Malheureusement, mon bref examen ne fournit pas suffisamment d'informations pour juger si ce problème actuel est la solution. Je devrais commencer par repro (probablement ceci https://github.com/moby/moby/issues/39274#issuecomment-497966709 ) et partir de là. Je ne sais pas quand j'aurai du temps à consacrer à cela. Si vous avez une meilleure suggestion que mon plan, faites-le moi savoir.

Alexis

Vous trouverez ci-dessous une démonstration fonctionnelle de la rotation des journaux de pratique standard, avec 100 goroutines enregistrant chacune 50 lignes à des intervalles de 0,1 à 2 secondes chaque fois qu'elles ouvrent le fichier journal, et une autre goroutine faisant pivoter le journal à des intervalles de 1 min. J'ai exécuté cela pendant plusieurs heures hier sur un ordinateur portable Win7 sans aucune erreur.

Il est construit avec un correctif pour syscall.Open() qui active FILE_SHARE_DELETE ; il échoue sinon. On pourrait concocter un schéma de journalisation plus complexe sans ce drapeau, mais il existe de nombreuses autres utilisations ; mon propre code renomme les fichiers ouverts pour différentes raisons.

@rsc , je pense que cela complète le cas de votre suggestion, s'il y avait encore des doutes. (Et comme indiqué ci-dessus, j'ai posté à plusieurs reprises dans golang-dev et golang-nuts demandant des projets qui reposent sur le comportement actuel, avec aucun résultat.)

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

Correctif à 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 Le fichier n'est pas renommé par un processus externe. Si renommer le fichier se fait dans le même processus, il existe d'autres moyens de l'implémenter.

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

Vous trouverez ci-dessous une démonstration fonctionnelle de la rotation des journaux de pratique standard

Merci pour votre exemple, @networkimprov . Je peux voir maintenant ce que vous essayez de faire.

Le seul problème avec votre code est que, si un processus externe ouvre les fichiers sur lesquels vous écrivez sans FILE_SHARE_DELETE, vous aurez le même problème que maintenant, quelles que soient les modifications que nous apportons à Go.

@cpuguy83 Je ne regarderai pas votre problème (comme je l'ai promis ici https://github.com/golang/go/issues/32088#issuecomment-536233195 ), car l'exemple @networkimprov me donne une image de ce qu'il essaie faire.

Alexis

Alex, on peut boucler la tentative de changement de nom si l'accès extérieur est autorisé, ou restreindre le fichier journal actif sinon.

Mattn, le changement de nom de rotation est généralement effectué par un processus séparé.

Il semble qu'il n'y ait pas de consensus clair ici. Les développeurs avec une formation Unix semblent en faveur de cela, mais deux de nos développeurs Windows les plus actifs ( @alexbranman et @mattn) ne le sont pas. Le changement n'unifierait également vraiment que la dernière version de Windows qui a le nouveau comportement de type Unix pour FILE_SHARE_DELETE.

Cela vaut peut-être la peine de revenir sur ce sujet dans quelques années, pour voir à quel point le nouveau comportement de drapeau est largement disponible et si d'autres langues ont convergé vers un comportement commun. Si quelqu'un veut déposer une nouvelle question dans, disons, deux ans environ pour repenser cela, ce serait bien.

Mais pour l'instant, étant donné l'absence de consensus, cela semble être une baisse probable .

Laissant ouvert pendant une semaine pour les commentaires finaux.

Y a-t-il une controverse sur le fait de simplement rendre l'option disponible ?

Si nous recherchons plus de voix pour intervenir, je serais certainement en faveur de faire _quelque chose_. À l'heure actuelle, la seule solution est de copier littéralement un tas de code hors de la bibliothèque standard de Go, de modifier une ligne, puis de conserver ce fork pour toujours. Chaque implémentation d'un moniteur de journal ou d'une "queue" en direct semble finalement le faire.

Pour un exemple, voir https://github.com/elastic/beats/blob/master/libbeat/common/file/file_windows.go#L85 -L103

Si j'ai bien suivi, cette proposition comporte en fait deux parties :

  1. Autoriser l'utilisation du flag FILE_SHARE_DELETE lors de l'ouverture d'un fichier -
    la mise en œuvre devrait être facile et sûre, le développeur devrait explicitement
    demander ce mode lors de l'ouverture d'un nouveau fichier
  2. Activer ce drapeau par défaut - peut être risqué et bien pris en charge uniquement
    lors de l'utilisation de la dernière version de Windows 10.

Si tout le monde était d'accord sur 1, peut-être pouvons-nous simplement autoriser le drapeau lorsque le développeur
demandez-le explicitement pour le moment et revisitez-en 2 dans quelques années.

Le mercredi 2 octobre 2019, à 19h32, Mark Dascher, [email protected] a écrit :

Si nous recherchons plus de voix pour intervenir, je serais certainement en faveur
de faire quelque chose . Pour l'instant, la seule solution est de copier littéralement un
un tas de code hors de la bibliothèque standard de Go, modifiez une ligne, puis
maintenir cette fourchette pour toujours. Chaque mise en œuvre d'un moniteur de journal ou en direct
"queue" semble finalement faire cela.

Pour un exemple, voir
https://github.com/elastic/beats/blob/master/libbeat/common/file/file_windows.go#L85 -L103

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4VFQLSYVI66ENT6G4LQMTLJ7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVX2HJZKTDN5Q14
ou couper le fil
https://github.com/notifications/unsubscribe-auth/ABNEY4U5L3WFZYNIUOSEY2TQMTLJ7ANCNFSM4HNPNYIA
.

Définitivement en faveur de _au moins_ avoir 1. (Autoriser l'utilisation du drapeau FILE_SHARE_DELETE lors de l'ouverture d'un fichier)

Juste pour revenir sur ma proposition précédente et suivre avec un exemple de mise en œuvre :

Le syscall.FILE_SHARE_DELETE drapeau cadrerait bien dans le os.OpenFile de flag paramètre et fournirait un mécanisme trivial pour permettre ce comportement à la demande. Il n'entre en collision avec aucun autre indicateur os.O_* sous Windows (et cela peut être appliqué/conçu) et il fournit un moyen idiomatique de spécifier le comportement d'ouverture de fichier spécifique au système (dans la mesure où os ' s L'interface orientée POSIX peut être considérée comme idiomatique sous Windows). C'est la même route qui est déjà utilisée pour transmettre le comportement d'ouverture de fichier spécifique au système sur les plates-formes POSIX (par exemple syscall.O_DIRECTORY sur Linux).

Même si la décision est prise de ne pas l'activer par défaut (ce qui, je pense, est le bon appel), je suis d'accord que le drapeau a ses cas d'utilisation (y compris ceux signalés par @networkimprov) et qu'il serait utile d'être capable de l'activer sans avoir à recourir à des appels CreateFileW et à copier du code passe-partout.

Veuillez considérer l'alternative [1] ci-dessus qui permet aux développeurs de définir FILE_SHARE_DELETE si nécessaire et d'empêcher les morceaux fourchus du code de la bibliothèque standard Go.

__Rust__ définit également cet indicateur par défaut et vous permet de _désactiver_ l'une des options FILE_SHARE_*.
https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html

@rsc pourrais-je demander lequel des arguments techniques avancés contre la proposition n'a pas été réfuté ?

@networkimprov après avoir lu toute la proposition et la discussion, je pense que vous devez fermer ceci et rouvrir explicitement un nouveau problème sur l'ajout d'un nouvel indicateur spécifique à Windows au package ossyscall. Cela garantirait que le nouveau comportement est opt-in plutôt que opt-out. La proposition actuelle implique de changer le code existant, ce qui inquiète les gens.

@rsc pourrais-je demander lequel des arguments techniques avancés contre la proposition n'a pas été réfuté ?

Je suis désolé, mais ce n'est pas ainsi que fonctionne le processus de proposition . Il ne suffit pas de « réfuter » les arguments des autres. "L'objectif du processus de proposition est d'atteindre un consensus général sur le résultat en temps opportun." Il n'y a pas de consensus clair sur la voie à suivre ici. Des arguments techniques ont été avancés, et ils n'ont pas convaincu les principaux développeurs Go qui ont apporté des contributions significatives au portage Windows (à savoir, @alexbrainman et @mattn). En plus de l'absence de consensus clair, il n'y a aucun signe clair d'urgence à faire quelque chose aujourd'hui : Go a bien fonctionné sur Windows pendant près de 10 ans avant que ce problème ne soit déposé. Si je comprends bien, laisser tout seul signifie que Go continuera à fonctionner aussi bien qu'il l'a toujours fait.

J'ai déposé #34681 pour fournir un moyen plus simple d'ouvrir des fichiers avec FILE_SHARE_DELETE dans le cas (toujours probable) où celui-ci serait refusé. Il m'a semblé plus utile de commencer un nouveau fil limité à cette idée que de continuer celui-ci, qui est devenu très long.

@rsc , avant de refuser, voyons ce qu'Alex pense de votre proposition O_ALLOW_DELETE.

Au début de cette discussion, il était contre un nouveau drapeau os.OpenFile() qui définit file_share_delete, pour les mêmes raisons qu'il n'était pas d'accord avec votre suggestion os.Create/Open/OpenFile(). Il craint que d'autres programmes supposent que personne n'ouvre jamais les fichiers de cette façon, car MSVC fopen() ne peut pas le faire. Ces programmes (encore non spécifiés) casseront donc les programmes Go qui définissent le drapeau.

Alex, on peut boucler la tentative de changement de nom si l'accès extérieur est autorisé, ...

Si vous êtes prêt à boucler le changement de nom, vous n'avez pas besoin de modifier quoi que ce soit dans le code du référentiel Go. Votre programme fonctionnera aussi bien qu'il fonctionne maintenant.

À l'heure actuelle, la seule solution est de copier littéralement un tas de code hors de la bibliothèque standard de Go, de modifier une ligne, puis de conserver ce fork pour toujours.

Je pense que copier le code dans un package séparé est bien. En enquêtant sur https://github.com/moby/moby/pull/39974, j'ai eu la même idée. Je ne pense pas qu'il y ait beaucoup de code à maintenir. En fait j'ai implémenté ça

https://github.com/alexbrainman/goissue34681

N'hésitez pas à copier ou à utiliser tel quel. Peut-être, étant donné l'intérêt suscité par cette fonctionnalité, vous créez en fait un package approprié qui peut être partagé et utilisé par d'autres. De cette façon, vous pouvez le maintenir à jour et corriger les bogues.

@rsc , avant de refuser, voyons ce qu'Alex pense de votre proposition O_ALLOW_DELETE.

S'il vous plaît essayez

https://github.com/alexbrainman/goissue34681

première. Si vous n'êtes pas satisfait pour une raison quelconque, nous pourrions discuter de l'ajout de nouvelles fonctionnalités au référentiel Go.

Merci.

Alexis

Je suis vraiment déçu par cette réponse.

Copier cette large bande de code, qui n'est même pas comprise en grande partie (comme
dans pourquoi cela fait-il ce qu'il fait) juste pour que nous puissions ajouter une seule option
que le système lui-même prend en charge, juste que Go, probablement je suppose que non
même intentionnellement, ne fournit pas un moyen de transmettre l'option même à
syscall.Open semble être une situation ridicule.

Pourquoi dois-je réécrire syscall.Open pour transmettre cette option ?

Le samedi 5 octobre 2019 à 18h43, Alex Brainman [email protected] a écrit :

Alex, on peut boucler la tentative de changement de nom si l'accès extérieur est autorisé, ...

Si vous êtes prêt à boucler le changement de nom, vous n'avez rien à changer
dans le code du dépôt Go. Votre programme fonctionnera aussi bien qu'il fonctionne maintenant.

À l'heure actuelle, la seule solution est de copier littéralement un tas de code hors de
La bibliothèque standard de Go, modifiez une ligne, puis maintenez cette fourchette pour toujours.

Je pense que copier le code dans un package séparé est bien. En enquêtant
moby/moby#39974 https://github.com/moby/moby/pull/39974 J'ai le même
idée. Je ne pense pas qu'il y ait beaucoup de code à maintenir. En fait, je
mis en œuvre juste que

https://github.com/alexbrainman/goissue34681

N'hésitez pas à copier ou à utiliser tel quel. Peut-être, étant donné qu'il y a tellement d'intérêt pour
cette fonctionnalité, vous créez en fait un package approprié qui peut être partagé
et utilisé par d'autres. De cette façon, vous pouvez le maintenir à jour et corriger les bogues.

@rsc https://github.com/rsc , avant de refuser cela, écoutons ce que
Alex pense à votre proposition O_ALLOW_DELETE.

S'il vous plaît essayez

https://github.com/alexbrainman/goissue34681

première. Si vous n'êtes pas satisfait pour une raison quelconque, nous pourrions discuter de l'ajout de nouveaux
fonctionnalité à Go repo.

Merci.

Alexis

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=AAGDCZXHULQEMHAPTO6ZUJTQNFGE7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVX2HJZKTDN7WWIQ2HJKTDN7
ou couper le fil
https://github.com/notifications/unsubscribe-auth/AAGDCZWYUNMCTAGO567AV73QNFGE7ANCNFSM4HNPNYIA
.

>

  • Brian Goff

Je suis vraiment déçu par cette réponse.

Je suis désolé que vous vous sentiez ainsi. Mais avez-vous réellement essayé d'utiliser mon package ?

Alexis

@rsc , étant donné qu'Alex est également opposé à un drapeau os.OpenFile(), ne devrions-nous rien faire ?

Que diriez-vous de mettre cette fonctionnalité derrière un indicateur de construction ?

Quant à savoir si "Go a bien fonctionné sous Windows pendant près de 10 ans", ce n'était certainement pas le cas, au cas où vous auriez dû renommer un fichier ouvert. (Il a également été cassé sur les ordinateurs portables Windows 8/10 au cours des 7 dernières années.)

J'ai mon propre fork de os.OpenFile et syscall.Open déjà dans moby/moby.

Pourquoi sommes-nous si méprisants ici ?

Le mardi 8 octobre 2019 à 01h47, Alex Brainman [email protected] a écrit :

Je suis vraiment déçu par cette réponse.

Je suis désolé que vous vous sentiez ainsi. Mais avez-vous réellement essayé d'utiliser mon package ?

Alexis

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=AAGDCZRV72GY4IJQJVJWMYTQNRCIHA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVX04HJKTDN5WWA146
ou couper le fil
https://github.com/notifications/unsubscribe-auth/AAGDCZRNYSMIE6BX77XJVWDQNRCIHANCNFSM4HNPNYIA
.

>

  • Brian Goff

Je suggère que cette proposition spécifique soit refusée. Nous n'allons pas introduire un tas de bogues de sécurité TOCTOU pour les utilisateurs de Windows qui se sont appuyés sur le comportement existant de os.OpenFile.

La question la plus importante ici est : « comment pouvons-nous exposer aux utilisateurs de Go la grande variété d'indicateurs intéressants pour les fonctions CreateFile et NtCreateFile de Windows ? » Je m'attends à ce que nous soyons en mesure de résoudre ces problèmes dans différentes propositions, mais certainement pas en les activant tous par défaut, comme cela le suggère ici.

@zx2c4 as -tu lu tout le fil ? Nous n'avons pas été en mesure d'identifier un seul cas d'utilisateur de Go qui s'appuie sur le comportement existant et non documenté, malgré des tentatives répétées.

Cela fait une semaine depuis https://github.com/golang/go/issues/32088#issuecomment -537590826, et il n'y a toujours pas de consensus clair, ce qui signifie que nous devrions refuser cela.

Diminué.

J'ai publié un résumé des options avec les avantages et les inconvénients dans https://github.com/golang/go/issues/34681#issuecomment -565853605.

Cette page vous a été utile?
0 / 5 - 0 notes