Oui, vous avez raison de penser que le déplacement d'un seul fichier sur le même système de fichiers est en fait implémenté comme une opération de renommage qui modifie la structure du système de fichiers pour mettre à jour le nouveau nom/emplacement du fichier, mais le contenu du fichier n'est pas lu/écrit sur le disque à nouveau.
Lorsque le déplacement se fait entre deux systèmes de fichiers différents (lecteurs ou partitions), alors la commande mv
supprime d'abord la destination (s'il y avait déjà un ancien fichier là-bas), copie le contenu du fichier vers la destination et enfin supprime le fichier source.
Le comportement est expliqué dans le manuel de mv sur macOS:
Comme l'appel à rename(2) ne fonctionne pas entre les systèmes de fichiers, mv utilise cp(1) et rm(1) pour réaliser le déplacement. L'effet est équivalent à :
rm -f chemin_destination && \
cp -pRP fichier_source destination && \
rm -rf fichier_source
En ce qui concerne l'autre réponse qui compare ce comportement avec le code source FreeBSD - la commande mv sur macOS est en réalité un peu différente de celle sur FreeBSD. En particulier, elle veille à ce que les attributs étendus et les fourches de ressources soient déplacés correctement et ne disparaissent pas lors des déplacements entre les limites des systèmes de fichiers.
Vous pouvez lire le véritable code source macOS pour mv
. Vous verrez qu'il est similaire en structure à la version FreeBSD, mais contient diverses améliorations spécifiques à Apple. En plus de la fonctionnalité concernant les attributs étendus et les fourches de ressources telles que décrites ci-dessus, il comporte également des améliorations de performance pour une utilisation avec Xsan (système de fichiers distribués).
Vous verrez dans le code qu'au départ un renommage est tenté :
if (!rename(from, to)) {
if (vflg)
printf("%s -> %s\n", from, to);
return (0);
}
Si ce rename()
échoue, le code vérifie pourquoi il a échoué. Surtout, il vérifie le numéro d'erreur EXDEV, qui signifie que le renommage aurait traversé les systèmes de fichiers, et donc ne peut être effectué:
if (errno == EXDEV) {
struct statfs sfs;
char path[PATH_MAX];
/* Impossible de mv(1) un point de montage. */
if (realpath(from, path) == NULL) {
warnx("ne peut pas résoudre %s: %s", from, path);
return (1);
}
if (!statfs(path, &sfs) && !strcmp(path, sfs.f_mntonname)) {
warnx("impossible de renommer un point de montage");
return (1);
}
} else {
warn("renommage %s en %s", from, to);
return (1);
}
Remarquez ici que ce code interrompt le déplacement si la source contient des liens symboliques non résolus, ou si c'est en fait un point de montage - et également généralement si le rename()
échoue pour d'autres raisons que EXDEV.
Seulement dans le cas où rename()
échoue avec le numéro d'erreur EXDEV, et non pour les raisons mentionnées ci-dessus, le code suivant est exécuté:
/*
* Si le renommage échoue parce que nous tentons de traverser des périphériques, et
* qu'il s'agit d'un fichier régulier, effectuez la copie en interne ; sinon, utilisez
* cp et rm.
*/
if (lstat(from, &sb)) {
warn("%s", from);
return (1);
}
return (S_ISREG(sb.st_mode) ?
fastcopy(from, to, &sb) : copy(from, to));
Ce code se ramifie pour effectuer le déplacement entre les systèmes de fichiers de deux manières différentes selon que la source à déplacer est effectivement un fichier régulier - ou autre chose. "Autre chose" est généralement un répertoire, un lien symbolique, un nœud de périphérique ou similaire.
Dans le cas d'un fichier régulier, il utilise fastcopy()
qui ouvre simplement les fichiers source et destination, lit les données du source grâce à read()
et les écrit à la destination grâce à write()
. Contrairement à la version FreeBSD, la fonction fastcopy()
utilise fcopyfile()
pour copier les ACL et les attributs étendus du source à la destination.
Dans le cas de quelque chose qui n'est pas un fichier régulier, il invoque simplement des commandes externes pour effectuer le déplacement: cp
pour copier et rm
pour supprimer.
2 votes
Notez que
mv
lui-même ne sait rien des détails internes du système de fichiers comme la table FAT sur un système de fichiers FAT. Comme le soulignent les réponses, il se contente de faire un appel systèmerename()
et laisse le noyau renvoyer le succès ou l'échec. L'API de fichiers Unix/POSIX laisse tous les détails du système de fichiers virtuel au noyau; le noyau lui-même est la seule chose avec des pilotes pour HFS+, VFAT, NTFS, etc. Voir la page de manuel POSIXrename(2)
pour voir à quel point c'est simple.