1 /* @(#)hole.c	1.68 20/07/08 Copyright 1993-2020 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)hole.c	1.68 20/07/08 Copyright 1993-2020 J. Schilling";
6 #endif
7 /*
8  *	Handle files with holes (sparse files)
9  *
10  *	Copyright (c) 1993-2020 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/stdio.h>
27 #include <schily/stdlib.h>
28 #include <schily/unistd.h>
29 #include <schily/errno.h>
30 #include <schily/standard.h>
31 #include "star.h"
32 #define	GT_COMERR		/* #define comerr gtcomerr */
33 #define	GT_ERROR		/* #define error gterror   */
34 #include <schily/schily.h>
35 #include "props.h"
36 #include "table.h"
37 #include "starsubs.h"
38 #include "checkerr.h"
39 #ifdef	sun
40 #include <sys/filio.h>
41 #if	_FIOAI == _FIOOBSOLETE67
42 #undef	_FIOAI
43 #endif
44 #ifdef	_FIOAI
45 #include <sys/fs/ufs_filio.h>
46 #endif
47 #endif	/* sun */
48 
49 #ifdef	SEEK_DEBUG
50 #define	SEEK_DATA	3
51 #define	SEEK_HOLE	4
52 #endif
53 
54 /*
55  * Initial sparse (sp_t) array allocation.
56  * We currently use SPARSE_IN_HDR + SPARSE_EXT_HDR,
57  * if we implement a new POSIX.1-2001 based sparse file handling method, we
58  * need to rething this ad it would make sense to start with 512 (8 KB).
59  */
60 #define	NSPARSE_INIT	25
61 
62 #define	DEBUG
63 #ifdef DEBUG
64 #define	EDEBUG(a)	if (debug) error a
65 #else
66 #define	EDEBUG(a)
67 #endif
68 
69 extern	long	bigcnt;
70 extern	char	*bigptr;
71 
72 extern	BOOL	debug;
73 extern	BOOL	force_hole;
74 extern	BOOL	nullout;
75 extern	BOOL	silent;
76 
77 /*
78  * XXX If we have really big files, there could in theory be more than
79  * XXX 2 billions of hole/nohole pairs in a file.
80  * XXX But if this happens, we would need > 32 GB of RAM for the hole list.
81  */
82 typedef	struct {
83 	FILE	*fh_file;
84 	char	*fh_name;
85 	off_t	fh_size;
86 	off_t	fh_newpos;
87 	sp_t	*fh_sparse;
88 	int	fh_nsparse;
89 	int	fh_spindex;
90 	int	fh_diffs;
91 } fh_t;
92 
93 LOCAL	ssize_t	force_hole_func	__PR((fh_t *fh, char *p, size_t amount));
94 EXPORT	int	get_forced_hole	__PR((FILE *f, FINFO *info));
95 LOCAL	void	bad_sparse_index __PR((fh_t *fh));
96 LOCAL	ssize_t	get_sparse_func	__PR((fh_t *fh, char *p, size_t amount));
97 LOCAL	ssize_t	get_ssparse_func __PR((fh_t *fh, char *p, size_t amount));
98 LOCAL	ssize_t	cmp_sparse_func	__PR((fh_t *fh, char *p, size_t amount));
99 LOCAL	ssize_t	put_sparse_func	__PR((fh_t *fh, char *p, size_t amount));
100 LOCAL	sp_t	*grow_sp_list	__PR((sp_t *sparse, int *nspp));
101 LOCAL	sp_t	*get_sp_list	__PR((FINFO *info, int *nsparsep));
102 LOCAL	int	mk_sp_list	__PR((int *fp, FINFO *info, sp_t **spp));
103 #if	defined(SEEK_HOLE) && defined(SEEK_DATA)
104 LOCAL	int	smk_sp_list	__PR((int f, FINFO *info, sp_t **spp, off_t hpos));
105 #endif
106 LOCAL	int	get_end_hole	__PR((fh_t *fh));
107 LOCAL	int	write_end_hole	__PR((fh_t *fh));
108 EXPORT	int	gnu_skip_extended __PR((FINFO *info));
109 EXPORT	int	get_sparse	__PR((FILE *f, FINFO *info));
110 EXPORT	int	get_as_hole	__PR((FILE *f, FINFO *info));
111 EXPORT	BOOL	cmp_sparse	__PR((FILE *f, FINFO *info));
112 EXPORT	void	put_sparse	__PR((int *fp, FINFO *info));
113 LOCAL	void	put_sp_list	__PR((FINFO *info, sp_t *sparse, int nsparse));
114 EXPORT	BOOL	sparse_file	__PR((int *fp, FINFO *info));
115 
116 #define	vp_force_hole_func ((ssize_t(*)__PR((void *, char *, size_t)))force_hole_func)
117 
118 LOCAL ssize_t
force_hole_func(fh,p,amount)119 force_hole_func(fh, p, amount)
120 	register fh_t	*fh;
121 	register char	*p;
122 		size_t	amount;
123 {
124 	register ssize_t	cnt;
125 
126 	fh->fh_newpos += amount;
127 	if (amount < fh->fh_size &&
128 				cmpnullbytes(bigptr, amount) >= amount) {
129 		if (lseek(fdown(fh->fh_file), fh->fh_newpos, SEEK_SET) == (off_t)-1) {
130 			if (!errhidden(E_WRITE, fh->fh_name)) {
131 				if (!errwarnonly(E_WRITE, fh->fh_name))
132 					xstats.s_rwerrs++;
133 				errmsg("Error seeking '%s'.\n", fh->fh_name);
134 				(void) errabort(E_WRITE, fh->fh_name, TRUE);
135 			}
136 		}
137 
138 		fh->fh_size -= amount;
139 		return (amount);
140 	}
141 	cnt = ffilewrite(fh->fh_file, p, amount);
142 	fh->fh_size -= amount;
143 	return (cnt);
144 }
145 
146 EXPORT BOOL
get_forced_hole(f,info)147 get_forced_hole(f, info)
148 	FILE	*f;
149 	FINFO	*info;
150 {
151 	fh_t	fh;
152 
153 	fh.fh_file = f;
154 	fh.fh_name = info->f_name;
155 	fh.fh_size = info->f_rsize;
156 	fh.fh_newpos = (off_t)0;
157 	return (xt_file(info, vp_force_hole_func, &fh, TBLOCK, "writing"));
158 }
159 
160 LOCAL void
bad_sparse_index(fh)161 bad_sparse_index(fh)
162 	register fh_t	*fh;
163 {
164 	errmsgno(EX_BAD,
165 		"Trying to access sparse aray beyond end (index %d).\n",
166 							fh->fh_spindex);
167 }
168 
169 /*
170  * Get "amount" bytes from the buffer and write it into the file.
171  * If "amount" spans more then a single data chunk in the resulting file,
172  * cut it into several chunks that probably start with a seek and get this
173  * done by get_ssparse_func().
174  */
175 #define	vp_get_sparse_func ((ssize_t(*)__PR((void *, char *, size_t)))get_sparse_func)
176 LOCAL ssize_t
get_sparse_func(fh,p,amount)177 get_sparse_func(fh, p, amount)
178 	register fh_t	*fh;
179 	register char	*p;
180 	register size_t	amount;
181 {
182 	register ssize_t	cnt = 0;
183 	register ssize_t	amt;
184 	off_t			next_hole;
185 	off_t			newpos = fh->fh_newpos;
186 
187 	do {
188 		if (fh->fh_spindex >= fh->fh_nsparse) {
189 			seterrno(EX_BAD);
190 			bad_sparse_index(fh);
191 			return (-1);
192 		}
193 		if (fh->fh_sparse[fh->fh_spindex].sp_offset > newpos) {
194 			newpos = fh->fh_sparse[fh->fh_spindex].sp_offset;
195 		} else {
196 			newpos = fh->fh_newpos;
197 		}
198 		next_hole = fh->fh_sparse[fh->fh_spindex].sp_offset +
199 				fh->fh_sparse[fh->fh_spindex].sp_numbytes;
200 		amt = amount - cnt;
201 		if ((newpos + amt) > next_hole)
202 			amt = next_hole - newpos;
203 		amt = get_ssparse_func(fh, p, amt);
204 		if (amt < 0)
205 			return (-1);
206 		cnt += amt;
207 		p += amt;
208 	} while (cnt < amount);
209 	return (cnt);
210 }
211 
212 /*
213  * Get a single chunk of data from the buffer and write it into the file.
214  */
215 LOCAL ssize_t
get_ssparse_func(fh,p,amount)216 get_ssparse_func(fh, p, amount)
217 	register fh_t	*fh;
218 	register char	*p;
219 		size_t	amount;
220 {
221 	register ssize_t	cnt;
222 
223 	EDEBUG(("amount: %zd newpos: %lld index: %d\n",
224 					amount, (Llong)fh->fh_newpos, fh->fh_spindex));
225 
226 	if (fh->fh_sparse[fh->fh_spindex].sp_offset > fh->fh_newpos) {
227 
228 		EDEBUG(("seek to: %lld\n",
229 				(Llong)fh->fh_sparse[fh->fh_spindex].sp_offset));
230 
231 		if (lseek(fdown(fh->fh_file),
232 				fh->fh_sparse[fh->fh_spindex].sp_offset, SEEK_SET) < 0) {
233 			if (!errhidden(E_WRITE, fh->fh_name)) {
234 				if (!errwarnonly(E_WRITE, fh->fh_name))
235 					xstats.s_rwerrs++;
236 				errmsg("Error seeking '%s'.\n", fh->fh_name);
237 				(void) errabort(E_WRITE, fh->fh_name, TRUE);
238 			}
239 		}
240 
241 		fh->fh_newpos = fh->fh_sparse[fh->fh_spindex].sp_offset;
242 	}
243 	EDEBUG(("write %zd at: %lld\n", amount, (Llong)fh->fh_newpos));
244 
245 	cnt = ffilewrite(fh->fh_file, p, amount);
246 	fh->fh_size -= cnt;
247 	fh->fh_newpos += cnt;
248 
249 	EDEBUG(("off: %lld numb: %lld cnt: %zd off+numb: %lld newpos: %lld index: %d\n",
250 		(Llong)fh->fh_sparse[fh->fh_spindex].sp_offset,
251 		(Llong)fh->fh_sparse[fh->fh_spindex].sp_numbytes, cnt,
252 		(Llong)(fh->fh_sparse[fh->fh_spindex].sp_offset +
253 			fh->fh_sparse[fh->fh_spindex].sp_numbytes),
254 		(Llong)fh->fh_newpos,
255 		fh->fh_spindex));
256 
257 	if ((fh->fh_sparse[fh->fh_spindex].sp_offset +
258 	    fh->fh_sparse[fh->fh_spindex].sp_numbytes)
259 	    <= fh->fh_newpos) {
260 
261 		fh->fh_spindex++;
262 
263 		EDEBUG(("new index: %d\n", fh->fh_spindex));
264 	}
265 	EDEBUG(("return (%zd)\n", cnt));
266 	return (cnt);
267 }
268 
269 #define	vp_cmp_sparse_func ((ssize_t(*)__PR((void *, char *, size_t)))cmp_sparse_func)
270 
271 LOCAL ssize_t
cmp_sparse_func(fh,p,amount)272 cmp_sparse_func(fh, p, amount)
273 	register fh_t	*fh;
274 	register char	*p;
275 		size_t	amount;
276 {
277 	register ssize_t	cnt;
278 		char		*cmp_buf[TBLOCK];
279 
280 	EDEBUG(("amount: %zd newpos: %lld index: %d\n",
281 					amount,
282 					(Llong)fh->fh_newpos, fh->fh_spindex));
283 
284 	if (fh->fh_spindex >= fh->fh_nsparse) {
285 		seterrno(EX_BAD);
286 		bad_sparse_index(fh);
287 		return (-1);
288 	}
289 
290 	/*
291 	 * If we already found diffs we save time and only pass tape ...
292 	 */
293 	if (fh->fh_diffs)
294 		return (amount);
295 
296 	if (fh->fh_sparse[fh->fh_spindex].sp_offset > fh->fh_newpos) {
297 		EDEBUG(("seek to: %lld\n",
298 				(Llong)fh->fh_sparse[fh->fh_spindex].sp_offset));
299 
300 		while (fh->fh_newpos < fh->fh_sparse[fh->fh_spindex].sp_offset) {
301 			register size_t	amt;
302 
303 			amt = min(TBLOCK,
304 				fh->fh_sparse[fh->fh_spindex].sp_offset -
305 				fh->fh_newpos);
306 
307 			cnt = ffileread(fh->fh_file, cmp_buf, amt);
308 			if (cnt != amt)
309 				fh->fh_diffs++;
310 
311 			if (cmpnullbytes(cmp_buf, amt) < cnt)
312 				fh->fh_diffs++;
313 
314 			fh->fh_newpos += cnt;
315 
316 			if (fh->fh_diffs)
317 				return (amount);
318 		}
319 	}
320 	EDEBUG(("read %zd at: %lld\n", amount, (Llong)fh->fh_newpos));
321 
322 	cnt = ffileread(fh->fh_file, cmp_buf, amount);
323 	if (cnt != amount)
324 		fh->fh_diffs++;
325 
326 	if (cmpbytes(cmp_buf, p, cnt) < cnt)
327 		fh->fh_diffs++;
328 
329 	fh->fh_size -= cnt;
330 	fh->fh_newpos += cnt;
331 
332 	EDEBUG(("off: %lld numb: %lld cnt: %zd off+numb: %lld newpos: %lld index: %d\n",
333 		(Llong)fh->fh_sparse[fh->fh_spindex].sp_offset,
334 		(Llong)fh->fh_sparse[fh->fh_spindex].sp_numbytes, cnt,
335 		(Llong)(fh->fh_sparse[fh->fh_spindex].sp_offset +
336 			fh->fh_sparse[fh->fh_spindex].sp_numbytes),
337 		(Llong)fh->fh_newpos,
338 		fh->fh_spindex));
339 
340 	if ((fh->fh_sparse[fh->fh_spindex].sp_offset +
341 	    fh->fh_sparse[fh->fh_spindex].sp_numbytes)
342 	    <= fh->fh_newpos) {
343 
344 		fh->fh_spindex++;
345 
346 		EDEBUG(("new index: %d\n", fh->fh_spindex));
347 	}
348 	EDEBUG(("return (%zd) diffs: %d\n", cnt, fh->fh_diffs));
349 	return (cnt);
350 }
351 
352 #define	vp_put_sparse_func ((ssize_t(*)__PR((void *, char *, size_t)))put_sparse_func)
353 
354 LOCAL ssize_t
put_sparse_func(fh,p,amount)355 put_sparse_func(fh, p, amount)
356 	register fh_t	*fh;
357 	register char	*p;
358 		size_t	amount;
359 {
360 	register ssize_t	cnt;
361 
362 	EDEBUG(("amount: %zd newpos: %lld index: %d\n",
363 					amount,
364 					(Llong)fh->fh_newpos, fh->fh_spindex));
365 
366 	if (fh->fh_spindex < fh->fh_nsparse &&
367 		fh->fh_sparse[fh->fh_spindex].sp_offset > fh->fh_newpos) {
368 
369 		EDEBUG(("seek to: %lld\n",
370 				(Llong)fh->fh_sparse[fh->fh_spindex].sp_offset));
371 
372 		if (!nullout &&
373 		    lseek(*(int *)(fh->fh_file),
374 				fh->fh_sparse[fh->fh_spindex].sp_offset, SEEK_SET) < 0) {
375 			if (!errhidden(E_READ, fh->fh_name)) {
376 				if (!errwarnonly(E_READ, fh->fh_name))
377 					xstats.s_rwerrs++;
378 				errmsg("Error seeking '%s'.\n", fh->fh_name);
379 				(void) errabort(E_READ, fh->fh_name, TRUE);
380 			}
381 		}
382 
383 		fh->fh_newpos = fh->fh_sparse[fh->fh_spindex].sp_offset;
384 	}
385 	/*
386 	 * I former times, cr_file has been called with an amt value of 512,
387 	 * we now try to optimize the I/O for the data chunk size.
388 	 * In case we are not yet at the expected end of the file, we limit the
389 	 * the amount to the end of the current data region, else (when cr_file
390 	 * tries to find whether the file did grow, we just pass the original.
391 	 */
392 	if (fh->fh_spindex < fh->fh_nsparse &&
393 	    (fh->fh_newpos + amount) >
394 	    (fh->fh_sparse[fh->fh_spindex].sp_offset +
395 	    fh->fh_sparse[fh->fh_spindex].sp_numbytes)) {
396 		amount =   (fh->fh_sparse[fh->fh_spindex].sp_offset +
397 			    fh->fh_sparse[fh->fh_spindex].sp_numbytes) -
398 			    fh->fh_newpos;
399 	}
400 	EDEBUG(("read %zd at: %lld\n", amount, (Llong)fh->fh_newpos));
401 
402 	if (nullout) {
403 		cnt = amount;
404 		if (cnt > fh->fh_size)
405 			cnt = fh->fh_size;
406 	} else {
407 		cnt = _fileread((int *)fh->fh_file, p, amount);
408 	}
409 	fh->fh_size -= cnt;
410 	fh->fh_newpos += cnt;
411 
412 	EDEBUG(("off: %lld numb: %lld cnt: %zd off+numb: %lld newpos: %lld index: %d\n",
413 		(Llong)fh->fh_sparse[fh->fh_spindex].sp_offset,
414 		(Llong)fh->fh_sparse[fh->fh_spindex].sp_numbytes, cnt,
415 		(Llong)(fh->fh_sparse[fh->fh_spindex].sp_offset +
416 			fh->fh_sparse[fh->fh_spindex].sp_numbytes),
417 		(Llong)fh->fh_newpos,
418 		fh->fh_spindex));
419 
420 	if ((fh->fh_sparse[fh->fh_spindex].sp_offset +
421 	    fh->fh_sparse[fh->fh_spindex].sp_numbytes)
422 	    <= fh->fh_newpos) {
423 
424 		fh->fh_spindex++;
425 
426 		EDEBUG(("new index: %d\n", fh->fh_spindex));
427 	}
428 	EDEBUG(("return (%zd)\n", cnt));
429 	return (cnt);
430 }
431 
432 LOCAL sp_t *
grow_sp_list(sparse,nspp)433 grow_sp_list(sparse, nspp)
434 	sp_t	*sparse;
435 	int	*nspp;
436 {
437 	sp_t	*new;
438 
439 	if (*nspp < 512)
440 		*nspp *= 2;
441 	else
442 		*nspp += 512;
443 	new = (sp_t *)realloc(sparse, *nspp*sizeof (sp_t));
444 	if (new == 0) {
445 		errmsg("Cannot grow sparse buf.\n");
446 		free(sparse);
447 	}
448 	return (new);
449 }
450 
451 LOCAL sp_t *
get_sp_list(info,nsparsep)452 get_sp_list(info, nsparsep)
453 	FINFO	*info;
454 	int	*nsparsep;
455 {
456 	TCB	tb;
457 	TCB	*ptb = info->f_tcb;
458 	sp_t	*sparse;
459 	int	nsparse = NSPARSE_INIT;
460 	int	extended;
461 	register int	i;
462 	register int	sparse_in_hdr = props.pr_sparse_in_hdr;
463 	register int	ind;
464 		char	*p;
465 	extern long hdrtype;	/* XXX */
466 
467 	EDEBUG(("rsize: %lld\n", (Llong)info->f_rsize));
468 
469 	sparse = (sp_t *)malloc(nsparse*sizeof (sp_t));
470 	if (sparse == 0) {
471 		errmsg("Cannot alloc sparse buf.\n");
472 		*nsparsep = 0;
473 		return (sparse);
474 	}
475 
476 	if (H_TYPE(hdrtype) == H_GNUTAR) {
477 		p = (char *)ptb->gnu_in_dbuf.t_sp;
478 	} else {
479 		p = (char *)ptb->xstar_in_dbuf.t_sp;
480 
481 		if (ptb->xstar_dbuf.t_prefix[0] == '\0' &&
482 		    ptb->xstar_in_dbuf.t_sp[0].t_offset[10] != '\0') {
483 			static	BOOL	warned;
484 			extern	BOOL	nowarn;
485 
486 			if (!nowarn && !warned) {
487 				errmsgno(EX_BAD, "WARNING: Archive uses old sparse format. Please upgrade!\n");
488 				warned = TRUE;
489 			}
490 			sparse_in_hdr = SPARSE_IN_HDR;
491 		}
492 	}
493 
494 	for (i = 0; i < sparse_in_hdr; i++) {
495 		Ullong	ull;
496 
497 		stolli(p, &ull); p += 12;
498 		sparse[i].sp_offset = ull;
499 		stolli(p, &ull);   p += 12;
500 		sparse[i].sp_numbytes = ull;
501 		if (sparse[i].sp_offset == 0 &&
502 		    sparse[i].sp_numbytes == 0)
503 			break;
504 	}
505 #ifdef	DEBUG
506 	/* CSTYLED */
507 	if (debug) for (i = 0; i < sparse_in_hdr; i++) {
508 		error("i: %d offset: %lld numbytes: %lld\n", i,
509 				(Llong)sparse[i].sp_offset,
510 				(Llong)sparse[i].sp_numbytes);
511 		if (sparse[i].sp_offset == 0 &&
512 		    sparse[i].sp_numbytes == 0)
513 			break;
514 	}
515 #endif
516 	ind = sparse_in_hdr-SPARSE_EXT_HDR;
517 
518 	if (H_TYPE(hdrtype) == H_GNUTAR)
519 		extended = ptb->gnu_in_dbuf.t_isextended;
520 	else
521 		extended = ptb->xstar_in_dbuf.t_isextended;
522 
523 	extended |= (sparse_in_hdr == 0);
524 	/*
525 	 * Set "ind" to allow us to set *nsparsep past while loop.
526 	 */
527 	if (!extended) {
528 		info->f_flags |= F_SP_EXTENDED;
529 		ind = 0;
530 	}
531 
532 	EDEBUG(("isextended: %d\n", extended));
533 
534 	ptb = &tb;	/* don't destroy orig TCB */
535 	while (extended) {
536 		if (readblock((char *)ptb, TBLOCK) == EOF) {
537 			free(sparse);
538 			*nsparsep = 0;
539 			return (0);
540 		}
541 		if ((props.pr_flags & PR_GNU_SPARSE_BUG) == 0)
542 			info->f_rsize -= TBLOCK;
543 
544 		EDEBUG(("rsize: %lld\n", (Llong)info->f_rsize));
545 
546 		ind += SPARSE_EXT_HDR;
547 
548 		EDEBUG(("ind: %d\n", ind));
549 
550 		if ((ind+SPARSE_EXT_HDR) > nsparse) {
551 			if ((sparse = grow_sp_list(sparse, &nsparse)) == 0) {
552 				*nsparsep = 0;
553 				return ((sp_t *)0);
554 			}
555 		}
556 		p = (char *)ptb;
557 		for (i = 0; i < SPARSE_EXT_HDR; i++) {
558 			Ullong	ull;
559 
560 			stolli(p, &ull);	p += 12;
561 			sparse[i+ind].sp_offset = ull;
562 			stolli(p, &ull);	p += 12;
563 			sparse[i+ind].sp_numbytes = ull;
564 
565 			EDEBUG(("i: %d offset: %lld numbytes: %lld\n", i,
566 				(Llong)sparse[i+ind].sp_offset,
567 				(Llong)sparse[i+ind].sp_numbytes));
568 
569 			if (sparse[i+ind].sp_offset == 0 &&
570 			    sparse[i+ind].sp_numbytes == 0)
571 				break;
572 		}
573 		extended = ptb->gnu_ext_dbuf.t_isextended;
574 	}
575 	info->f_flags |= F_SP_SKIPPED;
576 	ind += i;
577 	*nsparsep = ind;
578 	EDEBUG(("nsparse: %d\n", ind));
579 	if (ind < 1)
580 		return (sparse);
581 	EDEBUG(("i: %d offset: %lld numbytes: %lld\n", 0,
582 				(Llong)sparse[0].sp_offset,
583 				(Llong)sparse[0].sp_numbytes));
584 	for (i = 1; i < ind; i++) {
585 		if (((sparse[i-1].sp_offset + sparse[i-1].sp_numbytes) >
586 							sparse[i].sp_offset) &&
587 		    (sparse[i].sp_offset != 0 || sparse[i].sp_numbytes != 0)) {
588 			errmsgno(EX_BAD,
589 			"Bad sparse data:   offset %lld, numbytes %lld at idx %d.\n",
590 				(Llong)sparse[i].sp_offset,
591 				(Llong)sparse[i].sp_numbytes, i);
592 			errmsgno(EX_BAD, "Current write position is %lld.\n",
593 				(Llong)(sparse[i-1].sp_offset +
594 					sparse[i-1].sp_numbytes));
595 			free(sparse);
596 			*nsparsep = 0;
597 			return (0);
598 		}
599 		EDEBUG(("i: %d offset: %lld numbytes: %lld\n", i,
600 				(Llong)sparse[i].sp_offset,
601 				(Llong)sparse[i].sp_numbytes));
602 	}
603 	EDEBUG(("rsize: %lld\n", (Llong)info->f_rsize));
604 
605 	return (sparse);
606 }
607 
608 #if	defined(SEEK_HOLE) && defined(SEEK_DATA)
609 /*
610  * The seek based sparse list parser for files.
611  * The interface has been defined in 2004 for Solaris & star and will hopefully
612  * be integrated into a future POSIX standard.
613  */
614 LOCAL int
smk_sp_list(f,info,spp,hpos)615 smk_sp_list(f, info, spp, hpos)
616 	int	f;
617 	FINFO	*info;
618 	sp_t	**spp;
619 	register off_t	hpos;
620 {
621 	register off_t	dpos;
622 	register sp_t	*sparse = *spp;
623 		int	nsparse = NSPARSE_INIT;
624 	register int	i = 0;
625 
626 	*spp = (sp_t *)0;
627 	if (hpos != 0) {			/* Starts with data */
628 		if ((hpos % 512) != 0)		/* Unaligned hole ? */
629 			hpos = (hpos + 511) / 512 * 512;
630 
631 		if (hpos >= info->f_size) {	/* File has no hole */
632 			lseek(f, (off_t)0, SEEK_SET);
633 			free(sparse);
634 			return (0);
635 		}
636 		sparse[i].sp_offset = 0;
637 		sparse[i].sp_numbytes = hpos;
638 
639 		EDEBUG(("i: %d offset: %lld numbytes: %lld\n", i,
640 			(Llong)sparse[i].sp_offset,
641 			(Llong)sparse[i].sp_numbytes));
642 
643 		info->f_rsize += sparse[i].sp_numbytes;
644 		i++;
645 	}
646 	while (hpos < info->f_size) {
647 		dpos = lseek(f, hpos, SEEK_DATA);
648 		if (dpos == (off_t)-1 && geterrno() == ENXIO) {
649 			/*
650 			 * Catch the case "No more data past 'hpos'".
651 			 */
652 			dpos = info->f_size;
653 		} else if (dpos == (off_t)-1) {
654 			if (!errhidden(E_SHRINK, info->f_name)) {
655 				if (!errwarnonly(E_SHRINK, info->f_name))
656 					xstats.s_sizeerrs++;
657 				errmsg(
658 				"'%s': SEEK_DATA file shrunk at offset %lld.\n",
659 				info->f_name, (Llong)hpos);
660 				(void) errabort(E_SHRINK, info->f_name, TRUE);
661 			}
662 			lseek(f, (off_t)0, SEEK_SET);
663 			free(sparse);
664 			return (0);
665 		}
666 		hpos = lseek(f, dpos, SEEK_HOLE);
667 		if (hpos == (off_t)-1 && geterrno() == ENXIO) {
668 			/*
669 			 * Catch the case "No more holes past 'dpos'".
670 			 */
671 			hpos = info->f_size;
672 		} else if (hpos == (off_t)-1) {
673 			if (!errhidden(E_SHRINK, info->f_name)) {
674 				if (!errwarnonly(E_SHRINK, info->f_name))
675 					xstats.s_sizeerrs++;
676 				errmsg(
677 				"'%s': SEEK_HOLE file shrunk at offset %lld.\n",
678 				info->f_name, (Llong)dpos);
679 				(void) errabort(E_SHRINK, info->f_name, TRUE);
680 			}
681 			lseek(f, (off_t)0, SEEK_SET);
682 			free(sparse);
683 			return (0);
684 		}
685 		if (dpos < info->f_size &&	/* Not the hole at EOF ? */
686 		    (dpos % 512) != 0) {	/* Unaligned data ? */
687 			dpos = dpos / 512 * 512;
688 			if (i > 0 &&		/* Combine with previous ? */
689 			    (sparse[i-1].sp_offset +
690 			    sparse[i-1].sp_numbytes) >= dpos) {
691 				i--;
692 				dpos = sparse[i].sp_offset;
693 			}
694 		}
695 		if (hpos < info->f_size &&	/* Not the hole at EOF ? */
696 		    (hpos % 512) != 0) {	/* Unaligned hole ? */
697 			hpos = (hpos + 511) / 512 * 512;
698 			if (hpos > info->f_size)
699 				hpos = info->f_size;
700 		}
701 
702 		sparse[i].sp_offset = dpos;
703 		sparse[i].sp_numbytes = hpos - dpos;
704 
705 		info->f_rsize += sparse[i].sp_numbytes;
706 
707 		EDEBUG(("i: %d offset: %lld numbytes: %lld\n", i,
708 			(Llong)sparse[i].sp_offset,
709 			(Llong)sparse[i].sp_numbytes));
710 
711 		i++;
712 		if (i >= nsparse) {
713 			if ((sparse = grow_sp_list(sparse,
714 					&nsparse)) == 0) {
715 				lseek(f, (off_t)0, SEEK_SET);
716 				return (0);
717 			}
718 		}
719 	}
720 	lseek(f, (off_t)0, SEEK_SET);
721 	*spp = sparse;
722 	return (i);
723 }
724 #endif
725 
726 LOCAL int
mk_sp_list(fp,info,spp)727 mk_sp_list(fp, info, spp)
728 	int	*fp;
729 	FINFO	*info;
730 	sp_t	**spp;
731 {
732 	long	rbuf[32*1024/sizeof (long)];	/* Force it be long aligned */
733 	sp_t	*sparse;
734 	int	nsparse = NSPARSE_INIT;
735 	register ssize_t	amount = 0;
736 	register ssize_t	cnt = 0;
737 	register char		*rbp = (char *)rbuf;
738 	register off_t		pos = (off_t)0;
739 	register int		i = 0;
740 	register BOOL		data = FALSE;
741 	register BOOL		use_ai = FALSE;
742 	register BOOL		is_hole = FALSE;
743 #if	defined(SEEK_HOLE) && defined(SEEK_DATA)
744 #else
745 #ifdef	_FIOAI
746 		int	fai_idx;
747 	struct fioai	fai;
748 	struct fioai	*faip;
749 #define	NFAI	1024
750 	daddr_t		fai_arr[NFAI];
751 #endif	/* _FIOAI */
752 #endif	/* SEEK_HOLE */
753 
754 	*spp = (sp_t *)0;
755 	info->f_rsize = 0;
756 	sparse = (sp_t *)malloc(nsparse*sizeof (sp_t));
757 	if (sparse == 0) {
758 		errmsg("Cannot alloc sparse buf.\n");
759 		return (i);
760 	}
761 
762 #if	defined(SEEK_HOLE) && defined(SEEK_DATA)
763 	/*
764 	 * Error codes: EINVAL -> OS does not support SEEK_HOLE
765 	 * ENOTSUP -> Current filesystem does not support SEEK_HOLE.
766 	 */
767 	if (force_hole)
768 		pos = (off_t)-1;
769 	else
770 		pos = lseek(*fp, (off_t)0, SEEK_HOLE);
771 	if (pos != (off_t)-1) {			/* Use if operational */
772 		*spp = sparse;
773 		return (smk_sp_list(*fp, info, spp, pos));
774 	}
775 	pos = (off_t)0;
776 #else
777 #ifdef	_FIOAI
778 	fai.fai_off = 0;
779 	fai.fai_size = 512;
780 	fai.fai_num = 1;
781 	fai.fai_daddr = fai_arr;
782 	use_ai = ioctl(*fp, _FIOAI, &fai) >= 0;	/* Use if operational */
783 	fai_idx = 0;
784 	fai.fai_num = 0;			/* start at 0 again  */
785 #endif	/* _FIOAI */
786 #endif	/* SEEK_HOLE */
787 
788 	/*
789 	 * Shortcut for files which consist of a single hole and no written
790 	 * data. In that case on some file systems a file may occupy 0 blocks
791 	 * on disk, and the F_ALL_HOLE flag could be set in info->f_flags. In
792 	 * such a case, we can avoid scanning the file on dumb operating
793 	 * systems that do not support SEEK_HOLE nor something equivalent.
794 	 *
795 	 * Before, we made a decision based on F_ALL_HOLE unconditionally but
796 	 * in October 2013, it turned out that at least NetAPP stores
797 	 * files up to 64 bytes in the inode and then returns
798 	 * sp->st_blocks == 0 for a non sparse file. We now moved this decision
799 	 * past the check for a working SEEK_HOLE in hope that noone will
800 	 * implement a filesystem that hides more than DEV_BSIZE without
801 	 * supporting SEEK_HOLE.
802 	 *
803 	 * Update: There seems to be a major problem in btrfs:
804 	 * There was a report that btrfs reports sp->st_blocks == 0 for a file
805 	 * with an 8 GB hole followed by 512 bytes of 'A'. While this is most
806 	 * likely a btrfs bug, in theory a filesystem could compress the data
807 	 * past the hole and hold the compressed data inside the inode. As a
808 	 * result, we needed to disable the F_ALL_HOLE check.
809 	 */
810 	if (info->f_flags & F_ALL_HOLE) {
811 		pos = info->f_size;
812 		goto scan_done;
813 	}
814 
815 	for (;;) {
816 		if (use_ai) {
817 #if	defined(SEEK_HOLE) && defined(SEEK_DATA)
818 			/* EMPTY */
819 #else
820 #ifdef	_FIOAI
821 			if (fai_idx >= fai.fai_num) {
822 				fai.fai_off = pos;
823 				fai.fai_size = 512 * NFAI;
824 				fai.fai_num = NFAI;
825 			retry:
826 				ioctl(*fp, _FIOAI, &fai);
827 				if (fai.fai_num == 0) {
828 					/*
829 					 * Check whether we crossed the end of
830 					 * the file or off_t wrapps.
831 					 */
832 					if (pos < lseek(*fp, (off_t)0, SEEK_END)) {
833 						off_t	di;
834 
835 						di = lseek(*fp, (off_t)0, SEEK_END);
836 						di -= pos;
837 						fai.fai_off = pos;
838 						fai.fai_size = (di/512) * 512;
839 						fai.fai_num = di/512;
840 						if (fai.fai_num != 0)
841 							goto retry;
842 						if (di > 0 && di < 512) {
843 							/*
844 							 * The last entry
845 							 * cannot be checked
846 							 * via the ooctl.
847 							 */
848 							fai.fai_size = 512;
849 							fai.fai_num = 1;
850 							fai.fai_daddr[0] = !_FIOAI_HOLE;
851 							goto fake;
852 						}
853 					}
854 					break;
855 				}
856 			fake:
857 				fai_idx = 0;
858 			}
859 			is_hole = fai.fai_daddr[fai_idx++] == _FIOAI_HOLE;
860 
861 			amount = 512;
862 			if (pos + amount < 0)
863 				amount = info->f_size - pos;
864 			else if (pos + amount > info->f_size)
865 				amount = info->f_size - pos;
866 #else
867 			/* EMPTY */
868 #endif	/* _FIOAI */
869 #endif	/* SEEK_HOLE */
870 		} else {
871 			if (cnt <= 0) {
872 				if ((cnt = _fileread(fp, rbuf, sizeof (rbuf))) == 0)
873 					break;
874 				if (cnt < 0) {
875 					errmsg(
876 					"Error scanning for holes in '%s'.\n",
877 					info->f_name);
878 
879 					errmsg("Current pos: %lld\n",
880 					(Llong)lseek(*fp, (off_t)0, SEEK_CUR));
881 
882 					free(sparse);
883 					return (0);
884 				}
885 				rbp = (char *)rbuf;
886 			}
887 			if ((amount = cmpnullbytes(rbp, cnt)) >= cnt) {
888 				is_hole = TRUE;
889 				cnt = 0;
890 			} else {
891 				amount = (amount / TBLOCK) * TBLOCK;
892 				if ((is_hole = amount != 0) == FALSE) {
893 					amount += TBLOCK;
894 					if (amount > cnt)
895 						amount = cnt;
896 				}
897 				rbp += amount;
898 				cnt -= amount;
899 			}
900 		}
901 
902 		if (is_hole) {
903 			if (data) {
904 				sparse[i].sp_numbytes =
905 						pos - sparse[i].sp_offset;
906 				info->f_rsize += sparse[i].sp_numbytes;
907 
908 				EDEBUG(("i: %d offset: %lld numbytes: %lld\n", i,
909 					(Llong)sparse[i].sp_offset,
910 					(Llong)sparse[i].sp_numbytes));
911 
912 				data = FALSE;
913 				i++;
914 				if (i >= nsparse) {
915 					if ((sparse = grow_sp_list(sparse,
916 							&nsparse)) == 0) {
917 						lseek(*fp, (off_t)0, SEEK_SET);
918 						return (0);
919 					}
920 				}
921 			}
922 		} else {
923 			if (!data) {
924 				sparse[i].sp_offset = pos;
925 				data = TRUE;
926 			}
927 		}
928 		pos += amount;
929 	}
930 
931 scan_done:
932 	EDEBUG(("data: %d\n", data));
933 
934 	if (data) {
935 		sparse[i].sp_numbytes = pos - sparse[i].sp_offset;
936 		info->f_rsize += sparse[i].sp_numbytes;
937 
938 		EDEBUG(("i: %d offset: %lld numbytes: %lld\n", i,
939 				(Llong)sparse[i].sp_offset,
940 				(Llong)sparse[i].sp_numbytes));
941 	} else {
942 #ifdef	NO_HOLE_AT_END
943 		sparse[i].sp_offset = pos -1;
944 		sparse[i].sp_numbytes = 1;
945 		info->f_rsize += 1;
946 #else
947 		sparse[i].sp_offset = pos;
948 		sparse[i].sp_numbytes = 0;
949 #endif
950 		EDEBUG(("i: %d offset: %lld numbytes: %lld\n", i,
951 				(Llong)sparse[i].sp_offset,
952 				(Llong)sparse[i].sp_numbytes));
953 	}
954 	lseek(*fp, (off_t)0, SEEK_SET);
955 	*spp = sparse;
956 	return (++i);
957 }
958 
959 LOCAL int
get_end_hole(fh)960 get_end_hole(fh)
961 	register fh_t	*fh;
962 {
963 #ifdef	HAVE_FTRUNCATE
964 	int	f;
965 	off_t	off;
966 	int	add = 0;
967 #endif
968 
969 	/*
970 	 * At end of sparse array: return TRUE.
971 	 * There is no "end hole" in this case.
972 	 */
973 	if (fh->fh_spindex == fh->fh_nsparse)
974 		return (TRUE);
975 	/*
976 	 * Out of sparse array: return FALSE.
977 	 */
978 	if (fh->fh_spindex > fh->fh_nsparse)
979 		return (0);
980 
981 	/*
982 	 * New offset is not > fh->fh_newpos: return.
983 	 */
984 	if (fh->fh_sparse[fh->fh_spindex].sp_offset <= fh->fh_newpos)
985 		return (0);
986 
987 	/*
988 	 * No hole at end of file: return.
989 	 */
990 	if (fh->fh_sparse[fh->fh_spindex].sp_numbytes != 0)
991 		return (0);
992 
993 #ifdef	HAVE_FTRUNCATE
994 	/*
995 	 * In order to prevent Solaris from allocating space at the end of the
996 	 * file we need to shrink the file. For this reason, we first create
997 	 * the file a bit too large.
998 	 */
999 	f = fdown(fh->fh_file);
1000 	off = fh->fh_sparse[fh->fh_spindex].sp_offset;
1001 
1002 #ifdef	_PC_MIN_HOLE_SIZE
1003 	add = fpathconf(f, _PC_MIN_HOLE_SIZE);
1004 #endif
1005 	if (add <= 0)
1006 		add = 8192;
1007 
1008 	if ((OFF_T_MAX - off) > add)
1009 		(void) ftruncate(f, off+add);
1010 
1011 	if (ftruncate(f, off) < 0)
1012 		return (write_end_hole(fh));
1013 	return (TRUE);
1014 #else
1015 	return (write_end_hole(fh));
1016 #endif
1017 }
1018 
1019 LOCAL int
write_end_hole(fh)1020 write_end_hole(fh)
1021 	register fh_t	*fh;
1022 {
1023 	int	f;
1024 	off_t	off;
1025 
1026 	f = fdown(fh->fh_file);
1027 	off = fh->fh_sparse[fh->fh_spindex].sp_offset;
1028 
1029 	seterrno(0);
1030 	if (lseek(f, off-1, SEEK_SET) == (off_t)-1) {
1031 		if (!errhidden(E_WRITE, fh->fh_name)) {
1032 			if (!errwarnonly(E_WRITE, fh->fh_name))
1033 				xstats.s_rwerrs++;
1034 			errmsg("Error seeking '%s'.\n", fh->fh_name);
1035 			(void) errabort(E_WRITE, fh->fh_name, TRUE);
1036 			return (FALSE);
1037 		}
1038 	} else if (ffilewrite(fh->fh_file, "", 1) != 1) {
1039 		if (!errhidden(E_WRITE, fh->fh_name)) {
1040 			if (!errwarnonly(E_WRITE, fh->fh_name))
1041 				xstats.s_rwerrs++;
1042 			errmsg("Error writing '%s'.\n", fh->fh_name);
1043 			(void) errabort(E_WRITE, fh->fh_name, TRUE);
1044 			return (FALSE);
1045 		}
1046 	}
1047 	return (TRUE);
1048 }
1049 
1050 EXPORT int
gnu_skip_extended(info)1051 gnu_skip_extended(info)
1052 	FINFO	*info;
1053 {
1054 	TCB	*ptb;
1055 
1056 	if (info->f_flags & F_SP_SKIPPED)
1057 		return (0);
1058 
1059 	ptb = info->f_tcb;
1060 	if (((info->f_flags & F_SP_EXTENDED) != 0) ||
1061 	    ptb->gnu_in_dbuf.t_isextended) do {
1062 		info->f_flags |= F_SP_EXTENDED;
1063 		if (readblock((char *)ptb, TBLOCK) == EOF)
1064 			return (EOF);
1065 	} while (ptb->gnu_ext_dbuf.t_isextended);
1066 
1067 	info->f_flags |= F_SP_SKIPPED;
1068 	return (0);
1069 }
1070 
1071 EXPORT int
get_sparse(f,info)1072 get_sparse(f, info)
1073 	FILE	*f;
1074 	FINFO	*info;
1075 {
1076 	fh_t	fh;
1077 	sp_t	*sparse;
1078 	int	nsparse;
1079 	int	ret;
1080 
1081 	sparse = get_sp_list(info, &nsparse);
1082 	if (sparse == 0) {
1083 		errmsgno(EX_BAD, "Skipping '%s' sorry ...\n", info->f_name);
1084 		errmsgno(EX_BAD, "WARNING: '%s' is damaged\n", info->f_name);
1085 		void_file(info);
1086 		return (FALSE);
1087 	}
1088 	fh.fh_file = f;
1089 	fh.fh_name = info->f_name;
1090 	fh.fh_size = info->f_rsize;
1091 	fh.fh_newpos = (off_t)0;
1092 	fh.fh_sparse = sparse;
1093 	fh.fh_nsparse = nsparse;
1094 	fh.fh_spindex = 0;
1095 	ret = xt_file(info, vp_get_sparse_func, &fh, 0, "writing");
1096 	if (ret == TRUE)
1097 		ret = get_end_hole(&fh);
1098 	free(sparse);
1099 	return (ret);
1100 }
1101 
1102 EXPORT int
get_as_hole(f,info)1103 get_as_hole(f, info)
1104 	FILE	*f;
1105 	FINFO	*info;
1106 {
1107 	fh_t	fh;
1108 	sp_t	sparse;
1109 
1110 	sparse.sp_offset = info->f_size;
1111 	sparse.sp_numbytes = 0;
1112 
1113 	fh.fh_file = f;
1114 	fh.fh_name = info->f_name;
1115 	fh.fh_size = info->f_rsize;
1116 	fh.fh_newpos = (off_t)0;
1117 	fh.fh_sparse = &sparse;
1118 	fh.fh_nsparse = 1;
1119 	fh.fh_spindex = 0;
1120 
1121 	return (get_end_hole(&fh));
1122 }
1123 
1124 EXPORT BOOL
cmp_sparse(f,info)1125 cmp_sparse(f, info)
1126 	FILE	*f;
1127 	FINFO	*info;
1128 {
1129 	fh_t	fh;
1130 	sp_t	*sparse;
1131 	int	nsparse;
1132 
1133 	sparse = get_sp_list(info, &nsparse);
1134 	if (sparse == 0) {
1135 		errmsgno(EX_BAD, "Skipping '%s' sorry ...\n", info->f_name);
1136 		void_file(info);
1137 		fclose(f);
1138 		return (FALSE);
1139 	}
1140 	fh.fh_file = f;
1141 	fh.fh_name = info->f_name;
1142 	fh.fh_size = info->f_rsize;
1143 	fh.fh_newpos = (off_t)0;
1144 	fh.fh_sparse = sparse;
1145 	fh.fh_nsparse = nsparse;
1146 	fh.fh_spindex = 0;
1147 	fh.fh_diffs = 0;
1148 	if (xt_file(info, vp_cmp_sparse_func, &fh, TBLOCK, "reading") < 0)
1149 		die(EX_BAD);
1150 	if (fclose(f) != 0) {
1151 		if (!errhidden(E_READ, info->f_name)) {
1152 			if (!errwarnonly(E_SHRINK, info->f_name))
1153 				xstats.s_rwerrs++;
1154 			(void) errabort(E_READ, info->f_name, TRUE);
1155 		}
1156 	}
1157 	free(sparse);
1158 	return (fh.fh_diffs == 0);
1159 }
1160 
1161 EXPORT void
put_sparse(fp,info)1162 put_sparse(fp, info)
1163 	int	*fp;
1164 	FINFO	*info;
1165 {
1166 	fh_t	fh;
1167 	sp_t	*sparse;
1168 	int	nsparse;
1169 	off_t	rsize;
1170 
1171 	nsparse = mk_sp_list(fp, info, &sparse);
1172 	if (debug)
1173 		error("numsparse: %d\n", nsparse);
1174 	if (nsparse == 0) {
1175 		info->f_rsize = info->f_size;
1176 		put_tcb(info->f_tcb, info);
1177 		vprint(info);
1178 		errmsgno(EX_BAD, "Dumping SPARSE '%s' as file\n", info->f_name);
1179 		put_file(fp, info);
1180 		return;
1181 	}
1182 	rsize = info->f_rsize;
1183 
1184 	/*
1185 	 * If -force-hole was specified and the file is not sparse, only write
1186 	 * it as sparse to the archive if the amount of all-zero data is at
1187 	 * least TBLOCK bytes.
1188 	 */
1189 	if (force_hole && ((info->f_flags & F_SPARSE) == 0)) {
1190 		if (info->f_size - rsize < TBLOCK) {
1191 			info->f_rsize = info->f_size;
1192 			put_tcb(info->f_tcb, info);
1193 			vprint(info);
1194 			/*
1195 			 * At this point, we found a file with no zeroed
1196 			 * region of at least TBLOCK size.
1197 			 */
1198 			put_file(fp, info);
1199 			free(sparse);
1200 			return;
1201 		} else if (!silent) {
1202 			error("Treating '%s' as sparse\n", info->f_name);
1203 		}
1204 	}
1205 
1206 	EDEBUG(("rsize: %lld\n", (Llong)rsize));
1207 
1208 	put_sp_list(info, sparse, nsparse);
1209 	fh.fh_file = (FILE *)fp;
1210 	fh.fh_name = info->f_name;
1211 	fh.fh_size = info->f_rsize = rsize;
1212 	fh.fh_newpos = (off_t)0;
1213 	fh.fh_sparse = sparse;
1214 	fh.fh_nsparse = nsparse;
1215 	fh.fh_spindex = 0;
1216 	cr_file(info, vp_put_sparse_func, &fh, 0, "reading");
1217 	free(sparse);
1218 }
1219 
1220 LOCAL void
put_sp_list(info,sparse,nsparse)1221 put_sp_list(info, sparse, nsparse)
1222 	FINFO	*info;
1223 	sp_t	*sparse;
1224 	int	nsparse;
1225 {
1226 	register int	i;
1227 	register int	sparse_in_hdr = props.pr_sparse_in_hdr;
1228 	register char	*p;
1229 		TCB	tb;
1230 		TCB	*ptb = info->f_tcb;
1231 	extern long hdrtype;	/* XXX */
1232 
1233 	EDEBUG(("1nsparse: %d rsize: %lld\n", nsparse, (Llong)info->f_rsize));
1234 
1235 	if (nsparse > sparse_in_hdr) {
1236 		if ((props.pr_flags & PR_GNU_SPARSE_BUG) == 0)
1237 			info->f_rsize +=
1238 				(((nsparse-sparse_in_hdr)+SPARSE_EXT_HDR-1)/
1239 				SPARSE_EXT_HDR)*TBLOCK;
1240 	}
1241 	EDEBUG(("2nsparse: %d rsize: %lld added: %d\n", nsparse, (Llong)info->f_rsize,
1242 				(((nsparse-sparse_in_hdr)+SPARSE_EXT_HDR-1)/
1243 				SPARSE_EXT_HDR)*TBLOCK));
1244 	EDEBUG(("addr sp: %zd\n", (size_t)&((TCB *)0)->xstar_in_dbuf.t_sp));
1245 	EDEBUG(("addr rs: %zd\n", (size_t)&((TCB *)0)->xstar_in_dbuf.t_realsize));
1246 	EDEBUG(("flags: 0x%lX\n", info->f_flags));
1247 
1248 	info->f_rxftype = info->f_xftype = XT_SPARSE;
1249 	if (info->f_flags & F_SPLIT_NAME && props.pr_nflags & PR_PREFIX_REUSED)
1250 		tcb_undo_split(ptb, info);
1251 
1252 	info->f_xflags &= ~XF_SIZE;	/* Condensed file is smaller	*/
1253 	info_to_tcb(info, ptb);		/* Re-Compute anything		*/
1254 
1255 	if (H_TYPE(hdrtype) == H_GNUTAR)
1256 		p = (char *)ptb->gnu_in_dbuf.t_sp;
1257 	else
1258 		p = (char *)ptb->xstar_in_dbuf.t_sp;
1259 	for (i = 0; i < sparse_in_hdr && i < nsparse; i++) {
1260 		llitos(p, (Ullong)sparse[i].sp_offset, 11);   p += 12;
1261 		llitos(p, (Ullong)sparse[i].sp_numbytes, 11); p += 12;
1262 	}
1263 	if (sparse_in_hdr > 0 && nsparse > sparse_in_hdr) {
1264 		if (H_TYPE(hdrtype) == H_GNUTAR)
1265 			ptb->gnu_in_dbuf.t_isextended = 1;
1266 		else
1267 			ptb->xstar_in_dbuf.t_isextended = '1';
1268 	}
1269 
1270 	put_tcb(ptb, info);
1271 	vprint(info);
1272 
1273 	nsparse -= sparse_in_hdr;
1274 	sparse += sparse_in_hdr;
1275 	ptb = &tb;
1276 	while (nsparse > 0) {
1277 		filltcb(ptb);
1278 		p = (char *)ptb;
1279 		for (i = 0; i < SPARSE_EXT_HDR && i < nsparse; i++) {
1280 			llitos(p, (Ullong)sparse[i].sp_offset, 11);   p += 12;
1281 			llitos(p, (Ullong)sparse[i].sp_numbytes, 11); p += 12;
1282 		}
1283 		nsparse -= SPARSE_EXT_HDR;
1284 		sparse += SPARSE_EXT_HDR;
1285 		if (nsparse > 0)
1286 			ptb->gnu_ext_dbuf.t_isextended = '1';
1287 		writeblock((char *)ptb);
1288 	}
1289 }
1290 
1291 EXPORT BOOL
sparse_file(fp,info)1292 sparse_file(fp, info)
1293 	int	*fp;
1294 	FINFO	*info;
1295 {
1296 #if	defined(SEEK_HOLE) && defined(SEEK_DATA)
1297 	off_t	pos;
1298 	int	f = *fp;
1299 
1300 	/*
1301 	 * If we have been compiled on an OS that supports SEEK_HOLE but run
1302 	 * on an OS that does not support SEEK_HOLE, we get EINVAL.
1303 	 * If the underlying filesystem does not support the SEEK_HOLE call,
1304 	 * we get ENOTSUP. In all other cases, we will get either the position
1305 	 * of the first real hole in the file or statb.st_size in case the file
1306 	 * definitely has no holes.
1307 	 */
1308 	pos = lseek(f, (off_t)0, SEEK_HOLE);	/* Check for first hole	   */
1309 	if (pos == (off_t)-1)			/* SEEK_HOLE not supported */
1310 		return ((info->f_flags & F_SPARSE) != 0);
1311 
1312 	if (pos != 0)				/* Not at pos 0: seek back */
1313 		(void) lseek(f, 0, SEEK_SET);
1314 
1315 
1316 	if (pos >= info->f_size) {		/* Definitely not sparse */
1317 #ifdef	_PC_MIN_HOLE_SIZE
1318 		/*
1319 		 * If we are on an unfixed Solaris kernel and if the
1320 		 * underlying filesystem does not support SEEK_HOLE,
1321 		 * the file may be sparse, but SEEK_HOLE returns info->f_size.
1322 		 * Fortunately, the fpathconf call only takes 1usec on a 550MHz
1323 		 * Pentium III, so this call typically takes less than 1% of
1324 		 * the total system time.
1325 		 */
1326 		if (fpathconf(f, _PC_MIN_HOLE_SIZE) < 0)
1327 			return ((info->f_flags & F_SPARSE) != 0);
1328 #endif
1329 		return (FALSE);
1330 	}
1331 	return (TRUE);				/* Definitely sparse */
1332 #else
1333 	return ((info->f_flags & F_SPARSE) != 0);
1334 #endif
1335 }
1336