Home | History | Annotate | Download | only in cpio
      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