// Packer.c - Ecrit par Xavier Garreau // Licence NOL - http://wfr.tcl.tk/nol // // Compilation: // - OSX : // gcc -DMACOSX -O2 -o zpacker Packer.c && strip zpacker // - Windows : // Compiler comme application console // fichiers VC++ en ligne : http://fichiers.xgarreau.org/zazoupacker/ // // CHANGELOG: // Créé le 04 fév. 2006 // Modifié le 10 fév. 2006: // - Support OSX // - Quelques const ajoutés deci dela // Modifié le 12 fév. 2006: // - appel à umask supprimé sous windows #include #include #include #include #ifdef WIN32 #include #include #else #include #include #include #include #define O_BINARY 0 #define _MAX_PATH PATH_MAX #define open(x, y) open((x), (y), S_IRWXU | S_IRWXG | S_IRWXO) #endif // La fonction qui crée un pack // Le main se charge quand à lui du fonctionnement normal int fait_pack (int fd, int argc, char** argv); const char CHECK_SUFFIX[] = "ESTVALIDE"; typedef struct _datatype { long size; // Limité à 2G par bloc de données sur la plupart des architectures void* content; } datatype; /* La structure packée est la suivante - EXE - données taille_des_données - données taille_des_données - ... - nb de couples (données taille_des_données) - ESTVALIDE le nombre de couples étant stocké comme long */ int main(int argc, char** argv) { int fd, datafd; long nb_couples, i; datatype* data; char buffer[_MAX_PATH]; char* commandarg = NULL; const long suffix_len = sizeof(CHECK_SUFFIX); const long l_size = sizeof(long); // je considère que les read fonctionnent toujours, ça éclaircit le code mais c'est mal // il faut toujours vérifier que read a bien renvoyé le nombre d'octets qu'on lui a demandé // de lire, sinon, on n'a pas lu tout ce qu'on s'attendait à lire. // Dans le cas de ce programme ce n'est pas très grave car on ne risque pas de tomber sur une // fin de fichier mais dans le cas de la prog réseau ça devient vite très grave. // Il faudrait également vérifier que les lseek ne remontent pas trop haut et ne // retournent pas -1 (erreur) // on pourrait enfin séparer les données par un caractère particulier pour s'assurer qu'on // se trouve bien sur une "frontière" quand on est censé y être #ifndef WIN32 umask (S_IWOTH | S_IWGRP); #endif printf ("J'ouvre %s\n", argv[0]); fd = open (argv[0], O_RDONLY | O_BINARY); if (-1 == fd) { return 1; } // On s'est auto ouvert. Cool :) if (argc>1) { return fait_pack (fd, argc, argv); } lseek (fd, -suffix_len, SEEK_END); memset (buffer, 0, sizeof(buffer)); read (fd, buffer, suffix_len); printf ("Check %s\n", argv[0]); if (0 != strcmp(buffer, CHECK_SUFFIX)) { // On n'a pas le suffixe, il n'y a donc pas de pack // On rend la main // il faudrait fermer fd par un close (fd) mais // en pratique, c'est fait par le système ... printf ("Pack %s vide\n", argv[0]); return 0; } lseek (fd, -suffix_len, SEEK_CUR); // Si on arrive là, on a le suffixe en fin de fichier. printf ("Suffixe OK\n", argv[0]); // On peut récupérer le nombre de données packées // On part du principe qu'un long est toujours codé sur le même nombre d'octets // ça peut être faux, rarement, mais ça peut. (La pluspart du temps, un long est codé // sur 4 octets lseek (fd, -l_size, SEEK_CUR); read (fd, &nb_couples, l_size); if (0 == nb_couples) { // S'il n'y a pas de données, rien à faire printf ("Pack vide\n"); return 0; } lseek (fd, -l_size, SEEK_CUR); printf ("Trouvé %lu zones de données\n", nb_couples); // On alloue de la mémoire pour stocker nos données data = (datatype*)malloc(nb_couples * sizeof(datatype)); if (!data) { printf ("Mémoire insuffisante\n"); return 1; } // Ici, tout dépend de ce qu'on veut faire de nos données // On peut les charger en mémoire, si ce sont des ressources // on peut en créer des fichiers comme dans le cas d'un zip ... // je vais prendre le cas d'un "packageur de pages web" pour l'exemple // Je vais partir du principe que les données rangées dans le pack sont // le nom d'un fichier et son contenu, le nom d'un fichier et son contenu, ... // donc, je sors les fichiers du pack et je lance la visualisation // des fichiers ainsi extraits du pack. // Je décide aussi arbitrairement, que le premier fichier sorti est la "page d'accueil" // On peut améliorer le fonctionnement, en stockant des chemins relatifs, pour reconstruire // une arborescence. // On peut également compresser le tout, mais on n'est pas là pour réécrire winrar ou 7zip ;) // On commence par récupérer les tailles de nos "morceaux" i = nb_couples-1; do { lseek (fd, -l_size, SEEK_CUR); read (fd, &(data[i].size), l_size); lseek (fd, -l_size, SEEK_CUR); // On saute les données, on y reviendra plus tard lseek (fd, -data[i].size, SEEK_CUR); } while (i--); // Là on se trouve en tête des données dans l'exe // Avec notre tableau de tailles prérempli // on va lire les données en incrémentant i de 0 à nb_couples-1 // comme on a un nom et un contenu // quand i est pair, c'est un nom donc on ouvre un fichier. // quand i est impair on envoie les données dans le descripteur. datafd = -1; // on ne fait prequ'aucun contrôle d'erreur, c'est très très mal ! for (i=0; i data[i].size ? 1024 : data[i].size; read (fd, buffer, octets_a_lire); write (datafd, buffer, octets_a_lire); octets_ok += octets_a_lire; } } close (datafd); } else { //i pair memset (buffer, 0, sizeof(buffer)); read (fd, buffer, data[i].size); printf ("Extraction de %s\n", buffer); datafd = open (buffer, O_WRONLY | O_CREAT | O_EXCL | O_BINARY); if (-1 == datafd) { printf ("Impossible de créer %s, le fichier existe surement déjà\n", buffer); return 1; } if (0==i) { commandarg = (char*) malloc (strlen(buffer)+1); strcpy (commandarg, buffer); } } // On passe sur la taille, on la connait déjà lseek (fd, l_size, SEEK_CUR); } if (commandarg) { #ifdef WIN32 ShellExecute(NULL, NULL, commandarg, NULL, NULL, SW_SHOWNORMAL); #elif defined MACOSX char* command = (char*) malloc (5+strlen(commandarg)+1); if (command) { strcpy (command, "open "); strcat (command, commandarg); system (command); free (command); } #endif free (commandarg); } // Pas nécessaire ici mais si on faisait autre chose // Il ne faudrait pas oublier de le faire free (data); return 0; } // La fonction qui fait le pack int fait_pack (int fd, int argc, char** argv) { int param; int packfd, datafd; char buffer[4096]; long bufferlen; long taille; long nb_couples = (argc-2)*2; char* pack = argv[1]; const long l_size = sizeof(long); const long suffix_len = sizeof(CHECK_SUFFIX); printf ("Création du pack %s\n", pack); packfd = open (pack, O_WRONLY | O_CREAT | O_EXCL | O_BINARY); if (-1 == packfd) { printf ("Impossible de créer %s, le fichier existe surement déjà\n", pack); return 1; } // on commence par copier l'exécutable dans ce pack do { bufferlen = read (fd, buffer, sizeof(buffer)); if (bufferlen > 0) write (packfd, buffer, bufferlen); else break; } while (1); // Ensuite, on ajoute les fichiers passés en ligne de commande // Cette zone là aussi devraît comprendre beaucoup plus de contrôles d'erreurs // On devrait également comparer le nombre d'octets lus avec la taille du fichier param = 1; while (++param < argc) { // une donnée pour le nom bufferlen=strlen(argv[param]); write (packfd, argv[param], bufferlen); write (packfd, &bufferlen, l_size); printf ("Ajout de %s\n", argv[param]); // une donnée pour le contenu taille = 0; datafd = open (argv[param], O_RDONLY | O_BINARY); if (-1 == datafd) { printf ("Attention, erreur sur %s\n", argv[param]); write (packfd, &taille, l_size); continue; } do { bufferlen = read (datafd, buffer, sizeof(buffer)); if (bufferlen > 0) taille += write (packfd, buffer, bufferlen); else break; } while (1); write (packfd, &taille, l_size); close (datafd); } // on ajoute le nombre de données write (packfd, &nb_couples, l_size); // on ajoute le suffixe write (packfd, CHECK_SUFFIX, suffix_len); close (packfd); return 0; }