compress.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /*
  2. * compress.c -- part of compress.mod
  3. * uses the compression library libz to compress and uncompress the
  4. * userfiles during the sharing process
  5. *
  6. * Written by Fabian Knittel <fknittel@gmx.de>. Based on zlib examples
  7. * by Jean-loup Gailly and Miguel Albrecht.
  8. *
  9. */
  10. /*
  11. * Copyright (C) 2000, 2001, 2002 Eggheads Development Team
  12. *
  13. * This program is free software; you can redistribute it and/or
  14. * modify it under the terms of the GNU General Public License
  15. * as published by the Free Software Foundation; either version 2
  16. * of the License, or (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with this program; if not, write to the Free Software
  25. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  26. */
  27. #define MODULE_NAME "compress"
  28. #define MAKING_COMPRESS
  29. #include <string.h>
  30. #include <errno.h>
  31. #include <zlib.h>
  32. #include "src/mod/module.h"
  33. #include "share.mod/share.h"
  34. #ifdef HAVE_MMAP
  35. # include <sys/types.h>
  36. # include <sys/mman.h>
  37. # include <sys/stat.h>
  38. #endif /* HAVE_MMAP */
  39. #include "compress.h"
  40. #define BUFLEN 512
  41. static Function *global = NULL,
  42. *share_funcs = NULL;
  43. static unsigned int compressed_files; /* Number of files compressed. */
  44. static unsigned int uncompressed_files; /* Number of files uncompressed. */
  45. static unsigned int share_compressed = 1; /* Compress userfiles when sharing? */
  46. static unsigned int compress_level = 9; /* Default compression used. */
  47. static int uncompress_to_file(char *f_src, char *f_target);
  48. static int compress_to_file(char *f_src, char *f_target, int mode_num);
  49. static int compress_file(char *filename, int mode_num);
  50. static int uncompress_file(char *filename);
  51. static int is_compressedfile(char *filename);
  52. #include "tclcompress.c"
  53. /*
  54. * Misc functions.
  55. */
  56. static int is_compressedfile(char *filename)
  57. {
  58. char buf1[50], buf2[50];
  59. FILE *fin;
  60. register int len1, len2, i;
  61. egg_memset(buf1, 0, 50);
  62. egg_memset(buf2, 0, 50);
  63. if (!is_file(filename))
  64. return COMPF_FAILED;
  65. /* Read data with zlib routines.
  66. */
  67. fin = gzopen(filename, "rb");
  68. if (!fin)
  69. return COMPF_FAILED;
  70. len1 = gzread(fin, buf1, sizeof(buf1));
  71. if (len1 < 0)
  72. return COMPF_FAILED;
  73. if (gzclose(fin) != Z_OK)
  74. return COMPF_FAILED;
  75. /* Read raw data.
  76. */
  77. fin = fopen(filename, "rb");
  78. if (!fin)
  79. return COMPF_FAILED;
  80. len2 = fread(buf2, 1, sizeof(buf2), fin);
  81. if (ferror(fin))
  82. return COMPF_FAILED;
  83. fclose(fin);
  84. /* Compare what we found.
  85. */
  86. if (len1 != len2)
  87. return COMPF_COMPRESSED;
  88. for (i = 0; i < sizeof(buf1); i++)
  89. if (buf1[i] != buf2[i])
  90. return COMPF_COMPRESSED;
  91. return COMPF_UNCOMPRESSED;
  92. }
  93. /*
  94. * General compression / uncompression functions
  95. */
  96. /* Uncompresses a file `f_src' and saves it as `f_target'.
  97. */
  98. static int uncompress_to_file(char *f_src, char *f_target)
  99. {
  100. char buf[BUFLEN];
  101. int len;
  102. FILE *fin, *fout;
  103. if (!is_file(f_src)) {
  104. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': not a file.",
  105. f_src);
  106. return COMPF_ERROR;
  107. }
  108. fin = gzopen(f_src, "rb");
  109. if (!fin) {
  110. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': gzopen failed.",
  111. f_src);
  112. return COMPF_ERROR;
  113. }
  114. fout = fopen(f_target, "wb");
  115. if (!fout) {
  116. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': open failed: %s.",
  117. f_src, strerror(errno));
  118. return COMPF_ERROR;
  119. }
  120. while (1) {
  121. len = gzread(fin, buf, sizeof(buf));
  122. if (len < 0) {
  123. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': gzread failed.",
  124. f_src);
  125. return COMPF_ERROR;
  126. }
  127. if (!len)
  128. break;
  129. if ((int) fwrite(buf, 1, (unsigned int) len, fout) != len) {
  130. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': fwrite failed: %s.",
  131. f_src, strerror(errno));
  132. return COMPF_ERROR;
  133. }
  134. }
  135. if (fclose(fout)) {
  136. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': fclose failed: %s.",
  137. f_src, strerror(errno));
  138. return COMPF_ERROR;
  139. }
  140. if (gzclose(fin) != Z_OK) {
  141. putlog(LOG_MISC, "*", "Failed to uncompress file `%s': gzclose failed.",
  142. f_src);
  143. return COMPF_ERROR;
  144. }
  145. uncompressed_files++;
  146. return COMPF_SUCCESS;
  147. }
  148. /* Enforce limits.
  149. */
  150. inline static void adjust_mode_num(int *mode)
  151. {
  152. if (*mode > 9)
  153. *mode = 9;
  154. else if (*mode < 0)
  155. *mode = 0;
  156. }
  157. #ifdef HAVE_MMAP
  158. /* Attempt to compress in one go, by mmap'ing the file to memory.
  159. */
  160. static int compress_to_file_mmap(FILE *fout, FILE *fin)
  161. {
  162. int len, ifd = fileno(fin);
  163. char *buf;
  164. struct stat st;
  165. /* Find out size of file */
  166. if (fstat(ifd, &st) < 0)
  167. return COMPF_ERROR;
  168. if (st.st_size <= 0)
  169. return COMPF_ERROR;
  170. /* mmap file contents to memory */
  171. buf = mmap(0, st.st_size, PROT_READ, MAP_SHARED, ifd, 0);
  172. if (buf < 0)
  173. return COMPF_ERROR;
  174. /* Compress the whole file in one go */
  175. len = gzwrite(fout, buf, st.st_size);
  176. if (len != (int) st.st_size)
  177. return COMPF_ERROR;
  178. munmap(buf, st.st_size);
  179. fclose(fin);
  180. if (gzclose(fout) != Z_OK)
  181. return COMPF_ERROR;
  182. return COMPF_SUCCESS;
  183. }
  184. #endif /* HAVE_MMAP */
  185. /* Compresses a file `f_src' and saves it as `f_target'.
  186. */
  187. static int compress_to_file(char *f_src, char *f_target, int mode_num)
  188. {
  189. char buf[BUFLEN], mode[5];
  190. FILE *fin, *fout;
  191. int len;
  192. adjust_mode_num(&mode_num);
  193. egg_snprintf(mode, sizeof mode, "wb%d", mode_num);
  194. if (!is_file(f_src)) {
  195. putlog(LOG_MISC, "*", "Failed to compress file `%s': not a file.",
  196. f_src);
  197. return COMPF_ERROR;
  198. }
  199. fin = fopen(f_src, "rb");
  200. if (!fin) {
  201. putlog(LOG_MISC, "*", "Failed to compress file `%s': open failed: %s.",
  202. f_src, strerror(errno));
  203. return COMPF_ERROR;
  204. }
  205. fout = gzopen(f_target, mode);
  206. if (!fout) {
  207. putlog(LOG_MISC, "*", "Failed to compress file `%s': gzopen failed.",
  208. f_src);
  209. return COMPF_ERROR;
  210. }
  211. #ifdef HAVE_MMAP
  212. if (compress_to_file_mmap(fout, fin) == COMPF_SUCCESS) {
  213. compressed_files++;
  214. return COMPF_SUCCESS;
  215. } else {
  216. /* To be on the safe side, close the file before attempting
  217. * to write again.
  218. */
  219. gzclose(fout);
  220. fout = gzopen(f_target, mode);
  221. }
  222. #endif /* HAVE_MMAP */
  223. while (1) {
  224. len = fread(buf, 1, sizeof(buf), fin);
  225. if (ferror(fin)) {
  226. putlog(LOG_MISC, "*", "Failed to compress file `%s': fread failed: %s",
  227. f_src, strerror(errno));
  228. return COMPF_ERROR;
  229. }
  230. if (!len)
  231. break;
  232. if (gzwrite(fout, buf, (unsigned int) len) != len) {
  233. putlog(LOG_MISC, "*", "Failed to compress file `%s': gzwrite failed.",
  234. f_src);
  235. return COMPF_ERROR;
  236. }
  237. }
  238. fclose(fin);
  239. if (gzclose(fout) != Z_OK) {
  240. putlog(LOG_MISC, "*", "Failed to compress file `%s': gzclose failed.",
  241. f_src);
  242. return COMPF_ERROR;
  243. }
  244. compressed_files++;
  245. return COMPF_SUCCESS;
  246. }
  247. /* Compresses a file `filename' and saves it as `filename'.
  248. */
  249. static int compress_file(char *filename, int mode_num)
  250. {
  251. char *temp_fn, randstr[5];
  252. int ret;
  253. /* Create temporary filename. */
  254. temp_fn = nmalloc(strlen(filename) + 5);
  255. make_rand_str(randstr, 4);
  256. strcpy(temp_fn, filename);
  257. strcat(temp_fn, randstr);
  258. /* Compress file. */
  259. ret = compress_to_file(filename, temp_fn, mode_num);
  260. /* Overwrite old file with compressed version. Only do so
  261. * if the compression routine succeeded.
  262. */
  263. if (ret == COMPF_SUCCESS)
  264. movefile(temp_fn, filename);
  265. nfree(temp_fn);
  266. return ret;
  267. }
  268. /* Uncompresses a file `filename' and saves it as `filename'.
  269. */
  270. static int uncompress_file(char *filename)
  271. {
  272. char *temp_fn, randstr[5];
  273. int ret;
  274. /* Create temporary filename. */
  275. temp_fn = nmalloc(strlen(filename) + 5);
  276. make_rand_str(randstr, 4);
  277. strcpy(temp_fn, filename);
  278. strcat(temp_fn, randstr);
  279. /* Uncompress file. */
  280. ret = uncompress_to_file(filename, temp_fn);
  281. /* Overwrite old file with uncompressed version. Only do so
  282. * if the uncompression routine succeeded.
  283. */
  284. if (ret == COMPF_SUCCESS)
  285. movefile(temp_fn, filename);
  286. nfree(temp_fn);
  287. return ret;
  288. }
  289. /*
  290. * Userfile feature releated functions
  291. */
  292. static int uff_comp(int idx, char *filename)
  293. {
  294. debug1("Compressing user file for %s.", dcc[idx].nick);
  295. return compress_file(filename, compress_level);
  296. }
  297. static int uff_uncomp(int idx, char *filename)
  298. {
  299. debug1("Uncompressing user file from %s.", dcc[idx].nick);
  300. return uncompress_file(filename);
  301. }
  302. static int uff_ask_compress(int idx)
  303. {
  304. if (share_compressed)
  305. return 1;
  306. else
  307. return 0;
  308. }
  309. static uff_table_t compress_uff_table[] = {
  310. {"compress", UFF_COMPRESS, uff_ask_compress, 100, uff_comp, uff_uncomp},
  311. {NULL, 0, NULL, 0, NULL, NULL}
  312. };
  313. /*
  314. * Compress module related code
  315. */
  316. static tcl_ints my_tcl_ints[] = {
  317. {"share-compressed", &share_compressed},
  318. {"compress-level", &compress_level},
  319. {NULL, NULL}
  320. };
  321. static int compress_expmem(void)
  322. {
  323. return 0;
  324. }
  325. static int compress_report(int idx, int details)
  326. {
  327. if (details)
  328. dprintf(idx, " Compressed %u file(s), uncompressed %u file(s).\n",
  329. compressed_files, uncompressed_files);
  330. return 0;
  331. }
  332. EXPORT_SCOPE char *compress_start();
  333. static Function compress_table[] =
  334. {
  335. /* 0 - 3 */
  336. (Function) compress_start,
  337. (Function) NULL,
  338. (Function) compress_expmem,
  339. (Function) compress_report,
  340. /* 4 - 7 */
  341. (Function) compress_to_file,
  342. (Function) compress_file,
  343. (Function) uncompress_to_file,
  344. (Function) uncompress_file,
  345. /* 8 - 11 */
  346. (Function) is_compressedfile,
  347. };
  348. char *compress_start(Function *global_funcs)
  349. {
  350. global = global_funcs;
  351. compressed_files = 0;
  352. uncompressed_files = 0;
  353. share_compressed = 0;
  354. compress_level = 9;
  355. module_register(MODULE_NAME, compress_table, 1, 1);
  356. share_funcs = module_depend(MODULE_NAME, "share", 2, 3);
  357. if (!share_funcs) {
  358. module_undepend(MODULE_NAME);
  359. return "This module requires share module 2.3 or later.";
  360. }
  361. uff_addtable(compress_uff_table);
  362. add_tcl_ints(my_tcl_ints);
  363. add_tcl_commands(my_tcl_cmds);
  364. return NULL;
  365. }