1 /*
2    Copyright (C) Andrew Tridgell 1998-2003
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 /* rzip compression - main program */
19 
20 #include "rzip.h"
21 
usage(void)22 static void usage(void)
23 {
24 	printf("rzip %d.%d\n", RZIP_MAJOR_VERSION, RZIP_MINOR_VERSION);
25 	printf("Copright (C) Andrew Tridgell 1998-2003\n\n");
26 	printf("usage: rzip [options] <file...>\n");
27 	printf(" Options:\n");
28 	printf("     -0            fastest (worst) compression\n");
29 	printf("     -6            default compression level\n");
30 	printf("     -9            slowest (best) compression\n");
31 	printf("     -d            decompress\n");
32 	printf("     -o filename   specify the output file name\n");
33 	printf("     -S suffix     specify compressed suffix (default '.rz')\n");
34 	printf("     -f            force overwrite of any existing files\n");
35 	printf("     -k            keep existing files\n");
36 	printf("     -P            show compression progress\n");
37 	printf("     -L level      set compression level\n");
38 	printf("     -V            show version\n");
39 #if 0
40 	/* damn, this will be quite hard to do */
41 	printf("     -t          test compressed file integrity\n");
42 #endif
43 	printf("\nnote that rzip cannot operate on stdin/stdout\n");
44 }
45 
46 
write_magic(int fd_in,int fd_out)47 static void write_magic(int fd_in, int fd_out)
48 {
49 	struct stat st;
50 	char magic[24];
51 	uint32_t v;
52 
53 	memset(magic, 0, sizeof(magic));
54 	strcpy(magic, "RZIP");
55 	magic[4] = RZIP_MAJOR_VERSION;
56 	magic[5] = RZIP_MINOR_VERSION;
57 
58 	if (fstat(fd_in, &st) != 0) {
59 		fatal("bad magic file descriptor!?\n");
60 	}
61 
62 #if HAVE_LARGE_FILES
63 	v = htonl(st.st_size & 0xFFFFFFFF);
64 	memcpy(&magic[6], &v, 4);
65 	v = htonl(st.st_size >> 32);
66 	memcpy(&magic[10], &v, 4);
67 #else
68 	v = htonl(st.st_size);
69 	memcpy(&magic[6], &v, 4);
70 #endif
71 
72 	if (write(fd_out, magic, sizeof(magic)) != sizeof(magic)) {
73 		fatal("Failed to write magic header\n");
74 	}
75 }
76 
read_magic(int fd_in,int fd_out,off_t * expected_size)77 static void read_magic(int fd_in, int fd_out, off_t *expected_size)
78 {
79 	uint32_t v;
80 	char magic[24];
81 
82 	if (read(fd_in, magic, sizeof(magic)) != sizeof(magic)) {
83 		fatal("Failed to read magic header\n");
84 	}
85 
86 	*expected_size = 0;
87 
88 	if (strncmp(magic, "RZIP", 4) != 0) {
89 		fatal("Not an rzip file\n");
90 	}
91 
92 #if HAVE_LARGE_FILES
93 	memcpy(&v, &magic[6], 4);
94 	*expected_size = ntohl(v);
95 	memcpy(&v, &magic[10], 4);
96 	*expected_size |= ((off_t)ntohl(v)) << 32;
97 #else
98 	memcpy(&v, &magic[6], 4);
99 	*expected_size = ntohl(v);
100 #endif
101 
102 }
103 
104 
105 /* preserve ownership and permissions where possible */
preserve_perms(struct rzip_control * control,int fd_in,int fd_out)106 static void preserve_perms(struct rzip_control *control,
107 			   int fd_in, int fd_out)
108 {
109 	struct stat st;
110 
111 	if (fstat(fd_in, &st) != 0) {
112 		fatal("Failed to fstat input file\n");
113 	}
114 	if (fchmod(fd_out, (st.st_mode & 0777)) != 0) {
115 		fatal("Failed to set permissions on %s\n", control->outfile);
116 	}
117 
118 	/* chown fail is not fatal */
119 	fchown(fd_out, st.st_uid, st.st_gid);
120 }
121 
122 
123 
124 /*
125   decompress one file from the command line
126 */
decompress_file(struct rzip_control * control)127 static void decompress_file(struct rzip_control *control)
128 {
129 	int fd_in, fd_out = -1, fd_hist = -1;
130 	off_t expected_size;
131 
132 	if (control->outname) {
133 		control->outfile = strdup(control->outname);
134 	} else {
135 		if (strlen(control->suffix) >= strlen(control->infile) ||
136 		    strcmp(control->suffix,
137 			   control->infile +
138 			   strlen(control->infile) - strlen(control->suffix)) != 0) {
139 			fatal("%s: unknown suffix\n", control->infile);
140 		}
141 
142 		control->outfile = strdup(control->infile);
143 		control->outfile[strlen(control->infile) - strlen(control->suffix)] = 0;
144 	}
145 
146 	fd_in = open(control->infile,O_RDONLY);
147 	if (fd_in == -1) {
148 		fatal("Failed to open %s: %s\n",
149 		      control->infile,
150 		      strerror(errno));
151 	}
152 
153 	if ((control->flags & FLAG_TEST_ONLY) == 0) {
154 		if (control->flags & FLAG_FORCE_REPLACE) {
155 			fd_out = open(control->outfile,O_WRONLY|O_CREAT|O_TRUNC,0666);
156 		} else {
157 			fd_out = open(control->outfile,O_WRONLY|O_CREAT|O_EXCL,0666);
158 		}
159 		if (fd_out == -1) {
160 			fatal("Failed to create %s: %s\n",
161 			      control->outfile, strerror(errno));
162 		}
163 
164 		preserve_perms(control, fd_in, fd_out);
165 
166 		fd_hist = open(control->outfile,O_RDONLY);
167 		if (fd_hist == -1) {
168 			fatal("Failed to open history file %s\n",
169 			      control->outfile);
170 		}
171 	}
172 
173 
174 	read_magic(fd_in, fd_out, &expected_size);
175 	runzip_fd(fd_in, fd_out, fd_hist, expected_size);
176 
177 	if ((control->flags & FLAG_TEST_ONLY) == 0) {
178 		if (close(fd_hist) != 0 ||
179 		    close(fd_out) != 0) {
180 			fatal("Failed to close files\n");
181 		}
182 	}
183 
184 	close(fd_in);
185 
186 	if ((control->flags & (FLAG_KEEP_FILES | FLAG_TEST_ONLY)) == 0) {
187 		if (unlink(control->infile) != 0) {
188 			fatal("Failed to unlink %s: %s\n",
189 			      control->infile, strerror(errno));
190 		}
191 	}
192 
193 	free(control->outfile);
194 }
195 
196 /*
197   compress one file from the command line
198 */
compress_file(struct rzip_control * control)199 static void compress_file(struct rzip_control *control)
200 {
201 	int fd_in, fd_out;
202 
203 	if (strlen(control->suffix) <= strlen(control->infile) &&
204 	    strcmp(control->suffix, control->infile + strlen(control->infile) - strlen(control->suffix)) == 0) {
205 		printf("%s: already has %s suffix\n", control->infile, control->suffix);
206 		return;
207 	}
208 
209 	if (control->outname) {
210 		control->outfile = strdup(control->outname);
211 	} else {
212 		control->outfile = malloc(strlen(control->infile) +
213 					  strlen(control->suffix) + 1);
214 		if (!control->outfile) {
215 			fatal("Failed to allocate outfile name\n");
216 		}
217 		strcpy(control->outfile, control->infile);
218 		strcat(control->outfile, control->suffix);
219 	}
220 
221 	fd_in = open(control->infile,O_RDONLY);
222 	if (fd_in == -1) {
223 		fatal("Failed to open %s: %s\n", control->infile, strerror(errno));
224 	}
225 
226 	if (control->flags & FLAG_FORCE_REPLACE) {
227 		fd_out = open(control->outfile,O_WRONLY|O_CREAT|O_TRUNC,0666);
228 	} else {
229 		fd_out = open(control->outfile,O_WRONLY|O_CREAT|O_EXCL,0666);
230 	}
231 	if (fd_out == -1) {
232 		fatal("Failed to create %s: %s\n", control->outfile, strerror(errno));
233 	}
234 
235 	preserve_perms(control, fd_in, fd_out);
236 
237 	write_magic(fd_in, fd_out);
238 	rzip_fd(control, fd_in, fd_out);
239 
240 	if (close(fd_in) != 0 ||
241 	    close(fd_out) != 0) {
242 		fatal("Failed to close files\n");
243 	}
244 
245 	if ((control->flags & FLAG_KEEP_FILES) == 0) {
246 		if (unlink(control->infile) != 0) {
247 			fatal("Failed to unlink %s: %s\n", control->infile, strerror(errno));
248 		}
249 	}
250 
251 	free(control->outfile);
252 }
253 
main(int argc,char * argv[])254  int main(int argc, char *argv[])
255 {
256 	extern int optind;
257 	int c, i;
258 	struct rzip_control control;
259 
260 	memset(&control, 0, sizeof(control));
261 
262 	control.compression_level = 6;
263 	control.flags = 0;
264 	control.suffix = ".rz";
265 
266 	if (strstr(argv[0], "runzip")) {
267 		control.flags |= FLAG_DECOMPRESS;
268 	}
269 
270 	while ((c = getopt(argc, argv, "h0123456789dS:tVvkfPo:L:")) != -1) {
271 		if (isdigit(c)) {
272 			control.compression_level = c - '0';
273 			continue;
274 		}
275 		switch (c) {
276 		case 'L':
277 			control.compression_level = atoi(optarg);
278 			break;
279 		case 'd':
280 			control.flags |= FLAG_DECOMPRESS;
281 			break;
282 		case 'S':
283 			control.suffix = optarg;
284 			break;
285 		case 'o':
286 			control.outname = optarg;
287 			break;
288 		case 't':
289 			fatal("integrity checking currently not implemented\n");
290 			control.flags |= FLAG_TEST_ONLY;
291 			break;
292 		case 'f':
293 			control.flags |= FLAG_FORCE_REPLACE;
294 			break;
295 		case 'k':
296 			control.flags |= FLAG_KEEP_FILES;
297 			break;
298 		case 'v':
299 			control.verbosity++;
300 			break;
301 		case 'P':
302 			control.flags |= FLAG_SHOW_PROGRESS;
303 			break;
304 		case 'V':
305 			printf("rzip version %d.%d\n",
306 			       RZIP_MAJOR_VERSION, RZIP_MINOR_VERSION);
307 			exit(0);
308 			break;
309 
310 		default:
311 		case 'h':
312 			usage();
313 			return -1;
314 		}
315 	}
316 
317 	argc -= optind;
318 	argv += optind;
319 
320 	if (control.outname && argc > 1) {
321 		fatal("Cannot specify output filename with more than 1 file\n");
322 	}
323 
324 	if (argc < 1) {
325 		usage();
326 		exit(1);
327 	}
328 
329 	for (i=0;i<argc;i++) {
330 		control.infile = argv[i];
331 
332 		if (control.flags & (FLAG_DECOMPRESS | FLAG_TEST_ONLY)) {
333 			decompress_file(&control);
334 		} else {
335 			compress_file(&control);
336 		}
337 	}
338 
339 	return 0;
340 }
341