#include /* EXIT_FAILURE */ #include /* perror */ #include #include /* struct stat, futimens */ #include #include #include #include #ifndef CHEMIN_MAX #define CHEMIN_MAX 4096 // par exemple #endif /** Version minimale de la fonction sauve qui peut. Appeler cette fonction n'a de sens qu'après un appel système qui a échoué (puisqu'elle appelle perror()). \param[in] msg Une chaîne de caractère passée à perror() */ void raler (const char * msg) { perror (msg); exit (EXIT_FAILURE); } /** Teste si deux fichiers ont même date de dernière modification et mêmes permissions. Les deux fichiers sont décrits par leurs "struct stat". Le rôle des deux fichiers est symétrique, mais a priori le premier est un fichier du répertoire "source", et le second est un fichier du répertoire de référence. \param[in] src Description du premier fichier \param[in] ref Description du second fichier \return un booléen indiquant si les fichiers sont présumés identiques */ int regular_unchanged (const struct stat * src, const struct stat * ref) { return (src->st_mode == ref->st_mode && src->st_mtim.tv_sec == ref->st_mtim.tv_sec && src->st_mtim.tv_nsec == ref->st_mtim.tv_nsec); } /** Modifie les dates de dernier accès et dernière modification d'un fichier ouvert. Les dates à affecter au fichier "fd" proviennent d'une "struct stat". Le fichier doit être ouvert, et donné par son descripteur. L'appel de cette fonction doit être la dernière opération effectuée sur le fichier avant close(). Cette fonction de renvoie rien. En cas d'erreur de changement des dates, elle appelle la fonction raler(). \param[in] st Description d'un fichier dont il faut reproduire les dates. \param[in] df Descripteur de fichier correctement ouvert */ void regular_set_amtime (const struct stat * st, int fd) { struct timespec ut [2] = { st->st_atim, st->st_mtim }; if (futimens (fd, ut) == -1) raler ("futimens"); } void copy_reg (char*nom1,char*nom2) { int fp1; int fp2; fp1=open(nom1,O_RDONLY,0666); if(fp1<0){ perror ("File 2 error"); exit(-1); } fp2=open(nom2,O_WRONLY | O_CREAT,0644); if(fp2<0){ perror ("File 2 error"); close(fp1); exit(-1); } while(1){ ssize_t fp1_size; char buf[1024]; fp1_size=read(fp1,buf,1024); if(fp1_size <0){ perror("error fp1 size"); } if(fp1_size == 0) break; ssize_t a_ecrire=fp1_size; ssize_t ecrit=0; while(a_ecrire >0){ ssize_t fp2_size=write(fp2,&buf[ecrit],a_ecrire); if(fp2_size<0) perror("error fp2_size"); ecrit=ecrit + fp2_size; a_ecrire=a_ecrire -fp2_size; } } if(close(fp1) == -1) raler("Closing file to recopy error"); if(close(fp2) == -1) raler("Closing file to recopy error"); } void backup (char*source, char*ref, char*dest) { DIR*dp1; struct dirent *d1; if ((access(source,F_OK)) == -1 ) raler("Source file inexistant"); if((access(source,X_OK)) == -1) raler("Source non parcourable"); dp1=opendir(source); if(dp1==NULL) raler("Error opening source file"); if(access(ref,F_OK) == -1 ) mkdir(ref,0777); DIR*dp2; dp2=opendir(ref); if(dp2==NULL) raler("Error opening reference directory"); if ((access(dest,F_OK)) == 0 ) raler("Destination directory already exists"); int fp1; fp1=mkdir(dest,0777); if(fp1<0) raler("Error creating destination directory"); DIR*dp3; dp3=opendir(dest); if(dp3==NULL) raler("Error opening destination directory"); while((d1=readdir(dp1)) != NULL){ if((strcmp("..",d1->d_name) != 0) && (strcmp(".",d1->d_name) != 0)) { char source_path[CHEMIN_MAX]=""; strcat(source_path,source); strcat(source_path,"/"); strcat(source_path,d1->d_name); char dest_path[CHEMIN_MAX]=""; strcat(dest_path,dest); strcat(dest_path,"/"); strcat(dest_path,d1->d_name); char ref_path[CHEMIN_MAX]=""; strcat(ref_path,ref); strcat(ref_path,"/"); strcat(ref_path,d1->d_name); int fptmp; struct stat sttmp; fptmp=stat(source_path, &sttmp); if(fptmp == -1) raler("Stat source path error"); printf("Source_path : %s ",source_path); switch (sttmp.st_mode & S_IFMT){ case (S_IFDIR):{ backup(source_path,ref_path,dest_path); break; } case(S_IFLNK) : { char linked[CHEMIN_MAX]; strcat(linked,dest_path); strcat(linked,"/"); ssize_t read = readlink(source_path,linked,CHEMIN_MAX); if(read == -1 ) raler("Readlink"); strcat(linked,linked); symlink(dest_path,linked); break; } case(S_IFREG):{ printf("est un fichier régulier\n"); if(access(ref_path,F_OK) == 0){ int fptmp_ref; struct stat sttmp_ref; fptmp_ref=stat(ref_path, &sttmp_ref); if(fptmp_ref == -1) raler("Stat ref path error"); if(!regular_unchanged(&sttmp,&sttmp_ref)){ copy_reg(source_path,dest_path); int fd_cp=open(dest_path,O_RDWR,0666); if(fd_cp < 0)raler("Ouverture lien"); regular_set_amtime(&sttmp,fd_cp); } else { link(ref_path,dest_path); int fd_link=open(dest_path,O_RDWR,0666); if(fd_link < 0)raler("Ouverture lien"); regular_set_amtime(&sttmp,fd_link); } } else { copy_reg(source_path,dest_path); int fd_cp=open(dest_path,O_RDWR,0666); if(fd_cp < 0)raler("Ouverture lien"); regular_set_amtime(&sttmp,fd_cp); } break; } default : { printf("Fichier ignoré: %s\n",source_path); break; } } } } if((closedir(dp1) == -1) || (closedir(dp2) == -1) || (closedir(dp3) == -1) ) raler("Error closing files"); } int main (int argc, char **argv) { if(argc!=4){ perror("Usage : main "); exit(-1); } backup(argv[1],argv[2],argv[3]); return 0; }