Vi peut écrire dans un fichier bien que le fichier soit en lecture seule

12

L'exemple suivant montre comment créer un fichier avec uniquement des autorisations de lecture. Comme on peut le voir, lorsque je tente d'écrire dans ce fichier en utilisant la commande echo je reçois, Permission denied.

Mais pourquoi, dans le cas où nous utilisons vi, n'obtenons -nous pasPermission denied ? Comme on peut le voir ici, nous pouvons écrire dans le fichier même si le fichier est en lecture seule.

Que se passe-t-il ici? Est-ce un bug vi?

[admin@madona-machine1 ~]$ touch test-file
[admin@madona-machine1 ~]$ ls -ltr
total 0
-rw-r--r-- 1 admin admin 0 Apr 13 07:32 test-file
[admin@madona-machine1 ~]$ chmod -w  test-file
[admin@madona-machine1 ~]$ ls -ltr
total 0
-r--r--r-- 1 admin admin 0 Apr 13 07:32 test-file
[admin@madona-machine1 ~]$ echo try_to_write > test-file
-bash: test-file: Permission denied
[admin@madona-machine1 ~]$ vi test-file

I am good singer,

 ~
 ~
 ~
 ~
 ~
 ~
 ~                                                
   "test-file" 1L, 4C written
maihabunash
la source
1
Pour info, il existe un site bêta SE pour cela - vi.stackexchange.com
Raystafarian

Réponses:

28

Remarque : Pour des raisons de licence héritées, la plupart des distributions GNU / Linux n'incluent pas le programme vi original écrit par Bill Joy. Au lieu de cela, la commande vi est fournie en exécutant Vim en mode de compatibilité vi. La réponse suivante est basée sur l'exécution de Vim avec son mode de compatibilité vi.

Modification d'un fichier en lecture seule

Vim avertit l'utilisateur si elles modifient le tampon d'un fichier en lecture seule, W10: Warning: Changing a readonly file. Si les essais de l' utilisateur écrit à ce fichier, ils obtiennent le message d'erreur suivant, 'readonly' option is set (add ! to override).

Lorsque le répertoire parent est accessible en écriture par l'utilisateur Vim

Vim, étant utile, fait savoir à l'utilisateur qu'il peut insister avec force pour écrire en ajoutant un point d'exclamation !à la wcommande. Si cette version puissante de la commande d'écriture est utilisée, Vim supprime le fichier d'origine (si vous utilisez Vim avec le backupjeu d'options Vim uniquement , le fichier d'origine est en fait renommé pour être le même que le fichier de sauvegarde). Il ouvre ensuite (crée) un nouveau fichier portant le même nom que l'original et écrit le contenu de son tampon dans ce nouveau fichier. Cela peut être observé en vérifiant l' inode du fichier avant et après l'exécution de Vim:

$ ls -l --inode t

131529 -r--r--r-- 1 anthony anthony 0 Apr 13 09:23 t

$ vi t
$ ls -l --inode t

131649 -r--r--r-- 1 anthony anthony 4 Apr 13 09:23 t

Remarque: Cela peut également modifier l'autorisation et la propriété du fichier et rompre les liens (symboliques), par exemple, si le fichier d'origine appartenait à un autre utilisateur, le nouveau fichier appartiendrait à l'utilisateur exécutant Vim.

Un processus ne peut le faire que s'il dispose d'une autorisation d'écriture pour le répertoire parent du fichier. En général, pour garantir qu'un programme ne peut pas modifier un fichier, les autorisations du fichier lui-même et de son répertoire parent doivent être sécurisées.

Lorsque le répertoire parent n'est pas accessible en écriture par l'utilisateur Vim

Cependant, même dans ce cas, Vim fait toujours de son mieux pour aider l'utilisateur insistant à écraser le fichier. Si l'utilisateur Vim est propriétaire du fichier, Vim peut contourner la restriction du répertoire parent en lecture seule en modifiant temporairement l'autorisation du fichier (à l'aide de l' chmodappel système), en écrivant le tampon dans le fichier, en fermant le fichier, puis en modifiant le autorisations de retour. Voici un extrait des appels système effectués lors de l'exécution de vi via strace strace -o ../vi.trace vi t:

getuid()                                = 501
chmod("t", 0100644)                     = 0
open("t", O_WRONLY|O_CREAT|O_TRUNC, 0644) = 4
write(4, "I am good singer,\n", 18)     = 18
fsync(4)                                = 0
close(4)                                = 0
chmod("t", 0100444)                     = 0

Remarque: cela ne se produit pas si l'utilisateur Vim modifie un fichier dont il n'est pas propriétaire car Vim ne pourra pas modifier les autorisations du fichier.

Addenda

Pour être vraiment certain qu'un fichier ne peut pas être modifié (sur un système GNU / Linux), exécutez la chattrcommande en tant que superutilisateur:

sudo chattr +i filename

De man chattr:

Un fichier avec l'attribut «i» ne peut pas être modifié: il ne peut pas être supprimé ou renommé, aucun lien ne peut être créé vers ce fichier et aucune donnée ne peut être écrite dans le fichier. Seul le superutilisateur ou un processus possédant la capacité CAP_LINUX_IMMUTABLE peut définir ou effacer cet attribut.

Anthony Geoghegan
la source
2
Holy smoke, c'était complet!
Camille Goudeseune
4
@CamilleGoudeseune Après avoir publié la première version de ma réponse, j'ai fait quelques expériences et j'ai fini par passer environ une heure à exécuter Vim dans Strace pour voir ce qu'il faisait en coulisses dans différentes situations (différentes permutations d'autorisations et propriété des deux fichiers et Dossier Parent). Je m'emballe parfois, mais une fois que j'ai publié une réponse, je voulais m'assurer que ce que je disais était correct.
Anthony Geoghegan
5

La plupart , sinon toutes les viimplémentations vous empêchent d'écrire le fichier si vous utilisez une commande régulière de sauvegarde comme soit ZZ, :w, :wqou :x, par exemple avec vim:

:w
E45: 'readonly' option is set (add ! to override)

D'un autre côté, si vous dites vid'écrire le fichier malgré ses autorisations, en utilisant quelque chose comme :x!ou :wq!, l'éditeur assouplit temporairement les autorisations pour autoriser l'écriture du fichier:

...
stat("test-file", {st_mode=S_IFREG|0444, st_size=7, ...}) = 0
getuid()                                = 1000
chmod("test-file", 0100644)             = 0
...
open("test-file", O_WRONLY|O_CREAT|O_TRUNC, 0644) = 4
write(4, "I am good singer,\n", 18)               = 18
fsync(4)                                = 0
close(4)                                = 0
chmod("test-file", 0100444)             = 0
....

Dans ce cas, le numéro d'inode reste inchangé.

Enfin, ce n'est pas un bogue car si vous n'êtes pas autorisé à modifier les autorisations du fichier, vous ne pouvez pas le modifier vi.

jlliagre
la source
Hah! Après avoir posté ma réponse, j'ai fait quelques expériences et j'ai fini par passer près d'une heure à exécuter Vim dans Strace pour voir ce qu'il faisait en coulisses dans différentes situations (différentes permutations d'autorisations et propriété du fichier et du répertoire parent). Je viens juste de voir votre réponse après avoir fini de résumer les résultats de mes expériences. C'était une expérience enrichissante.
Anthony Geoghegan