xref: /titanic_52/usr/src/cmd/cpio/utils.c (revision 98a55d335316c9ad7a1e0095bfa1c3d0bbd4ab09)
1  /*
2   * CDDL HEADER START
3   *
4   * The contents of this file are subject to the terms of the
5   * Common Development and Distribution License (the "License").
6   * You may not use this file except in compliance with the License.
7   *
8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9   * or http://www.opensolaris.org/os/licensing.
10   * See the License for the specific language governing permissions
11   * and limitations under the License.
12   *
13   * When distributing Covered Code, include this CDDL HEADER in each
14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15   * If applicable, add the following below this CDDL HEADER, with the
16   * fields enclosed by brackets "[]" replaced with your own identifying
17   * information: Portions Copyright [yyyy] [name of copyright owner]
18   *
19   * CDDL HEADER END
20   */
21  /*
22   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23   * Use is subject to license terms.
24   */
25  
26  /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27  /*	All Rights Reserved					*/
28  
29  /*
30   * Portions of this source code were derived from Berkeley 4.3 BSD
31   * under license from the Regents of the University of California.
32   */
33  
34  #include <stdio.h>
35  #include <stdlib.h>
36  #include <stdarg.h>
37  #include <string.h>
38  #include <sys/stat.h>
39  #include <unistd.h>
40  #include <ctype.h>
41  #include <limits.h>
42  #include <errno.h>
43  #include <fcntl.h>
44  
45  #include "cpio.h"
46  
47  /*
48   * Allocation wrappers.  Used to centralize error handling for
49   * failed allocations.
50   */
51  static void *
52  e_alloc_fail(int flag)
53  {
54  	if (flag == E_EXIT)
55  		msg(EXTN, "Out of memory");
56  
57  	return (NULL);
58  }
59  
60  /*
61   *  Note: unlike the other e_*lloc functions, e_realloc does not zero out the
62   *  additional memory it returns.  Ensure that you do not trust its contents
63   *  when you call it.
64   */
65  void *
66  e_realloc(int flag, void *old, size_t newsize)
67  {
68  	void *ret = realloc(old, newsize);
69  
70  	if (ret == NULL) {
71  		return (e_alloc_fail(flag));
72  	}
73  
74  	return (ret);
75  }
76  
77  char *
78  e_strdup(int flag, const char *arg)
79  {
80  	char *ret = strdup(arg);
81  
82  	if (ret == NULL) {
83  		return (e_alloc_fail(flag));
84  	}
85  
86  	return (ret);
87  }
88  
89  void *
90  e_valloc(int flag, size_t size)
91  {
92  	void *ret = valloc(size);
93  
94  	if (ret == NULL) {
95  		return (e_alloc_fail(flag));
96  	}
97  
98  	return (ret);
99  }
100  
101  void *
102  e_zalloc(int flag, size_t size)
103  {
104  	void *ret = malloc(size);
105  
106  	if (ret == NULL) {
107  		return (e_alloc_fail(flag));
108  	}
109  
110  	(void) memset(ret, 0, size);
111  	return (ret);
112  }
113  
114  /*
115   * Simple printf() which only support "%s" conversion.
116   * We need secure version of printf since format string can be supplied
117   * from gettext().
118   */
119  void
120  str_fprintf(FILE *fp, const char *fmt, ...)
121  {
122  	const char *s = fmt;
123  	va_list	ap;
124  
125  	va_start(ap, fmt);
126  	while (*s != '\0') {
127  		if (*s != '%') {
128  			(void) fputc(*s++, fp);
129  			continue;
130  		}
131  		s++;
132  		if (*s != 's') {
133  			(void) fputc(*(s - 1), fp);
134  			(void) fputc(*s++, fp);
135  			continue;
136  		}
137  		(void) fputs(va_arg(ap, char *), fp);
138  		s++;
139  	}
140  	va_end(ap);
141  }
142  
143  /*
144   * Step through a file discovering and recording pairs of data and hole
145   * offsets. Returns a linked list of data/hole offset pairs of a file.
146   * If there is no holes found, NULL is returned.
147   *
148   * Note: According to lseek(2), only filesystems which support
149   * fpathconf(_PC_MIN_HOLE_SIZE) support SEEK_HOLE.  For filesystems
150   * that do not supply information about holes, the file will be
151   * represented as one entire data region.
152   */
153  static holes_list_t *
154  get_holes_list(int fd, off_t filesz, size_t *countp)
155  {
156  	off_t	data, hole;
157  	holes_list_t *hlh, *hl, **hlp;
158  	size_t	cnt;
159  
160  	if (filesz == 0 || fpathconf(fd, _PC_MIN_HOLE_SIZE) < 0)
161  		return (NULL);
162  
163  	cnt = 0;
164  	hole = 0;
165  	hlh = NULL;
166  	hlp = &hlh;
167  
168  	while (hole < filesz) {
169  		if ((data = lseek(fd, hole, SEEK_DATA)) == -1) {
170  			/* no more data till the end of file */
171  			if (errno == ENXIO) {
172  				data = filesz;
173  			} else {
174  				/* assume data starts from the * beginning */
175  				data = 0;
176  			}
177  		}
178  		if ((hole = lseek(fd, data, SEEK_HOLE)) == -1) {
179  			/* assume that data ends at the end of file */
180  			hole = filesz;
181  		}
182  		if (data == 0 && hole == filesz) {
183  			/* no holes */
184  			break;
185  		}
186  		hl = e_zalloc(E_EXIT, sizeof (holes_list_t));
187  		hl->hl_next = NULL;
188  
189  		/* set data and hole */
190  		hl->hl_data = data;
191  		hl->hl_hole = hole;
192  
193  		*hlp = hl;
194  		hlp = &hl->hl_next;
195  		cnt++;
196  	}
197  	if (countp != NULL)
198  		*countp = cnt;
199  
200  	/*
201  	 * reset to the beginning, otherwise subsequent read calls would
202  	 * get EOF
203  	 */
204  	(void) lseek(fd, 0, SEEK_SET);
205  
206  	return (hlh);
207  }
208  
209  /*
210   * Calculate the real data size in the sparse file.
211   */
212  static off_t
213  get_compressed_filesz(holes_list_t *hlh)
214  {
215  	holes_list_t *hl;
216  	off_t	size;
217  
218  	size = 0;
219  	for (hl = hlh; hl != NULL; hl = hl->hl_next) {
220  		size += (hl->hl_hole - hl->hl_data);
221  	}
222  	return (size);
223  }
224  
225  /*
226   * Convert val to digit string and put it in str. The next address
227   * of the last digit is returned.
228   */
229  static char *
230  put_value(off_t val, char *str)
231  {
232  	size_t	len;
233  	char	*digp, dbuf[ULL_MAX_SIZE + 1];
234  
235  	dbuf[ULL_MAX_SIZE] = '\0';
236  	digp = ulltostr((u_longlong_t)val, &dbuf[ULL_MAX_SIZE]);
237  	len = &dbuf[ULL_MAX_SIZE] - digp;
238  	(void) memcpy(str, digp, len);
239  
240  	return (str + len);
241  }
242  
243  /*
244   * Put data/hole offset pair into string in the following
245   * sequence.
246   * <data> <sp> <hole> <sp>
247   */
248  static void
249  store_sparse_string(holes_list_t *hlh, char *str, size_t *szp)
250  {
251  	holes_list_t *hl;
252  	char	*p;
253  
254  	p = str;
255  	for (hl = hlh; hl != NULL; hl = hl->hl_next) {
256  		p = put_value(hl->hl_data, p);
257  		*p++ = ' ';
258  		p = put_value(hl->hl_hole, p);
259  		*p++ = ' ';
260  	}
261  	*--p = '\0';
262  	if (szp != NULL)
263  		*szp = p - str;
264  }
265  
266  /*
267   * Convert decimal str into unsigned long long value. The end pointer
268   * is returned.
269   */
270  static const char *
271  get_ull_tok(const char *str, uint64_t *ulp)
272  {
273  	uint64_t ul;
274  	char	*np;
275  
276  	while (isspace(*str))
277  		str++;
278  	if (!isdigit(*str))
279  		return (NULL);
280  
281  	errno = 0;
282  	ul = strtoull(str, &np, 10);
283  	if (ul == ULLONG_MAX && errno == ERANGE)
284  		return (NULL);		/* invalid value */
285  	if (*np != ' ' && *np != '\0')
286  		return (NULL);		/* invalid input */
287  
288  	*ulp = ul;
289  	return (np);
290  }
291  
292  static void
293  free_holesdata(holes_info_t *hi)
294  {
295  	holes_list_t	*hl, *nhl;
296  
297  	for (hl = hi->holes_list; hl != NULL; hl = nhl) {
298  		nhl = hl->hl_next;
299  		free(hl);
300  	}
301  	hi->holes_list = NULL;
302  
303  	if (hi->holesdata != NULL)
304  		free(hi->holesdata);
305  	hi->holesdata = NULL;
306  }
307  
308  /*
309   * When a hole is detected, non NULL holes_info pointer is returned.
310   * If we are in copy-out mode, holes_list is converted to string (holesdata)
311   * which will be prepended to file contents. The holesdata is a character
312   * string and in the format of:
313   *
314   * <data size(%10u)><SP><file size(%llu)><SP>
315   *   <SP><data off><SP><hole off><SP><data off><SP><hole off> ...
316   *
317   * This string is parsed by parse_holesholes() in copy-in mode to restore
318   * the sparse info.
319   */
320  holes_info_t *
321  get_holes_info(int fd, off_t filesz, boolean_t pass_mode)
322  {
323  	holes_info_t *hi;
324  	holes_list_t *hl;
325  	char	*str, hstr[MIN_HOLES_HDRSIZE + 1];
326  	size_t	ninfo, len;
327  
328  	if ((hl = get_holes_list(fd, filesz, &ninfo)) == NULL)
329  		return (NULL);
330  
331  	hi = e_zalloc(E_EXIT, sizeof (holes_info_t));
332  	hi->holes_list = hl;
333  
334  	if (!pass_mode) {
335  		str = e_zalloc(E_EXIT,
336  		    MIN_HOLES_HDRSIZE + ninfo * (ULL_MAX_SIZE * 2));
337  		/*
338  		 * Convert into string data, and place it to after
339  		 * the first 2 fixed entries.
340  		 */
341  		store_sparse_string(hl, str + MIN_HOLES_HDRSIZE, &len);
342  
343  		/*
344  		 * Add the first two fixed entries. The size of holesdata
345  		 * includes '\0' at the end of data
346  		 */
347  		(void) sprintf(hstr, "%10lu %20llu ",
348  		    (ulong_t)MIN_HOLES_HDRSIZE + len + 1, filesz);
349  		(void) memcpy(str, hstr, MIN_HOLES_HDRSIZE);
350  
351  		/* calc real file size without holes */
352  		hi->data_size = get_compressed_filesz(hl);
353  		hi->holesdata = str;
354  		hi->holesdata_sz = MIN_HOLES_HDRSIZE + len + 1;
355  	}
356  	return (hi);
357  }
358  
359  /*
360   * The holesdata information is in the following format:
361   * <data size(%10u)><SP><file size(%llu)><SP>
362   *   <SP><data off><SP><hole off><SP><data off><SP><hole off> ...
363   * read_holes_header() allocates holes_info_t, and read the first 2
364   * entries (data size and file size). The rest of holesdata is
365   * read by parse_holesdata().
366   */
367  holes_info_t *
368  read_holes_header(const char *str, off_t filesz)
369  {
370  	holes_info_t	*hi;
371  	uint64_t	ull;
372  
373  	hi = e_zalloc(E_EXIT, sizeof (holes_info_t));
374  
375  	/* read prepended holes data size */
376  	if ((str = get_ull_tok(str, &ull)) == NULL || *str != ' ') {
377  bad:
378  		free(hi);
379  		return (NULL);
380  	}
381  	hi->holesdata_sz = (size_t)ull;
382  
383  	/* read original(expanded) file size */
384  	if (get_ull_tok(str, &ull) == NULL)
385  		goto bad;
386  	hi->orig_size = (off_t)ull;
387  
388  	/* sanity check */
389  	if (hi->holesdata_sz > filesz ||
390  	    hi->holesdata_sz <= MIN_HOLES_HDRSIZE) {
391  		goto bad;
392  	}
393  	return (hi);
394  }
395  
396  int
397  parse_holesdata(holes_info_t *hi, const char *str)
398  {
399  	holes_list_t	*hl, **hlp;
400  	uint64_t	ull;
401  	off_t		loff;
402  
403  	/* create hole list */
404  	hlp = &hi->holes_list;
405  	while (*str != '\0') {
406  		hl = e_zalloc(E_EXIT, sizeof (holes_list_t));
407  		/* link list */
408  		hl->hl_next = NULL;
409  		*hlp = hl;
410  		hlp = &hl->hl_next;
411  
412  		/* read the string token for data */
413  		if ((str = get_ull_tok(str, &ull)) == NULL)
414  			goto bad;
415  		hl->hl_data = (off_t)ull;
416  
417  		/* there must be single blank space in between */
418  		if (*str != ' ')
419  			goto bad;
420  
421  		/* read the string token for hole */
422  		if ((str = get_ull_tok(str, &ull)) == NULL)
423  			goto bad;
424  		hl->hl_hole = (off_t)ull;
425  	}
426  
427  	/* check to see if offset is in ascending order */
428  	loff = -1;
429  	for (hl = hi->holes_list; hl != NULL; hl = hl->hl_next) {
430  		if (loff >= hl->hl_data)
431  			goto bad;
432  		loff = hl->hl_data;
433  		/* data and hole can be equal */
434  		if (loff > hl->hl_hole)
435  			goto bad;
436  		loff = hl->hl_hole;
437  	}
438  	/* The last hole offset should match original file size */
439  	if (hi->orig_size != loff) {
440  bad:
441  		free_holesdata(hi);
442  		return (1);
443  	}
444  
445  	hi->data_size = get_compressed_filesz(hi->holes_list);
446  
447  	return (0);
448  }
449  
450  void
451  free_holes_info(holes_info_t *hi)
452  {
453  	free_holesdata(hi);
454  	free(hi);
455  }
456