OVH Cloud OVH Cloud

Déplacer un fichier d'une partition à l'autre

22 réponses
Avatar
Robert CHERAMY
Bonjour,

je cherche (dans un programme C++) à déplacer un fichier d'une partition
à l'autre, ce qui n'est pas possible avec la fonction rename(), qui
renvoit dans ce cas l'erreur "Invalid cross-device link".

J'ai cherché dans google, regardé dans la glib, dans les sources du
programme mv:
* google: recherches infructueuses, je dois être trop bête pour trouver
la requête qui va bien.
* glib: la commande g_rename ne fait rien de plus qu'appeler rename, ce
qui ne m'avance pas :-)
* mv: c'est un peu trop complexe pour un "déplacement de rien du tout" ;-)

Avez-vous des idées ?

Merci!

tibob

10 réponses

1 2 3
Avatar
Harpo
Loïc Restoux wrote:

Le 28 jan, à 10:36, Harpo papotait :

Tu mènes quand même une vie dangereuse...


Hmmmm... voyons...
(Attention, je vais etre HS).


(...)

-----------------------------
#define FORMAT "/bin/mv %s %s"
#define FORMAT_SIZE ( sizeof( FORMAT ) - 5 )
^^^^^

Aie :
- La constante FORMAT_SIZE ne contient donc *pas* la taille de la
constante FORMAT. Mauvais nommage.


Mettons.

- On a le nombre 5 en dur, si quelqu'un modifie la chaine de format
sans chercher à comprendre, ça ne marchera plus.


C'est pour cette raison que les 2 lignes sont près l'une de l'autre.
Propose une solution.

- Dans les calculs qui suivent, on perd l'espace séparateur entre les
deux arguments...


Non, j'ai enlevé 1 de plus pour le 0 terminal.
5 = ( 2 * la taille de %s ) + la taille du zero terminal

(...)

len = FORMAT_SIZE + strlen( from ) + strlen( to );


Ici, len ne tient pas compte du fameux espace séparateur des deux
arguments. Par contre, on y trouve le caractère terminal de la chaine
de format.


Ben non !
1 = 1 quoiqu'on puisse imaginer de sa signification.


cmd = malloc( len + 1 );


Pour quoi +1, puisque l'on a compté le caractère terminal de la chaine
de format ?


Ben non, je l'ai enlevé.

Coup de bol, ça rattrape le fameux espace que l'on avait
perdu en cours de route.


Pourquoi tiens-tu à ce que ce soit l'espace entre les 2 opérandes que
j'ai voulu enlever ?


Ou alors, c'était fait exprès dès le départ, mais dans ce cas là, je
plains franchement le mainteneur du bout de code, il va avoir du mal à
retrouver ces petits.


Dans ce cas, il se met à un autre langage.

Et puis, ça manque de commentaires. :)


Je ne pensais pas que c'était nécessaire pour un bout de code aussi
trivial, il semble que si.

Grâce à ton aide, je corrige :
----------------
// ***********
// *ATTENTION* : modifier FORMAT et FORMAT_SIZE conjointement
// ***********
#define FORMAT "/bin/mv %s %s"
// *IMPORTANT* : FORMAT_SIZE n'est pas la taille du string FORMAT, mais
// la taille constante qu'il prend dans la commande.
// ici 5 = ( 2 * la taille prise par %s ) plus la taille du 0 terminal
#define FORMAT_SIZE ( sizeof( FORMAT ) - 5 )
----------------

Merci !

--
http;//harpo.free.fr/


Avatar
Loïc Restoux
Le 29 jan, à 17:35, Harpo papotait :
- On a le nombre 5 en dur, si quelqu'un modifie la chaine de format
sans chercher à comprendre, ça ne marchera plus.


C'est pour cette raison que les 2 lignes sont près l'une de l'autre.
Propose une solution.


Au moins un commentaire, comme tu l'as très bien fait ci-dessous. :)
Sinon, en fait je crois que je m'y prendrais autrement, mais bon, les
trolls sur les traitement de chaines en C sont un peu dépassés.

- Dans les calculs qui suivent, on perd l'espace séparateur entre les
deux arguments...


Non, j'ai enlevé 1 de plus pour le 0 terminal.
5 = ( 2 * la taille de %s ) + la taille du zero terminal

(...)


C'est ce que je craignais, c'était calculé.

[...]

Pourquoi tiens-tu à ce que ce soit l'espace entre les 2 opérandes que
j'ai voulu enlever ?


Comment peux-t-on savoir en fait ? Moi, j'ai fait cette hypothèse.

Ou alors, c'était fait exprès dès le départ, mais dans ce cas là, je
plains franchement le mainteneur du bout de code, il va avoir du mal à
retrouver ces petits.


Dans ce cas, il se met à un autre langage.


Désolé, mais je pose comme postulat qu'un principe de sécurité est
d'avoir du code clair, précis et commenté. Et là, je suis désolé, mais
j'ai du mal à suivre les additions et soustractions qui se baladent.

D'autre part, par mon expérience, je sais très bien que je ne peux
absolument rien supposer sur le niveau du mainteneur et du temps
disponible qu'il a pour corriger un bug ou apporter une évolution à mon
code. Dans un environnemebnt professionnel s'entend (mais bon, j'ai vu
assez d'horreurs dans des logiciels libres fait sur du temps loisir
aussi).

Alors du coup, c'est vrai, je m'efforce d'avoir un raisonnement clair,
et de commenter tout ce qui n'est pas immédiat dans la logique.

Et puis, ça manque de commentaires. :)


Je ne pensais pas que c'était nécessaire pour un bout de code aussi
trivial, il semble que si.

Grâce à ton aide, je corrige :
----------------
// ***********
// *ATTENTION* : modifier FORMAT et FORMAT_SIZE conjointement
// ***********
#define FORMAT "/bin/mv %s %s"
// *IMPORTANT* : FORMAT_SIZE n'est pas la taille du string FORMAT, mais
// la taille constante qu'il prend dans la commande.
// ici 5 = ( 2 * la taille prise par %s ) plus la taille du 0 terminal
#define FORMAT_SIZE ( sizeof( FORMAT ) - 5 )
----------------


:)

C'est un peu caricatural, mais je soutiens que si ça apporte quelque
chose à un relecteur, c'est que ce n'était pas inutile.

--
No fortunes found


Avatar
Harpo
Loïc Restoux wrote:


Désolé, mais je pose comme postulat qu'un principe de sécurité est
d'avoir du code clair, précis et commenté. Et là, je suis désolé, mais
j'ai du mal à suivre les additions et soustractions qui se baladent.


En sachant ce que fait le programme, cela devrait être clair. C'est toi
qui a eu une idée préconçue.

D'autre part, par mon expérience, je sais très bien que je ne peux
absolument rien supposer sur le niveau du mainteneur et du temps
disponible qu'il a pour corriger un bug ou apporter une évolution à
mon code.


On peut supposer qu'il connaît suffisamment le langage pour comprendre
un code simple.

Dans un environnemebnt professionnel s'entend (mais bon,
j'ai vu assez d'horreurs dans des logiciels libres fait sur du temps
loisir aussi).


Il n'y a pas de grandes différences, même si on programme pendant ses
loisirs on peut être plus ou moins bon et aussi s'imposer un timeline
et l'horreur à souvent tendance à croître avec les modifs.

Alors du coup, c'est vrai, je m'efforce d'avoir un raisonnement clair,
et de commenter tout ce qui n'est pas immédiat dans la logique.


Je pense qu'il faut utiliser les commentaires avec parcimonie et les
garder pour les choses importantes, principalement pour dire ce à quoi
corrrespond une variable (lorsque possible sur la même ligne), ce que
fait une fonction et les points du code qui sont inhabituels.
Plus c'est risquer :
. Que les commentaires importants ne se remarquent pas.
. Que les commentaires en général ne soient pas modifiés quand le code
l'est, ce qui est bien pire que pas de commentaires du tout.
De plus, il y a moins de code qui tient sur une page d'écran, ce qui
gêne la lecture.

Dans le code que j'ai posté, j'aurais pu mettre une ligne de commentaire
pour FORMAT_SIZE pour dire ce à quoi il correspond, et je l'aurais sans
doute mis dans un programme plus important.

--
http://harpo.free.fr/

Avatar
Fred
Fred wrote:



Fred wrote in message
<dr25qm$brc$:


Pas besoin de chercher midi à quatorze heures :

sprintf(ch, "mv %s %s", sourcePath, destPath);
system(ch);





Bravo la sécurité !




Il n'était pas question de sécurité dans le cahier des charges ;)



Tu mènes quand même une vie dangereuse...

1 - system( ) n'est pas très sûr, surtout si on est suid / sgid. on ne
peut guère l'employer que lorsqu'on est sûr que le programme ne sera
pas utilisé dans ces conditions, c'est à dire pas très souvent.
Utiliser plutôt fork( ), execv( )/execve( ), waitpid( ).
2 - sprintf( ) mérite un peu d'attention dans son emploi. Ici, c'est un
des cas où il peut être utilisé car la taille de la chaîne résultante
est prévisible.

Si tu tiens à utiliser system( ), ce qui peut peut-être (?) se concevoir
dans ce cas, il faut prendre un minimum de précautions.
Voilà un petit programme qui appelle mv et s'utilise comme mv :

-----------------------------
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define FORMAT "/bin/mv %s %s"
#define FORMAT_SIZE ( sizeof( FORMAT ) - 5 )

int main( int argc, char ** argv ){
char * cmd = NULL;
char * from;
char * to;
size_t len;
int rc;

if ( argc < 3 ) {
fprintf( stderr, "Not enough arguments.n" );
fprintf( stderr, "usage : mymove from-file to-filen" );
return( EXIT_FAILURE );
}
from = *(argv + 1);
to = *(argv + 2);
len = FORMAT_SIZE + strlen( from ) + strlen( to );
cmd = malloc( len + 1 );
if ( cmd == NULL ) {
fprintf( stderr, "Cannot allocate memory for mv commandn" );
return( EXIT_FAILURE ) ;
}
rc = sprintf( cmd, FORMAT, from, to );
if ( rc != (int) len ) {
fprintf( stderr, "sprintf failed rc=%dn", rc );
return( EXIT_FAILURE ) ;
}
rc = system( cmd );
if ( rc ) {
fprintf( stderr, "Cannot execute command below rc=%d :n'%s'n",
WEXITSTATUS( rc ), cmd );
if ( WIFSIGNALED( rc ) ) {
fprintf( stderr, "Killed by signal %d.n", WTERMSIG( rc ) );
}
return( EXIT_FAILURE ) ;
}
return( EXIT_SUCCESS ) ;
}
-----------------------------


Alors là il faut qu'on m'explique !!

Le programme ci-dessus n'a rien de plus sécurisé que les deux lignes que
j'avais donné (sans prétention). Certes il y a toutes les fioritures
requises pour faire une allocation de chaîne mais un appel du programme
de la forme :

mymv toto "tata; rm -rf /"

n'est pas plus sécurisé.

Il aurait fallu écrire :

#define FORMAT "/bin/mv "%s" "%s""

qui fonctionne aussi avec les noms de fichiers contenant des espaces.

D'autre part, les problèmes de sécurité qui peuvent survenir avec les
bits suid et sgid seront identiques, qu'on utilise system ou fork/exec.
C'est à l'administrateur de gérer ce genre de droits.
La sécurité est effectivement un critère important mais il ne faut pas
non plus tomber dans la paranoia : on n'est pas sous Windows, un rm * ne
va pas non plus casser tout le systeme, sauf si les droits sont gérés
n'importe comment. Un système d'exploitation digne de ce nom fourni des
outils et on ne va pas tout ré-écrire chaque fois par peur des chevaux
de Troie. Pour ma part, je ne vois pas le mal à appeler une commande
externe à partir d'un programme.

La question de départ était une question simple, la réponse que j'y ai
apporté était simple. La question ne portait pas sur l'allocation
mémoire, la gestion des codes de retour, l'analyse des paramètres ou les
traitements de signaux, donc inutile de dériver dans cette voie si ce
n'est pour refaire un sempiternel troll à la suite d'une question simple.
Il est d'ailleurs très intéressant de constater que les trolls se
forment toujours sur les questions simples, jamais sur les complexes...
Ca doit être un signe...

A+
Fred




Avatar
lhabert
Fred :

Le programme ci-dessus n'a rien de plus sécurisé que les deux lignes que
j'avais donné (sans prétention). Certes il y a toutes les fioritures
requises pour faire une allocation de chaîne mais un appel du programme
de la forme :

mymv toto "tata; rm -rf /"

n'est pas plus sécurisé.


En effet.

Il aurait fallu écrire :

#define FORMAT "/bin/mv "%s" "%s""

qui fonctionne aussi avec les noms de fichiers contenant des espaces.


Oui, mais ça va louzer si il y a des « " » ou des « ` », c'est pas vraiment
mieux.

D'autre part, les problèmes de sécurité qui peuvent survenir avec les
bits suid et sgid seront identiques, qu'on utilise system ou fork/exec.
C'est à l'administrateur de gérer ce genre de droits.
La sécurité est effectivement un critère important mais il ne faut pas
non plus tomber dans la paranoia : on n'est pas sous Windows, un rm * ne
va pas non plus casser tout le systeme, sauf si les droits sont gérés
n'importe comment. Un système d'exploitation digne de ce nom fourni des
outils et on ne va pas tout ré-écrire chaque fois par peur des chevaux
de Troie.


Le problème, en l'occurence, c'est que l'outil en question (system) est
lourdissime à utiliser correctement.

Pour ma part, je ne vois pas le mal à appeler une commande externe à
partir d'un programme.


Ce n'est pas la question.

Avatar
Nicolas George
Fred wrote in message
<drkmr1$m1a$:
Il aurait fallu écrire :

#define FORMAT "/bin/mv "%s" "%s""

qui fonctionne aussi avec les noms de fichiers contenant des espaces.


Mais pas plus. Ça va échouer dès que le nom de fichier comporte un ", un ,
un ` ou un $, et probablement quelques autres. Sans compter, évidemment, les
cas où le premier nom de fichier commence par un -.

La sécurité est effectivement un critère important mais il ne faut pas
non plus tomber dans la paranoia : on n'est pas sous Windows, un rm * ne
va pas non plus casser tout le systeme, sauf si les droits sont gérés
n'importe comment.


Dans le monde où je vis, le système se trouve en téléchargement gratuit sur
Internet, ou sur CD dans des revues à 5 EUR, tandis que les données
personnelles perdues le sont définitivement par rapport au dernier backup.
Une perte de données personnelles est donc plus grave qu'une dégât sur le
système.

Un système d'exploitation digne de ce nom fourni des
outils et on ne va pas tout ré-écrire chaque fois par peur des chevaux
de Troie. Pour ma part, je ne vois pas le mal à appeler une commande
externe à partir d'un programme.


Le mal est de ne pas le faire correctement. Comme tu le dis, on n'est pas
sous windows, alors autant en profiter pour faire des programmes fiables et
sûrs.

Il est _possible_ d'appeler une commande externe de manière fiable, mais ce
n'est pas aussi simple qu'un sprintf et un system. Prétendre le contraire et
promouvoir l'utilisation de méthode bancales fait énormément de tort à Unix.

Avatar
Paul Gaborit
À (at) Mon, 30 Jan 2006 09:52:00 +0000 (UTC),
(Luc Habert) écrivait (wrote):
Le problème, en l'occurence, c'est que l'outil en question (system) est
lourdissime à utiliser correctement.


Le vrai problème du point de vue sécurité, c'est que la fonction
'system' ne devrait pas exister... Si on tient absolument à passer par
une commande externe (comme '/bin/mv' ou '/bin/cp'), on peut tout
faire via du (v)fork/exec(l|v|ve|...) sans jongler inutilement avec un
éventuel shell. Là, on peut être sûr de passer les bons arguments et
le bon environnement à la bonne commande.

--
Paul Gaborit - <http://perso.enstimac.fr/~gaborit/>

Avatar
lhabert
Paul Gaborit :

Le vrai problème du point de vue sécurité, c'est que la fonction
'system' ne devrait pas exister... Si on tient absolument à passer par
une commande externe (comme '/bin/mv' ou '/bin/cp'), on peut tout
faire via du (v)fork/exec(l|v|ve|...) sans jongler inutilement avec un
éventuel shell. Là, on peut être sûr de passer les bons arguments et
le bon environnement à la bonne commande.


On est bien d'accord. Enfin, c'est quand même un peu pénible de faire le
fork et cie à la main. Ça serait bien d'avoir une fonction system qui prenne
un argv au lieu d'une string.

Avatar
Stephane Chazelas
On Mon, 30 Jan 2006 10:36:47 +0100, Fred wrote:
[...]
Le programme ci-dessus n'a rien de plus sécurisé que les deux lignes que
j'avais donné (sans prétention). Certes il y a toutes les fioritures
requises pour faire une allocation de chaîne mais un appel du programme
de la forme :

mymv toto "tata; rm -rf /"

n'est pas plus sécurisé.

Il aurait fallu écrire :

#define FORMAT "/bin/mv "%s" "%s""


Pas mieux.

Si on tient absolument a utiliser un shell (via system(3)), on
peut utiliser des variables d'environnement:

#define MV_CMD "exec /bin/mv -- "$a" "$b""

et fare:

if (setenv("a", file1, 1) == 0 &&
setenv("b", file2, 1) == 0)
system(MV_CMD);

Sinon, il faut soit escaper les characters speciaux du shell
soit-meme, soit se passer du shell.

--
Stephane

Avatar
Nicolas George
Luc Habert wrote in message <drkson$1qat$:
On est bien d'accord. Enfin, c'est quand même un peu pénible de faire le
fork et cie à la main. Ça serait bien d'avoir une fonction system qui prenne
un argv au lieu d'une string.


Il y a ça dans les bibliothèques bien nées. Typiquement, dans glib :

char args[] = { "/bin/mv", "--", file1, file2, NULL };
if(!g_spawn_sync(NULL, args, NULL, 0, NULL, NULL, NULL, NULL,
&status, &error) {
...
}

À noter quand même que ça suppose toujours de soit faire confiance à $PATH
soit être sûr que mv est dans /bin/mv et pas dans /usr/bin/mv.

1 2 3