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