compress.c 10 KB

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