1*8df76133Sjca /* $OpenBSD: tar.c,v 1.85 2024/04/17 18:12:12 jca Exp $ */
2df930be7Sderaadt /* $NetBSD: tar.c,v 1.5 1995/03/21 09:07:49 cgd Exp $ */
3df930be7Sderaadt
4df930be7Sderaadt /*-
5df930be7Sderaadt * Copyright (c) 1992 Keith Muller.
6df930be7Sderaadt * Copyright (c) 1992, 1993
7df930be7Sderaadt * The Regents of the University of California. All rights reserved.
8df930be7Sderaadt *
9df930be7Sderaadt * This code is derived from software contributed to Berkeley by
10df930be7Sderaadt * Keith Muller of the University of California, San Diego.
11df930be7Sderaadt *
12df930be7Sderaadt * Redistribution and use in source and binary forms, with or without
13df930be7Sderaadt * modification, are permitted provided that the following conditions
14df930be7Sderaadt * are met:
15df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright
16df930be7Sderaadt * notice, this list of conditions and the following disclaimer.
17df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright
18df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the
19df930be7Sderaadt * documentation and/or other materials provided with the distribution.
2029295d1cSmillert * 3. Neither the name of the University nor the names of its contributors
21df930be7Sderaadt * may be used to endorse or promote products derived from this software
22df930be7Sderaadt * without specific prior written permission.
23df930be7Sderaadt *
24df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34df930be7Sderaadt * SUCH DAMAGE.
35df930be7Sderaadt */
36df930be7Sderaadt
37df930be7Sderaadt #include <sys/types.h>
38013e174aSjca #include <sys/queue.h>
39df930be7Sderaadt #include <sys/stat.h>
40734dd9a4Sfgsch #include <ctype.h>
41734dd9a4Sfgsch #include <errno.h>
424c043852Sguenther #include <grp.h>
43013e174aSjca #include <libgen.h>
44734dd9a4Sfgsch #include <limits.h>
454c043852Sguenther #include <pwd.h>
46df930be7Sderaadt #include <stdio.h>
47df930be7Sderaadt #include <stdlib.h>
484c043852Sguenther #include <string.h>
494c043852Sguenther #include <unistd.h>
504c043852Sguenther
51df930be7Sderaadt #include "pax.h"
52df930be7Sderaadt #include "extern.h"
53df930be7Sderaadt #include "tar.h"
54df930be7Sderaadt
55013e174aSjca SLIST_HEAD(xheader, xheader_record);
56013e174aSjca struct xheader_record {
57013e174aSjca SLIST_ENTRY(xheader_record) entry;
58013e174aSjca size_t reclen;
59013e174aSjca char *record;
60013e174aSjca };
61013e174aSjca
62013e174aSjca /* shortest possible extended record: "5 a=\n" */
63013e174aSjca #define MINXHDRSZ 5
64013e174aSjca
65df930be7Sderaadt /*
66df930be7Sderaadt * Routines for reading, writing and header identify of various versions of tar
67df930be7Sderaadt */
68df930be7Sderaadt
69386e027cSotto static size_t expandname(char *, size_t, char **, const char *, size_t);
70be87792eSmillert static u_long tar_chksm(char *, int);
71be87792eSmillert static char *name_split(char *, int);
72be87792eSmillert static int ul_oct(u_long, char *, int, int);
73f37d86feSguenther static int ull_oct(unsigned long long, char *, int, int);
742dbd6dc5Sguenther static int rd_xheader(ARCHD *arcn, int, off_t);
75013e174aSjca #ifndef SMALL
76013e174aSjca static int wr_xheader(ARCHD *, struct xheader *);
77013e174aSjca #endif
78df930be7Sderaadt
79b45d27d7Sotto static uid_t uid_nobody;
80b45d27d7Sotto static uid_t uid_warn;
81b45d27d7Sotto static gid_t gid_nobody;
82b45d27d7Sotto static gid_t gid_warn;
83b45d27d7Sotto
84df930be7Sderaadt /*
85df930be7Sderaadt * Routines common to all versions of tar
86df930be7Sderaadt */
87df930be7Sderaadt
882d53aafbSguenther int tar_nodir; /* do not write dirs under old tar */
89a79a0570Smillert char *gnu_name_string; /* GNU ././@LongLink hackery name */
90a79a0570Smillert char *gnu_link_string; /* GNU ././@LongLink hackery link */
91df930be7Sderaadt
92df930be7Sderaadt /*
93df930be7Sderaadt * tar_endwr()
94df930be7Sderaadt * add the tar trailer of two null blocks
95df930be7Sderaadt * Return:
96df930be7Sderaadt * 0 if ok, -1 otherwise (what wr_skip returns)
97df930be7Sderaadt */
98df930be7Sderaadt
99df930be7Sderaadt int
tar_endwr(void)100df930be7Sderaadt tar_endwr(void)
101df930be7Sderaadt {
1029a58b8c6Sguenther return wr_skip(NULLCNT * BLKMULT);
103df930be7Sderaadt }
104df930be7Sderaadt
105df930be7Sderaadt /*
106df930be7Sderaadt * tar_endrd()
107df930be7Sderaadt * no cleanup needed here, just return size of trailer (for append)
108df930be7Sderaadt * Return:
109df930be7Sderaadt * size of trailer (2 * BLKMULT)
110df930be7Sderaadt */
111df930be7Sderaadt
112df930be7Sderaadt off_t
tar_endrd(void)113df930be7Sderaadt tar_endrd(void)
114df930be7Sderaadt {
1159a58b8c6Sguenther return NULLCNT * BLKMULT;
116df930be7Sderaadt }
117df930be7Sderaadt
118df930be7Sderaadt /*
119df930be7Sderaadt * tar_trail()
120df930be7Sderaadt * Called to determine if a header block is a valid trailer. We are passed
121df930be7Sderaadt * the block, the in_sync flag (which tells us we are in resync mode;
122df930be7Sderaadt * looking for a valid header), and cnt (which starts at zero) which is
123df930be7Sderaadt * used to count the number of empty blocks we have seen so far.
124df930be7Sderaadt * Return:
125df930be7Sderaadt * 0 if a valid trailer, -1 if not a valid trailer, or 1 if the block
126df930be7Sderaadt * could never contain a header.
127df930be7Sderaadt */
128df930be7Sderaadt
129df930be7Sderaadt int
tar_trail(ARCHD * ignore,char * buf,int in_resync,int * cnt)130cd90c754Sderaadt tar_trail(ARCHD *ignore, char *buf, int in_resync, int *cnt)
131df930be7Sderaadt {
132be87792eSmillert int i;
133df930be7Sderaadt
134df930be7Sderaadt /*
135df930be7Sderaadt * look for all zero, trailer is two consecutive blocks of zero
136df930be7Sderaadt */
137df930be7Sderaadt for (i = 0; i < BLKMULT; ++i) {
138df930be7Sderaadt if (buf[i] != '\0')
139df930be7Sderaadt break;
140df930be7Sderaadt }
141df930be7Sderaadt
142df930be7Sderaadt /*
143df930be7Sderaadt * if not all zero it is not a trailer, but MIGHT be a header.
144df930be7Sderaadt */
145df930be7Sderaadt if (i != BLKMULT)
146df930be7Sderaadt return(-1);
147df930be7Sderaadt
148df930be7Sderaadt /*
149df930be7Sderaadt * When given a zero block, we must be careful!
150df930be7Sderaadt * If we are not in resync mode, check for the trailer. Have to watch
151df930be7Sderaadt * out that we do not mis-identify file data as the trailer, so we do
152df930be7Sderaadt * NOT try to id a trailer during resync mode. During resync mode we
153df930be7Sderaadt * might as well throw this block out since a valid header can NEVER be
154df930be7Sderaadt * a block of all 0 (we must have a valid file name).
155df930be7Sderaadt */
156d22c2939Smillert if (!in_resync && (++*cnt >= NULLCNT))
157df930be7Sderaadt return(0);
158df930be7Sderaadt return(1);
159df930be7Sderaadt }
160df930be7Sderaadt
161df930be7Sderaadt /*
162df930be7Sderaadt * ul_oct()
163df930be7Sderaadt * convert an unsigned long to an octal string. many oddball field
164df930be7Sderaadt * termination characters are used by the various versions of tar in the
1652407619dStholo * different fields. term selects which kind to use. str is '0' padded
166df930be7Sderaadt * at the front to len. we are unable to use only one format as many old
167df930be7Sderaadt * tar readers are very cranky about this.
168df930be7Sderaadt * Return:
169df930be7Sderaadt * 0 if the number fit into the string, -1 otherwise
170df930be7Sderaadt */
171df930be7Sderaadt
172df930be7Sderaadt static int
ul_oct(u_long val,char * str,int len,int term)173be87792eSmillert ul_oct(u_long val, char *str, int len, int term)
174df930be7Sderaadt {
175be87792eSmillert char *pt;
176df930be7Sderaadt
177df930be7Sderaadt /*
178df930be7Sderaadt * term selects the appropriate character(s) for the end of the string
179df930be7Sderaadt */
180df930be7Sderaadt pt = str + len - 1;
181df930be7Sderaadt switch (term) {
182df930be7Sderaadt case 3:
183df930be7Sderaadt *pt-- = '\0';
184df930be7Sderaadt break;
185df930be7Sderaadt case 2:
186df930be7Sderaadt *pt-- = ' ';
187df930be7Sderaadt *pt-- = '\0';
188df930be7Sderaadt break;
189df930be7Sderaadt case 1:
190df930be7Sderaadt *pt-- = ' ';
191df930be7Sderaadt break;
192df930be7Sderaadt case 0:
193df930be7Sderaadt default:
194df930be7Sderaadt *pt-- = '\0';
195df930be7Sderaadt *pt-- = ' ';
196df930be7Sderaadt break;
197df930be7Sderaadt }
198df930be7Sderaadt
199df930be7Sderaadt /*
200df930be7Sderaadt * convert and blank pad if there is space
201df930be7Sderaadt */
202df930be7Sderaadt while (pt >= str) {
203df930be7Sderaadt *pt-- = '0' + (char)(val & 0x7);
204f37d86feSguenther val >>= 3;
205f37d86feSguenther if (val == 0)
206df930be7Sderaadt break;
207df930be7Sderaadt }
208df930be7Sderaadt
209df930be7Sderaadt while (pt >= str)
2102407619dStholo *pt-- = '0';
211f37d86feSguenther if (val != 0)
212df930be7Sderaadt return(-1);
213df930be7Sderaadt return(0);
214df930be7Sderaadt }
215df930be7Sderaadt
216df930be7Sderaadt /*
217f37d86feSguenther * ull_oct()
218f37d86feSguenther * Convert an unsigned long long to an octal string. One of many oddball
219f37d86feSguenther * field termination characters are used by the various versions of tar
220f37d86feSguenther * in the different fields. term selects which kind to use. str is
221f37d86feSguenther * '0' padded at the front to len. We are unable to use only one format
222f37d86feSguenther * as many old tar readers are very cranky about this.
223df930be7Sderaadt * Return:
224df930be7Sderaadt * 0 if the number fit into the string, -1 otherwise
225df930be7Sderaadt */
226df930be7Sderaadt
227df930be7Sderaadt static int
ull_oct(unsigned long long val,char * str,int len,int term)228f37d86feSguenther ull_oct(unsigned long long val, char *str, int len, int term)
229df930be7Sderaadt {
230be87792eSmillert char *pt;
231df930be7Sderaadt
232df930be7Sderaadt /*
233df930be7Sderaadt * term selects the appropriate character(s) for the end of the string
234df930be7Sderaadt */
235df930be7Sderaadt pt = str + len - 1;
236df930be7Sderaadt switch (term) {
237df930be7Sderaadt case 3:
238df930be7Sderaadt *pt-- = '\0';
239df930be7Sderaadt break;
240df930be7Sderaadt case 2:
241df930be7Sderaadt *pt-- = ' ';
242df930be7Sderaadt *pt-- = '\0';
243df930be7Sderaadt break;
244df930be7Sderaadt case 1:
245df930be7Sderaadt *pt-- = ' ';
246df930be7Sderaadt break;
247df930be7Sderaadt case 0:
248df930be7Sderaadt default:
249df930be7Sderaadt *pt-- = '\0';
250df930be7Sderaadt *pt-- = ' ';
251df930be7Sderaadt break;
252df930be7Sderaadt }
253df930be7Sderaadt
254df930be7Sderaadt /*
255df930be7Sderaadt * convert and blank pad if there is space
256df930be7Sderaadt */
257df930be7Sderaadt while (pt >= str) {
258df930be7Sderaadt *pt-- = '0' + (char)(val & 0x7);
259f37d86feSguenther val >>= 3;
260f37d86feSguenther if (val == 0)
261df930be7Sderaadt break;
262df930be7Sderaadt }
263df930be7Sderaadt
264df930be7Sderaadt while (pt >= str)
2652407619dStholo *pt-- = '0';
266f37d86feSguenther if (val != 0)
267df930be7Sderaadt return(-1);
268df930be7Sderaadt return(0);
269df930be7Sderaadt }
270df930be7Sderaadt
271df930be7Sderaadt /*
272df930be7Sderaadt * tar_chksm()
273df930be7Sderaadt * calculate the checksum for a tar block counting the checksum field as
2744eb0b000Smillert * all blanks (BLNKSUM is that value pre-calculated, the sum of 8 blanks).
275df930be7Sderaadt * NOTE: we use len to short circuit summing 0's on write since we ALWAYS
276df930be7Sderaadt * pad headers with 0.
277df930be7Sderaadt * Return:
278df930be7Sderaadt * unsigned long checksum
279df930be7Sderaadt */
280df930be7Sderaadt
281df930be7Sderaadt static u_long
tar_chksm(char * blk,int len)282be87792eSmillert tar_chksm(char *blk, int len)
283df930be7Sderaadt {
284be87792eSmillert char *stop;
285be87792eSmillert char *pt;
2863ee1107dStodd u_long chksm = BLNKSUM; /* initial value is checksum field sum */
287df930be7Sderaadt
288df930be7Sderaadt /*
289df930be7Sderaadt * add the part of the block before the checksum field
290df930be7Sderaadt */
291df930be7Sderaadt pt = blk;
292df930be7Sderaadt stop = blk + CHK_OFFSET;
293df930be7Sderaadt while (pt < stop)
294df930be7Sderaadt chksm += (u_long)(*pt++ & 0xff);
295df930be7Sderaadt /*
296df930be7Sderaadt * move past the checksum field and keep going, spec counts the
297df930be7Sderaadt * checksum field as the sum of 8 blanks (which is pre-computed as
298df930be7Sderaadt * BLNKSUM).
299df930be7Sderaadt * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding
300df930be7Sderaadt * starts, no point in summing zero's)
301df930be7Sderaadt */
302df930be7Sderaadt pt += CHK_LEN;
303df930be7Sderaadt stop = blk + len;
304df930be7Sderaadt while (pt < stop)
305df930be7Sderaadt chksm += (u_long)(*pt++ & 0xff);
306df930be7Sderaadt return(chksm);
307df930be7Sderaadt }
308df930be7Sderaadt
309df930be7Sderaadt /*
310df930be7Sderaadt * Routines for old BSD style tar (also made portable to sysV tar)
311df930be7Sderaadt */
312df930be7Sderaadt
313df930be7Sderaadt /*
314df930be7Sderaadt * tar_id()
315df930be7Sderaadt * determine if a block given to us is a valid tar header (and not a USTAR
316df930be7Sderaadt * header). We have to be on the lookout for those pesky blocks of all
317df930be7Sderaadt * zero's.
318df930be7Sderaadt * Return:
319df930be7Sderaadt * 0 if a tar header, -1 otherwise
320df930be7Sderaadt */
321df930be7Sderaadt
322df930be7Sderaadt int
tar_id(char * blk,int size)323be87792eSmillert tar_id(char *blk, int size)
324df930be7Sderaadt {
325be87792eSmillert HD_TAR *hd;
326be87792eSmillert HD_USTAR *uhd;
327df930be7Sderaadt
328df930be7Sderaadt if (size < BLKMULT)
329df930be7Sderaadt return(-1);
330df930be7Sderaadt hd = (HD_TAR *)blk;
331df930be7Sderaadt uhd = (HD_USTAR *)blk;
332df930be7Sderaadt
333df930be7Sderaadt /*
334df930be7Sderaadt * check for block of zero's first, a simple and fast test, then make
335df930be7Sderaadt * sure this is not a ustar header by looking for the ustar magic
336df930be7Sderaadt * cookie. We should use TMAGLEN, but some USTAR archive programs are
337df930be7Sderaadt * wrong and create archives missing the \0. Last we check the
338df930be7Sderaadt * checksum. If this is ok we have to assume it is a valid header.
339df930be7Sderaadt */
340df930be7Sderaadt if (hd->name[0] == '\0')
341df930be7Sderaadt return(-1);
342df930be7Sderaadt if (strncmp(uhd->magic, TMAGIC, TMAGLEN - 1) == 0)
343df930be7Sderaadt return(-1);
344df930be7Sderaadt if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT))
345df930be7Sderaadt return(-1);
346da60b202Smillert force_one_volume = 1;
347df930be7Sderaadt return(0);
348df930be7Sderaadt }
349df930be7Sderaadt
350df930be7Sderaadt /*
351df930be7Sderaadt * tar_opt()
352df930be7Sderaadt * handle tar format specific -o options
353df930be7Sderaadt * Return:
354df930be7Sderaadt * 0 if ok -1 otherwise
355df930be7Sderaadt */
356df930be7Sderaadt
357df930be7Sderaadt int
tar_opt(void)358df930be7Sderaadt tar_opt(void)
359df930be7Sderaadt {
360df930be7Sderaadt OPLIST *opt;
361df930be7Sderaadt
362df930be7Sderaadt while ((opt = opt_next()) != NULL) {
363df930be7Sderaadt if (strcmp(opt->name, TAR_OPTION) ||
364df930be7Sderaadt strcmp(opt->value, TAR_NODIR)) {
36542cf9836Stholo paxwarn(1, "Unknown tar format -o option/value pair %s=%s",
366df930be7Sderaadt opt->name, opt->value);
36742cf9836Stholo paxwarn(1,"%s=%s is the only supported tar format option",
368df930be7Sderaadt TAR_OPTION, TAR_NODIR);
369df930be7Sderaadt return(-1);
370df930be7Sderaadt }
371df930be7Sderaadt
372df930be7Sderaadt /*
373df930be7Sderaadt * we only support one option, and only when writing
374df930be7Sderaadt */
375df930be7Sderaadt if ((act != APPND) && (act != ARCHIVE)) {
37642cf9836Stholo paxwarn(1, "%s=%s is only supported when writing.",
377df930be7Sderaadt opt->name, opt->value);
378df930be7Sderaadt return(-1);
379df930be7Sderaadt }
380df930be7Sderaadt tar_nodir = 1;
381df930be7Sderaadt }
382df930be7Sderaadt return(0);
383df930be7Sderaadt }
384df930be7Sderaadt
385df930be7Sderaadt
386df930be7Sderaadt /*
387df930be7Sderaadt * tar_rd()
388df930be7Sderaadt * extract the values out of block already determined to be a tar header.
389df930be7Sderaadt * store the values in the ARCHD parameter.
390df930be7Sderaadt * Return:
391df930be7Sderaadt * 0
392df930be7Sderaadt */
393df930be7Sderaadt
394df930be7Sderaadt int
tar_rd(ARCHD * arcn,char * buf)395be87792eSmillert tar_rd(ARCHD *arcn, char *buf)
396df930be7Sderaadt {
397be87792eSmillert HD_TAR *hd;
398f37d86feSguenther unsigned long long val;
399be87792eSmillert char *pt;
400df930be7Sderaadt
401df930be7Sderaadt /*
402df930be7Sderaadt * we only get proper sized buffers passed to us
403df930be7Sderaadt */
404df930be7Sderaadt if (tar_id(buf, BLKMULT) < 0)
405df930be7Sderaadt return(-1);
406a79a0570Smillert memset(arcn, 0, sizeof(*arcn));
407df930be7Sderaadt arcn->org_name = arcn->name;
408df930be7Sderaadt arcn->sb.st_nlink = 1;
409df930be7Sderaadt
410df930be7Sderaadt /*
411df930be7Sderaadt * copy out the name and values in the stat buffer
412df930be7Sderaadt */
413df930be7Sderaadt hd = (HD_TAR *)buf;
414a79a0570Smillert if (hd->linkflag != LONGLINKTYPE && hd->linkflag != LONGNAMETYPE) {
415a79a0570Smillert arcn->nlen = expandname(arcn->name, sizeof(arcn->name),
416386e027cSotto &gnu_name_string, hd->name, sizeof(hd->name));
417a79a0570Smillert arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name),
418386e027cSotto &gnu_link_string, hd->linkname, sizeof(hd->linkname));
419da60b202Smillert }
420df930be7Sderaadt arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode,sizeof(hd->mode),OCT) &
421df930be7Sderaadt 0xfff);
422df930be7Sderaadt arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT);
423df930be7Sderaadt arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT);
424f37d86feSguenther arcn->sb.st_size = (off_t)asc_ull(hd->size, sizeof(hd->size), OCT);
425f37d86feSguenther val = asc_ull(hd->mtime, sizeof(hd->mtime), OCT);
42676c0e1cfSotto if (val > MAX_TIME_T)
4272f186deaSmillert arcn->sb.st_mtime = MAX_TIME_T;
428bdaaddddSguenther else
429bdaaddddSguenther arcn->sb.st_mtime = val;
430e425abdcSguenther arcn->sb.st_ctim = arcn->sb.st_atim = arcn->sb.st_mtim;
431df930be7Sderaadt
432df930be7Sderaadt /*
433df930be7Sderaadt * have to look at the last character, it may be a '/' and that is used
434df930be7Sderaadt * to encode this as a directory
435df930be7Sderaadt */
436df930be7Sderaadt pt = &(arcn->name[arcn->nlen - 1]);
437df930be7Sderaadt arcn->pad = 0;
438df930be7Sderaadt arcn->skip = 0;
439df930be7Sderaadt switch (hd->linkflag) {
440df930be7Sderaadt case SYMTYPE:
441df930be7Sderaadt /*
442df930be7Sderaadt * symbolic link, need to get the link name and set the type in
443df930be7Sderaadt * the st_mode so -v printing will look correct.
444df930be7Sderaadt */
445df930be7Sderaadt arcn->type = PAX_SLK;
446df930be7Sderaadt arcn->sb.st_mode |= S_IFLNK;
447df930be7Sderaadt break;
448df930be7Sderaadt case LNKTYPE:
449df930be7Sderaadt /*
450df930be7Sderaadt * hard link, need to get the link name, set the type in the
451df930be7Sderaadt * st_mode and st_nlink so -v printing will look better.
452df930be7Sderaadt */
453df930be7Sderaadt arcn->type = PAX_HLK;
454df930be7Sderaadt arcn->sb.st_nlink = 2;
455df930be7Sderaadt
456df930be7Sderaadt /*
457df930be7Sderaadt * no idea of what type this thing really points at, but
458df930be7Sderaadt * we set something for printing only.
459df930be7Sderaadt */
460df930be7Sderaadt arcn->sb.st_mode |= S_IFREG;
461df930be7Sderaadt break;
462da60b202Smillert case LONGLINKTYPE:
463da60b202Smillert case LONGNAMETYPE:
464da60b202Smillert /*
465da60b202Smillert * GNU long link/file; we tag these here and let the
466da60b202Smillert * pax internals deal with it -- too ugly otherwise.
467da60b202Smillert */
468a79a0570Smillert arcn->type =
469a79a0570Smillert hd->linkflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF;
470da60b202Smillert arcn->pad = TAR_PAD(arcn->sb.st_size);
471da60b202Smillert arcn->skip = arcn->sb.st_size;
472da60b202Smillert break;
4739a590279Smillert case DIRTYPE:
4749a590279Smillert /*
4759a590279Smillert * It is a directory, set the mode for -v printing
4769a590279Smillert */
4779a590279Smillert arcn->type = PAX_DIR;
4789a590279Smillert arcn->sb.st_mode |= S_IFDIR;
4799a590279Smillert arcn->sb.st_nlink = 2;
4809a590279Smillert break;
481df930be7Sderaadt case AREGTYPE:
482df930be7Sderaadt case REGTYPE:
483df930be7Sderaadt default:
484df930be7Sderaadt /*
485df930be7Sderaadt * If we have a trailing / this is a directory and NOT a file.
486df930be7Sderaadt */
487df930be7Sderaadt arcn->ln_name[0] = '\0';
488df930be7Sderaadt arcn->ln_nlen = 0;
489df930be7Sderaadt if (*pt == '/') {
490df930be7Sderaadt /*
491df930be7Sderaadt * it is a directory, set the mode for -v printing
492df930be7Sderaadt */
493df930be7Sderaadt arcn->type = PAX_DIR;
494df930be7Sderaadt arcn->sb.st_mode |= S_IFDIR;
495df930be7Sderaadt arcn->sb.st_nlink = 2;
496df930be7Sderaadt } else {
497df930be7Sderaadt /*
498df930be7Sderaadt * have a file that will be followed by data. Set the
4994eb0b000Smillert * skip value to the size field and calculate the size
500df930be7Sderaadt * of the padding.
501df930be7Sderaadt */
502df930be7Sderaadt arcn->type = PAX_REG;
503df930be7Sderaadt arcn->sb.st_mode |= S_IFREG;
504df930be7Sderaadt arcn->pad = TAR_PAD(arcn->sb.st_size);
505df930be7Sderaadt arcn->skip = arcn->sb.st_size;
506df930be7Sderaadt }
507df930be7Sderaadt break;
508df930be7Sderaadt }
509df930be7Sderaadt
510df930be7Sderaadt /*
511df930be7Sderaadt * strip off any trailing slash.
512df930be7Sderaadt */
513df930be7Sderaadt if (*pt == '/') {
514df930be7Sderaadt *pt = '\0';
515df930be7Sderaadt --arcn->nlen;
516df930be7Sderaadt }
517df930be7Sderaadt return(0);
518df930be7Sderaadt }
519df930be7Sderaadt
520df930be7Sderaadt /*
521df930be7Sderaadt * tar_wr()
522df930be7Sderaadt * write a tar header for the file specified in the ARCHD to the archive.
523df930be7Sderaadt * Have to check for file types that cannot be stored and file names that
524df930be7Sderaadt * are too long. Be careful of the term (last arg) to ul_oct, each field
525df930be7Sderaadt * of tar has it own spec for the termination character(s).
526df930be7Sderaadt * ASSUMED: space after header in header block is zero filled
527df930be7Sderaadt * Return:
528df930be7Sderaadt * 0 if file has data to be written after the header, 1 if file has NO
529df930be7Sderaadt * data to write after the header, -1 if archive write failed
530df930be7Sderaadt */
531df930be7Sderaadt
532df930be7Sderaadt int
tar_wr(ARCHD * arcn)533be87792eSmillert tar_wr(ARCHD *arcn)
534df930be7Sderaadt {
535be87792eSmillert HD_TAR *hd;
536df930be7Sderaadt int len;
537df930be7Sderaadt char hdblk[sizeof(HD_TAR)];
538df930be7Sderaadt
539df930be7Sderaadt /*
540df930be7Sderaadt * check for those file system types which tar cannot store
541df930be7Sderaadt */
542df930be7Sderaadt switch (arcn->type) {
543df930be7Sderaadt case PAX_DIR:
544df930be7Sderaadt /*
545df930be7Sderaadt * user asked that dirs not be written to the archive
546df930be7Sderaadt */
547df930be7Sderaadt if (tar_nodir)
548df930be7Sderaadt return(1);
549df930be7Sderaadt break;
550df930be7Sderaadt case PAX_CHR:
55142cf9836Stholo paxwarn(1, "Tar cannot archive a character device %s",
552df930be7Sderaadt arcn->org_name);
553df930be7Sderaadt return(1);
554df930be7Sderaadt case PAX_BLK:
55542cf9836Stholo paxwarn(1, "Tar cannot archive a block device %s", arcn->org_name);
556df930be7Sderaadt return(1);
557df930be7Sderaadt case PAX_SCK:
55842cf9836Stholo paxwarn(1, "Tar cannot archive a socket %s", arcn->org_name);
559df930be7Sderaadt return(1);
560df930be7Sderaadt case PAX_FIF:
56142cf9836Stholo paxwarn(1, "Tar cannot archive a fifo %s", arcn->org_name);
562df930be7Sderaadt return(1);
563df930be7Sderaadt case PAX_SLK:
564df930be7Sderaadt case PAX_HLK:
565df930be7Sderaadt case PAX_HRG:
566a38f4902Sotto if ((size_t)arcn->ln_nlen > sizeof(hd->linkname)) {
5679cfd895cSotto paxwarn(1, "Link name too long for tar %s",
5689cfd895cSotto arcn->ln_name);
569df930be7Sderaadt return(1);
570df930be7Sderaadt }
571df930be7Sderaadt break;
572df930be7Sderaadt case PAX_REG:
573df930be7Sderaadt case PAX_CTG:
574df930be7Sderaadt default:
575df930be7Sderaadt break;
576df930be7Sderaadt }
577df930be7Sderaadt
578df930be7Sderaadt /*
579df930be7Sderaadt * check file name len, remember extra char for dirs (the / at the end)
580df930be7Sderaadt */
581df930be7Sderaadt len = arcn->nlen;
582df930be7Sderaadt if (arcn->type == PAX_DIR)
583df930be7Sderaadt ++len;
584a38f4902Sotto if ((size_t)len > sizeof(hd->name)) {
58542cf9836Stholo paxwarn(1, "File name too long for tar %s", arcn->name);
586df930be7Sderaadt return(1);
587df930be7Sderaadt }
588df930be7Sderaadt
589df930be7Sderaadt /*
5908ec595f1Smillert * Copy the data out of the ARCHD into the tar header based on the type
5918ec595f1Smillert * of the file. Remember, many tar readers want all fields to be
5927a25f9afSmillert * padded with zero so we zero the header first. We then set the
5937a25f9afSmillert * linkflag field (type), the linkname, the size, and set the padding
5947a25f9afSmillert * (if any) to be added after the file data (0 for all other types,
5957a25f9afSmillert * as they only have a header).
596df930be7Sderaadt */
5977a25f9afSmillert memset(hdblk, 0, sizeof(hdblk));
598df930be7Sderaadt hd = (HD_TAR *)hdblk;
5999cfd895cSotto fieldcpy(hd->name, sizeof(hd->name), arcn->name, sizeof(arcn->name));
600df930be7Sderaadt arcn->pad = 0;
601df930be7Sderaadt
602df930be7Sderaadt if (arcn->type == PAX_DIR) {
603df930be7Sderaadt /*
604df930be7Sderaadt * directories are the same as files, except have a filename
6057a25f9afSmillert * that ends with a /, we add the slash here. No data follows
606df930be7Sderaadt * dirs, so no pad.
607df930be7Sderaadt */
608df930be7Sderaadt hd->linkflag = AREGTYPE;
609df930be7Sderaadt hd->name[len-1] = '/';
6109a58b8c6Sguenther if (ul_oct(0, hd->size, sizeof(hd->size), 1))
611df930be7Sderaadt goto out;
612df930be7Sderaadt } else if (arcn->type == PAX_SLK) {
613df930be7Sderaadt /*
614df930be7Sderaadt * no data follows this file, so no pad
615df930be7Sderaadt */
616df930be7Sderaadt hd->linkflag = SYMTYPE;
6179cfd895cSotto fieldcpy(hd->linkname, sizeof(hd->linkname), arcn->ln_name,
6189cfd895cSotto sizeof(arcn->ln_name));
6199a58b8c6Sguenther if (ul_oct(0, hd->size, sizeof(hd->size), 1))
620df930be7Sderaadt goto out;
6215316f7a4Sguenther } else if (PAX_IS_HARDLINK(arcn->type)) {
622df930be7Sderaadt /*
623df930be7Sderaadt * no data follows this file, so no pad
624df930be7Sderaadt */
625df930be7Sderaadt hd->linkflag = LNKTYPE;
6269cfd895cSotto fieldcpy(hd->linkname, sizeof(hd->linkname), arcn->ln_name,
6279cfd895cSotto sizeof(arcn->ln_name));
6289a58b8c6Sguenther if (ul_oct(0, hd->size, sizeof(hd->size), 1))
629df930be7Sderaadt goto out;
630df930be7Sderaadt } else {
631df930be7Sderaadt /*
632df930be7Sderaadt * data follows this file, so set the pad
633df930be7Sderaadt */
634df930be7Sderaadt hd->linkflag = AREGTYPE;
635f37d86feSguenther if (ull_oct(arcn->sb.st_size, hd->size, sizeof(hd->size), 1)) {
636f37d86feSguenther paxwarn(1, "File is too large for tar %s",
637f37d86feSguenther arcn->org_name);
638df930be7Sderaadt return(1);
639df930be7Sderaadt }
640df930be7Sderaadt arcn->pad = TAR_PAD(arcn->sb.st_size);
641df930be7Sderaadt }
642df930be7Sderaadt
643df930be7Sderaadt /*
644df930be7Sderaadt * copy those fields that are independent of the type
645df930be7Sderaadt */
6469a58b8c6Sguenther if (ul_oct(arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) ||
647f37d86feSguenther ull_oct(arcn->sb.st_mtime < 0 ? 0 : arcn->sb.st_mtime, hd->mtime,
648b5f8b155Sguenther sizeof(hd->mtime), 1) ||
6499a58b8c6Sguenther ul_oct(arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) ||
6509a58b8c6Sguenther ul_oct(arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0))
651df930be7Sderaadt goto out;
652df930be7Sderaadt
653df930be7Sderaadt /*
654df930be7Sderaadt * calculate and add the checksum, then write the header. A return of
655df930be7Sderaadt * 0 tells the caller to now write the file data, 1 says no data needs
656df930be7Sderaadt * to be written
657df930be7Sderaadt */
658df930be7Sderaadt if (ul_oct(tar_chksm(hdblk, sizeof(HD_TAR)), hd->chksum,
6592407619dStholo sizeof(hd->chksum), 3))
660df930be7Sderaadt goto out;
661df930be7Sderaadt if (wr_rdbuf(hdblk, sizeof(HD_TAR)) < 0)
662df930be7Sderaadt return(-1);
6639a58b8c6Sguenther if (wr_skip(BLKMULT - sizeof(HD_TAR)) < 0)
664df930be7Sderaadt return(-1);
6655316f7a4Sguenther if (PAX_IS_REG(arcn->type))
666df930be7Sderaadt return(0);
667df930be7Sderaadt return(1);
668df930be7Sderaadt
669df930be7Sderaadt out:
670df930be7Sderaadt /*
671df930be7Sderaadt * header field is out of range
672df930be7Sderaadt */
67342cf9836Stholo paxwarn(1, "Tar header field is too small for %s", arcn->org_name);
674df930be7Sderaadt return(1);
675df930be7Sderaadt }
676df930be7Sderaadt
677df930be7Sderaadt /*
678df930be7Sderaadt * Routines for POSIX ustar
679df930be7Sderaadt */
680df930be7Sderaadt
681df930be7Sderaadt /*
682df930be7Sderaadt * ustar_id()
683df930be7Sderaadt * determine if a block given to us is a valid ustar header. We have to
684df930be7Sderaadt * be on the lookout for those pesky blocks of all zero's
685df930be7Sderaadt * Return:
686df930be7Sderaadt * 0 if a ustar header, -1 otherwise
687df930be7Sderaadt */
688df930be7Sderaadt
689df930be7Sderaadt int
ustar_id(char * blk,int size)690df930be7Sderaadt ustar_id(char *blk, int size)
691df930be7Sderaadt {
692be87792eSmillert HD_USTAR *hd;
693df930be7Sderaadt
694df930be7Sderaadt if (size < BLKMULT)
695df930be7Sderaadt return(-1);
696df930be7Sderaadt hd = (HD_USTAR *)blk;
697df930be7Sderaadt
698df930be7Sderaadt /*
699df930be7Sderaadt * check for block of zero's first, a simple and fast test then check
700df930be7Sderaadt * ustar magic cookie. We should use TMAGLEN, but some USTAR archive
701df930be7Sderaadt * programs are fouled up and create archives missing the \0. Last we
702df930be7Sderaadt * check the checksum. If ok we have to assume it is a valid header.
703df930be7Sderaadt */
70464e70991Sotto if (hd->prefix[0] == '\0' && hd->name[0] == '\0')
705df930be7Sderaadt return(-1);
706df930be7Sderaadt if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0)
707df930be7Sderaadt return(-1);
708df930be7Sderaadt if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT))
709df930be7Sderaadt return(-1);
710df930be7Sderaadt return(0);
711df930be7Sderaadt }
712df930be7Sderaadt
713df930be7Sderaadt /*
714df930be7Sderaadt * ustar_rd()
715df930be7Sderaadt * extract the values out of block already determined to be a ustar header.
716df930be7Sderaadt * store the values in the ARCHD parameter.
717df930be7Sderaadt * Return:
718df930be7Sderaadt * 0
719df930be7Sderaadt */
720df930be7Sderaadt
721df930be7Sderaadt int
ustar_rd(ARCHD * arcn,char * buf)722be87792eSmillert ustar_rd(ARCHD *arcn, char *buf)
723df930be7Sderaadt {
7242dbd6dc5Sguenther HD_USTAR *hd = (HD_USTAR *)buf;
725be87792eSmillert char *dest;
726be87792eSmillert int cnt = 0;
727df930be7Sderaadt dev_t devmajor;
728df930be7Sderaadt dev_t devminor;
729f37d86feSguenther unsigned long long val;
730df930be7Sderaadt
731df930be7Sderaadt /*
732df930be7Sderaadt * we only get proper sized buffers
733df930be7Sderaadt */
734df930be7Sderaadt if (ustar_id(buf, BLKMULT) < 0)
735df930be7Sderaadt return(-1);
7362dbd6dc5Sguenther
7372dbd6dc5Sguenther reset:
738a79a0570Smillert memset(arcn, 0, sizeof(*arcn));
739df930be7Sderaadt arcn->org_name = arcn->name;
740df930be7Sderaadt arcn->sb.st_nlink = 1;
741eafddf6eSjca arcn->sb.st_size = (off_t)-1;
742df930be7Sderaadt
7432dbd6dc5Sguenther /* Process Extended headers. */
744734dd9a4Sfgsch if (hd->typeflag == XHDRTYPE || hd->typeflag == GHDRTYPE) {
7452dbd6dc5Sguenther if (rd_xheader(arcn, hd->typeflag == GHDRTYPE,
7468e4f2f54Sderaadt (off_t)asc_ull(hd->size, sizeof(hd->size), OCT)) < 0)
747734dd9a4Sfgsch return (-1);
7482dbd6dc5Sguenther
7492dbd6dc5Sguenther /* Update and check the ustar header. */
7502dbd6dc5Sguenther if (rd_wrbuf(buf, BLKMULT) != BLKMULT)
7512dbd6dc5Sguenther return (-1);
7522dbd6dc5Sguenther if (ustar_id(buf, BLKMULT) < 0)
7532dbd6dc5Sguenther return(-1);
7542dbd6dc5Sguenther
7552dbd6dc5Sguenther /* if the next block is another extension, reset the values */
7562dbd6dc5Sguenther if (hd->typeflag == XHDRTYPE || hd->typeflag == GHDRTYPE)
7572dbd6dc5Sguenther goto reset;
758734dd9a4Sfgsch }
759734dd9a4Sfgsch
760734dd9a4Sfgsch if (!arcn->nlen) {
761df930be7Sderaadt /*
762734dd9a4Sfgsch * See if the filename is split into two parts. if, so join
763734dd9a4Sfgsch * the parts. We copy the prefix first and add a / between
764734dd9a4Sfgsch * the prefix and name.
765df930be7Sderaadt */
766df930be7Sderaadt dest = arcn->name;
767df930be7Sderaadt if (*(hd->prefix) != '\0') {
768734dd9a4Sfgsch cnt = fieldcpy(dest, sizeof(arcn->name) - 1,
769734dd9a4Sfgsch hd->prefix, sizeof(hd->prefix));
7702407619dStholo dest += cnt;
771df930be7Sderaadt *dest++ = '/';
7722407619dStholo cnt++;
773734dd9a4Sfgsch } else
774a79a0570Smillert cnt = 0;
775734dd9a4Sfgsch
776734dd9a4Sfgsch if (hd->typeflag != LONGLINKTYPE &&
777734dd9a4Sfgsch hd->typeflag != LONGNAMETYPE) {
778734dd9a4Sfgsch arcn->nlen = cnt + expandname(dest,
779734dd9a4Sfgsch sizeof(arcn->name) - cnt, &gnu_name_string,
780734dd9a4Sfgsch hd->name, sizeof(hd->name));
781734dd9a4Sfgsch }
782a79a0570Smillert }
783a79a0570Smillert
784734dd9a4Sfgsch if (!arcn->ln_nlen &&
785734dd9a4Sfgsch hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) {
786a79a0570Smillert arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name),
787386e027cSotto &gnu_link_string, hd->linkname, sizeof(hd->linkname));
788da60b202Smillert }
789df930be7Sderaadt
790df930be7Sderaadt /*
791df930be7Sderaadt * follow the spec to the letter. we should only have mode bits, strip
792df930be7Sderaadt * off all other crud we may be passed.
793df930be7Sderaadt */
794df930be7Sderaadt arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) &
795df930be7Sderaadt 0xfff);
796eafddf6eSjca if (arcn->sb.st_size == (off_t)-1) {
797eafddf6eSjca arcn->sb.st_size =
798eafddf6eSjca (off_t)asc_ull(hd->size, sizeof(hd->size), OCT);
799eafddf6eSjca }
8003e1094ecSsthen if (arcn->sb.st_mtime == 0) {
801f37d86feSguenther val = asc_ull(hd->mtime, sizeof(hd->mtime), OCT);
80276c0e1cfSotto if (val > MAX_TIME_T)
8032f186deaSmillert arcn->sb.st_mtime = MAX_TIME_T;
804bdaaddddSguenther else
805bdaaddddSguenther arcn->sb.st_mtime = val;
8063e1094ecSsthen }
8073e1094ecSsthen if (arcn->sb.st_ctime == 0) {
808e425abdcSguenther arcn->sb.st_ctim = arcn->sb.st_mtim;
8093e1094ecSsthen }
8103e1094ecSsthen if (arcn->sb.st_atime == 0) {
811e425abdcSguenther arcn->sb.st_atim = arcn->sb.st_mtim;
8123e1094ecSsthen }
813df930be7Sderaadt
814df930be7Sderaadt /*
815df930be7Sderaadt * If we can find the ascii names for gname and uname in the password
816df930be7Sderaadt * and group files we will use the uid's and gid they bind. Otherwise
817df930be7Sderaadt * we use the uid and gid values stored in the header. (This is what
818df930be7Sderaadt * the posix spec wants).
819df930be7Sderaadt */
820df930be7Sderaadt hd->gname[sizeof(hd->gname) - 1] = '\0';
8216ed32900Sderaadt if (Nflag || gid_from_group(hd->gname, &(arcn->sb.st_gid)) == -1)
822df930be7Sderaadt arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT);
823df930be7Sderaadt hd->uname[sizeof(hd->uname) - 1] = '\0';
8246ed32900Sderaadt if (Nflag || uid_from_user(hd->uname, &(arcn->sb.st_uid)) == -1)
825df930be7Sderaadt arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT);
826df930be7Sderaadt
827df930be7Sderaadt /*
828df930be7Sderaadt * set the defaults, these may be changed depending on the file type
829df930be7Sderaadt */
830df930be7Sderaadt arcn->pad = 0;
831df930be7Sderaadt arcn->skip = 0;
832df930be7Sderaadt arcn->sb.st_rdev = (dev_t)0;
833df930be7Sderaadt
834df930be7Sderaadt /*
835df930be7Sderaadt * set the mode and PAX type according to the typeflag in the header
836df930be7Sderaadt */
837df930be7Sderaadt switch (hd->typeflag) {
838df930be7Sderaadt case FIFOTYPE:
839df930be7Sderaadt arcn->type = PAX_FIF;
840df930be7Sderaadt arcn->sb.st_mode |= S_IFIFO;
841df930be7Sderaadt break;
842df930be7Sderaadt case DIRTYPE:
843df930be7Sderaadt arcn->type = PAX_DIR;
844df930be7Sderaadt arcn->sb.st_mode |= S_IFDIR;
845df930be7Sderaadt arcn->sb.st_nlink = 2;
846df930be7Sderaadt
847df930be7Sderaadt /*
848df930be7Sderaadt * Some programs that create ustar archives append a '/'
849df930be7Sderaadt * to the pathname for directories. This clearly violates
850df930be7Sderaadt * ustar specs, but we will silently strip it off anyway.
851df930be7Sderaadt */
852df930be7Sderaadt if (arcn->name[arcn->nlen - 1] == '/')
853df930be7Sderaadt arcn->name[--arcn->nlen] = '\0';
854df930be7Sderaadt break;
855df930be7Sderaadt case BLKTYPE:
856df930be7Sderaadt case CHRTYPE:
857df930be7Sderaadt /*
858df930be7Sderaadt * this type requires the rdev field to be set.
859df930be7Sderaadt */
860df930be7Sderaadt if (hd->typeflag == BLKTYPE) {
861df930be7Sderaadt arcn->type = PAX_BLK;
862df930be7Sderaadt arcn->sb.st_mode |= S_IFBLK;
863df930be7Sderaadt } else {
864df930be7Sderaadt arcn->type = PAX_CHR;
865df930be7Sderaadt arcn->sb.st_mode |= S_IFCHR;
866df930be7Sderaadt }
867df930be7Sderaadt devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT);
868df930be7Sderaadt devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT);
869df930be7Sderaadt arcn->sb.st_rdev = TODEV(devmajor, devminor);
870df930be7Sderaadt break;
871df930be7Sderaadt case SYMTYPE:
872df930be7Sderaadt case LNKTYPE:
873df930be7Sderaadt if (hd->typeflag == SYMTYPE) {
874df930be7Sderaadt arcn->type = PAX_SLK;
875df930be7Sderaadt arcn->sb.st_mode |= S_IFLNK;
876df930be7Sderaadt } else {
877df930be7Sderaadt arcn->type = PAX_HLK;
878df930be7Sderaadt /*
879df930be7Sderaadt * so printing looks better
880df930be7Sderaadt */
881df930be7Sderaadt arcn->sb.st_mode |= S_IFREG;
882df930be7Sderaadt arcn->sb.st_nlink = 2;
883df930be7Sderaadt }
884df930be7Sderaadt break;
885da60b202Smillert case LONGLINKTYPE:
886da60b202Smillert case LONGNAMETYPE:
887da60b202Smillert /*
888da60b202Smillert * GNU long link/file; we tag these here and let the
889da60b202Smillert * pax internals deal with it -- too ugly otherwise.
890da60b202Smillert */
891da60b202Smillert arcn->type =
892da60b202Smillert hd->typeflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF;
893da60b202Smillert arcn->pad = TAR_PAD(arcn->sb.st_size);
894da60b202Smillert arcn->skip = arcn->sb.st_size;
895da60b202Smillert break;
896df930be7Sderaadt case CONTTYPE:
897df930be7Sderaadt case AREGTYPE:
898df930be7Sderaadt case REGTYPE:
899df930be7Sderaadt default:
900df930be7Sderaadt /*
901df930be7Sderaadt * these types have file data that follows. Set the skip and
902df930be7Sderaadt * pad fields.
903df930be7Sderaadt */
904df930be7Sderaadt arcn->type = PAX_REG;
905df930be7Sderaadt arcn->pad = TAR_PAD(arcn->sb.st_size);
906df930be7Sderaadt arcn->skip = arcn->sb.st_size;
907df930be7Sderaadt arcn->sb.st_mode |= S_IFREG;
908df930be7Sderaadt break;
909df930be7Sderaadt }
910df930be7Sderaadt return(0);
911df930be7Sderaadt }
912df930be7Sderaadt
913013e174aSjca #ifndef SMALL
914013e174aSjca static int
xheader_add(struct xheader * xhdr,const char * keyword,const char * value)915013e174aSjca xheader_add(struct xheader *xhdr, const char *keyword,
916013e174aSjca const char *value)
917013e174aSjca {
918013e174aSjca struct xheader_record *rec;
919013e174aSjca int reclen, tmplen;
920013e174aSjca char *s;
921df930be7Sderaadt
922013e174aSjca tmplen = MINXHDRSZ;
923013e174aSjca do {
924013e174aSjca reclen = tmplen;
925013e174aSjca tmplen = snprintf(NULL, 0, "%d %s=%s\n", reclen, keyword,
926013e174aSjca value);
927013e174aSjca } while (tmplen >= 0 && tmplen != reclen);
928013e174aSjca if (tmplen < 0)
929013e174aSjca return -1;
930013e174aSjca
931013e174aSjca rec = calloc(1, sizeof(*rec));
932013e174aSjca if (rec == NULL)
933013e174aSjca return -1;
934013e174aSjca rec->reclen = reclen;
935013e174aSjca if (asprintf(&s, "%d %s=%s\n", reclen, keyword, value) < 0) {
936013e174aSjca free(rec);
937013e174aSjca return -1;
938013e174aSjca }
939013e174aSjca rec->record = s;
940013e174aSjca
941013e174aSjca SLIST_INSERT_HEAD(xhdr, rec, entry);
942013e174aSjca
943013e174aSjca return 0;
944013e174aSjca }
945013e174aSjca
946eafddf6eSjca static int
xheader_add_ull(struct xheader * xhdr,const char * keyword,unsigned long long value)947eafddf6eSjca xheader_add_ull(struct xheader *xhdr, const char *keyword,
948eafddf6eSjca unsigned long long value)
949eafddf6eSjca {
950eafddf6eSjca struct xheader_record *rec;
951eafddf6eSjca int reclen, tmplen;
952eafddf6eSjca char *s;
953eafddf6eSjca
954eafddf6eSjca tmplen = MINXHDRSZ;
955eafddf6eSjca do {
956eafddf6eSjca reclen = tmplen;
957eafddf6eSjca tmplen = snprintf(NULL, 0, "%d %s=%llu\n", reclen, keyword,
958eafddf6eSjca value);
959eafddf6eSjca } while (tmplen >= 0 && tmplen != reclen);
960eafddf6eSjca if (tmplen < 0)
961eafddf6eSjca return -1;
962eafddf6eSjca
963eafddf6eSjca rec = calloc(1, sizeof(*rec));
964eafddf6eSjca if (rec == NULL)
965eafddf6eSjca return -1;
966eafddf6eSjca rec->reclen = reclen;
967eafddf6eSjca if (asprintf(&s, "%d %s=%llu\n", reclen, keyword, value) < 0) {
968eafddf6eSjca free(rec);
969eafddf6eSjca return -1;
970eafddf6eSjca }
971eafddf6eSjca rec->record = s;
972eafddf6eSjca
973eafddf6eSjca SLIST_INSERT_HEAD(xhdr, rec, entry);
974eafddf6eSjca
975eafddf6eSjca return 0;
976eafddf6eSjca }
977eafddf6eSjca
978554c9a8fSjca static int
xheader_add_ts(struct xheader * xhdr,const char * keyword,const struct timespec * value)979554c9a8fSjca xheader_add_ts(struct xheader *xhdr, const char *keyword,
980554c9a8fSjca const struct timespec *value)
981554c9a8fSjca {
982554c9a8fSjca struct xheader_record *rec;
983554c9a8fSjca int reclen, tmplen;
984b54e1577Sjca char frac[sizeof(".111222333")] = "";
985554c9a8fSjca char *s;
986554c9a8fSjca
987b54e1577Sjca /* Only write subsecond part if non-zero */
988b54e1577Sjca if (value->tv_nsec != 0) {
989b54e1577Sjca int n;
990b54e1577Sjca
991b54e1577Sjca n = snprintf(frac, sizeof(frac), ".%09ld",
992b54e1577Sjca (long)value->tv_nsec);
993b54e1577Sjca if (n <= 0)
994b54e1577Sjca return -1;
995b54e1577Sjca
996b54e1577Sjca /* Zap trailing zeros */
997b54e1577Sjca for (n--; n > 1 && frac[n] == '0'; n--)
998b54e1577Sjca frac[n] = '\0';
999b54e1577Sjca }
1000b54e1577Sjca
1001554c9a8fSjca tmplen = MINXHDRSZ;
1002554c9a8fSjca do {
1003554c9a8fSjca reclen = tmplen;
1004b54e1577Sjca tmplen = snprintf(NULL, 0, "%d %s=%lld%s\n", reclen,
1005b54e1577Sjca keyword, (long long)value->tv_sec, frac);
1006554c9a8fSjca } while (tmplen >= 0 && tmplen != reclen);
1007554c9a8fSjca if (tmplen < 0)
1008554c9a8fSjca return -1;
1009554c9a8fSjca
1010554c9a8fSjca rec = calloc(1, sizeof(*rec));
1011554c9a8fSjca if (rec == NULL)
1012554c9a8fSjca return -1;
1013554c9a8fSjca rec->reclen = reclen;
1014b54e1577Sjca if (asprintf(&s, "%d %s=%lld%s\n", reclen, keyword,
1015b54e1577Sjca (long long)value->tv_sec, frac) < 0) {
1016554c9a8fSjca free(rec);
1017554c9a8fSjca return -1;
1018554c9a8fSjca }
1019554c9a8fSjca rec->record = s;
1020554c9a8fSjca
1021554c9a8fSjca SLIST_INSERT_HEAD(xhdr, rec, entry);
1022554c9a8fSjca
1023554c9a8fSjca return 0;
1024554c9a8fSjca }
1025554c9a8fSjca
1026013e174aSjca static void
xheader_free(struct xheader * xhdr)1027013e174aSjca xheader_free(struct xheader *xhdr)
1028013e174aSjca {
1029013e174aSjca struct xheader_record *rec;
1030013e174aSjca
1031013e174aSjca while (!SLIST_EMPTY(xhdr)) {
1032013e174aSjca rec = SLIST_FIRST(xhdr);
1033013e174aSjca SLIST_REMOVE_HEAD(xhdr, entry);
1034013e174aSjca free(rec->record);
1035013e174aSjca free(rec);
1036013e174aSjca }
1037013e174aSjca }
1038013e174aSjca
1039013e174aSjca static int
wr_xheader(ARCHD * arcn,struct xheader * xhdr)1040013e174aSjca wr_xheader(ARCHD *arcn, struct xheader *xhdr)
1041013e174aSjca {
1042013e174aSjca char hdblk[sizeof(HD_USTAR)];
1043013e174aSjca HD_USTAR *hd;
1044013e174aSjca char buf[sizeof(hd->name) + 1];
1045013e174aSjca struct xheader_record *rec;
1046013e174aSjca size_t size;
1047013e174aSjca
1048013e174aSjca size = 0;
1049013e174aSjca SLIST_FOREACH(rec, xhdr, entry)
1050013e174aSjca size += rec->reclen;
1051013e174aSjca
1052013e174aSjca memset(hdblk, 0, sizeof(hdblk));
1053013e174aSjca hd = (HD_USTAR *)hdblk;
1054013e174aSjca hd->typeflag = XHDRTYPE;
1055013e174aSjca strncpy(hd->magic, TMAGIC, TMAGLEN);
1056013e174aSjca strncpy(hd->version, TVERSION, TVERSLEN);
1057013e174aSjca if (ul_oct(size, hd->size, sizeof(hd->size), 3))
1058013e174aSjca return -1;
1059013e174aSjca
1060013e174aSjca /*
1061013e174aSjca * Best effort attempt at providing a useful file name for
1062013e174aSjca * implementations that don't support pax format. Don't bother
1063013e174aSjca * with truncation if the resulting file name doesn't fit.
1064013e174aSjca * XXX dirname/basename portability (check return value?)
1065013e174aSjca */
1066013e174aSjca (void)snprintf(buf, sizeof(buf), "%s/PaxHeaders.%ld/%s",
1067013e174aSjca dirname(arcn->name), (long)getpid(), basename(arcn->name));
1068013e174aSjca fieldcpy(hd->name, sizeof(hd->name), buf, sizeof(buf));
1069013e174aSjca
1070013e174aSjca if (ul_oct(arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) ||
1071013e174aSjca ull_oct(arcn->sb.st_mtime < 0 ? 0 : arcn->sb.st_mtime, hd->mtime,
1072013e174aSjca sizeof(hd->mtime), 1) ||
1073013e174aSjca ul_oct(arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) ||
1074013e174aSjca ul_oct(arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0))
1075013e174aSjca return -1;
1076013e174aSjca
1077013e174aSjca if (ul_oct(tar_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum,
1078013e174aSjca sizeof(hd->chksum), 3))
1079013e174aSjca return -1;
1080013e174aSjca
1081013e174aSjca /* write out extended header */
1082013e174aSjca if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0)
1083013e174aSjca return -1;
1084013e174aSjca if (wr_skip(BLKMULT - sizeof(HD_USTAR)) < 0)
1085013e174aSjca return -1;
1086013e174aSjca
1087013e174aSjca /* write out extended header records */
1088013e174aSjca SLIST_FOREACH(rec, xhdr, entry)
1089013e174aSjca if (wr_rdbuf(rec->record, rec->reclen) < 0)
1090013e174aSjca return -1;
1091013e174aSjca
1092013e174aSjca if (wr_skip(TAR_PAD(size)) < 0)
1093013e174aSjca return -1;
1094013e174aSjca
1095013e174aSjca return 0;
1096013e174aSjca }
1097013e174aSjca #endif
1098013e174aSjca
1099013e174aSjca static int
wr_ustar_or_pax(ARCHD * arcn,int ustar)1100013e174aSjca wr_ustar_or_pax(ARCHD *arcn, int ustar)
1101df930be7Sderaadt {
1102be87792eSmillert HD_USTAR *hd;
1103f84583feSmillert const char *name;
1104f84583feSmillert char *pt, hdblk[sizeof(HD_USTAR)];
1105013e174aSjca #ifndef SMALL
1106013e174aSjca struct xheader xhdr = SLIST_HEAD_INITIALIZER(xhdr);
1107013e174aSjca #endif
1108554c9a8fSjca int bad_mtime;
1109df930be7Sderaadt
1110df930be7Sderaadt /*
1111df930be7Sderaadt * check for those file system types ustar cannot store
1112df930be7Sderaadt */
1113df930be7Sderaadt if (arcn->type == PAX_SCK) {
111442cf9836Stholo paxwarn(1, "Ustar cannot archive a socket %s", arcn->org_name);
1115df930be7Sderaadt return(1);
1116df930be7Sderaadt }
1117df930be7Sderaadt
1118df930be7Sderaadt /*
1119403f1672Szhuk * user asked that dirs not be written to the archive
1120403f1672Szhuk */
1121403f1672Szhuk if (arcn->type == PAX_DIR && tar_nodir)
1122403f1672Szhuk return (1);
1123403f1672Szhuk
1124403f1672Szhuk /*
1125df930be7Sderaadt * check the length of the linkname
1126df930be7Sderaadt */
1127a38f4902Sotto if (PAX_IS_LINK(arcn->type) &&
1128a38f4902Sotto ((size_t)arcn->ln_nlen > sizeof(hd->linkname))) {
1129013e174aSjca if (ustar) {
1130013e174aSjca paxwarn(1, "Link name too long for ustar %s",
1131013e174aSjca arcn->ln_name);
1132df930be7Sderaadt return(1);
1133df930be7Sderaadt }
1134013e174aSjca #ifndef SMALL
1135cf0ae65bSjca else if (xheader_add(&xhdr, "linkpath", arcn->ln_name) == -1) {
1136013e174aSjca paxwarn(1, "Link name too long for pax %s",
1137013e174aSjca arcn->ln_name);
1138013e174aSjca xheader_free(&xhdr);
1139013e174aSjca return(1);
1140013e174aSjca }
1141013e174aSjca #endif
1142013e174aSjca }
1143df930be7Sderaadt
1144df930be7Sderaadt /*
1145df930be7Sderaadt * split the path name into prefix and name fields (if needed). if
1146df930be7Sderaadt * pt != arcn->name, the name has to be split
1147df930be7Sderaadt */
1148df930be7Sderaadt if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) {
1149013e174aSjca if (ustar) {
1150013e174aSjca paxwarn(1, "File name too long for ustar %s",
1151013e174aSjca arcn->name);
1152df930be7Sderaadt return(1);
1153df930be7Sderaadt }
1154013e174aSjca #ifndef SMALL
1155013e174aSjca else if (xheader_add(&xhdr, "path", arcn->name) == -1) {
1156013e174aSjca paxwarn(1, "File name too long for pax %s",
1157ba5cb451Sjca arcn->name);
1158013e174aSjca xheader_free(&xhdr);
1159013e174aSjca return(1);
1160013e174aSjca }
1161013e174aSjca /* PAX format, we don't need to split the path */
1162013e174aSjca pt = arcn->name;
1163013e174aSjca #endif
1164013e174aSjca }
11657a25f9afSmillert
11667a25f9afSmillert /*
11677a25f9afSmillert * zero out the header so we don't have to worry about zero fill below
11687a25f9afSmillert */
11697a25f9afSmillert memset(hdblk, 0, sizeof(hdblk));
1170df930be7Sderaadt hd = (HD_USTAR *)hdblk;
11719a58b8c6Sguenther arcn->pad = 0;
1172df930be7Sderaadt
1173df930be7Sderaadt /*
1174df930be7Sderaadt * split the name, or zero out the prefix
1175df930be7Sderaadt */
1176df930be7Sderaadt if (pt != arcn->name) {
1177df930be7Sderaadt /*
1178df930be7Sderaadt * name was split, pt points at the / where the split is to
1179df930be7Sderaadt * occur, we remove the / and copy the first part to the prefix
1180df930be7Sderaadt */
1181df930be7Sderaadt *pt = '\0';
11829cfd895cSotto fieldcpy(hd->prefix, sizeof(hd->prefix), arcn->name,
11839cfd895cSotto sizeof(arcn->name));
1184df930be7Sderaadt *pt++ = '/';
11857a25f9afSmillert }
1186df930be7Sderaadt
1187df930be7Sderaadt /*
1188df930be7Sderaadt * copy the name part. this may be the whole path or the part after
1189df930be7Sderaadt * the prefix
1190df930be7Sderaadt */
11919cfd895cSotto fieldcpy(hd->name, sizeof(hd->name), pt,
11929cfd895cSotto sizeof(arcn->name) - (pt - arcn->name));
1193df930be7Sderaadt
1194df930be7Sderaadt /*
1195df930be7Sderaadt * set the fields in the header that are type dependent
1196df930be7Sderaadt */
1197df930be7Sderaadt switch (arcn->type) {
1198df930be7Sderaadt case PAX_DIR:
1199df930be7Sderaadt hd->typeflag = DIRTYPE;
12009a58b8c6Sguenther if (ul_oct(0, hd->size, sizeof(hd->size), 3))
1201df930be7Sderaadt goto out;
1202df930be7Sderaadt break;
1203df930be7Sderaadt case PAX_CHR:
1204df930be7Sderaadt case PAX_BLK:
1205df930be7Sderaadt if (arcn->type == PAX_CHR)
1206df930be7Sderaadt hd->typeflag = CHRTYPE;
1207df930be7Sderaadt else
1208df930be7Sderaadt hd->typeflag = BLKTYPE;
12099a58b8c6Sguenther if (ul_oct(MAJOR(arcn->sb.st_rdev), hd->devmajor,
1210df930be7Sderaadt sizeof(hd->devmajor), 3) ||
12119a58b8c6Sguenther ul_oct(MINOR(arcn->sb.st_rdev), hd->devminor,
1212df930be7Sderaadt sizeof(hd->devminor), 3) ||
12139a58b8c6Sguenther ul_oct(0, hd->size, sizeof(hd->size), 3))
1214df930be7Sderaadt goto out;
1215df930be7Sderaadt break;
1216df930be7Sderaadt case PAX_FIF:
1217df930be7Sderaadt hd->typeflag = FIFOTYPE;
12189a58b8c6Sguenther if (ul_oct(0, hd->size, sizeof(hd->size), 3))
1219df930be7Sderaadt goto out;
1220df930be7Sderaadt break;
1221df930be7Sderaadt case PAX_SLK:
1222df930be7Sderaadt case PAX_HLK:
1223df930be7Sderaadt case PAX_HRG:
1224df930be7Sderaadt if (arcn->type == PAX_SLK)
1225df930be7Sderaadt hd->typeflag = SYMTYPE;
1226df930be7Sderaadt else
1227df930be7Sderaadt hd->typeflag = LNKTYPE;
12289cfd895cSotto fieldcpy(hd->linkname, sizeof(hd->linkname), arcn->ln_name,
12299cfd895cSotto sizeof(arcn->ln_name));
12309a58b8c6Sguenther if (ul_oct(0, hd->size, sizeof(hd->size), 3))
1231df930be7Sderaadt goto out;
1232df930be7Sderaadt break;
1233df930be7Sderaadt case PAX_REG:
1234df930be7Sderaadt case PAX_CTG:
1235df930be7Sderaadt default:
1236df930be7Sderaadt /*
1237df930be7Sderaadt * file data with this type, set the padding
1238df930be7Sderaadt */
1239df930be7Sderaadt if (arcn->type == PAX_CTG)
1240df930be7Sderaadt hd->typeflag = CONTTYPE;
1241df930be7Sderaadt else
1242df930be7Sderaadt hd->typeflag = REGTYPE;
1243df930be7Sderaadt arcn->pad = TAR_PAD(arcn->sb.st_size);
1244f37d86feSguenther if (ull_oct(arcn->sb.st_size, hd->size, sizeof(hd->size), 3)) {
1245eafddf6eSjca if (ustar) {
1246f37d86feSguenther paxwarn(1, "File is too long for ustar %s",
1247f37d86feSguenther arcn->org_name);
1248df930be7Sderaadt return(1);
1249df930be7Sderaadt }
1250eafddf6eSjca #ifndef SMALL
1251eafddf6eSjca else if (xheader_add_ull(&xhdr, "size",
1252eafddf6eSjca arcn->sb.st_size) == -1) {
1253eafddf6eSjca paxwarn(1, "File is too long for pax %s",
1254eafddf6eSjca arcn->org_name);
1255eafddf6eSjca xheader_free(&xhdr);
1256eafddf6eSjca return(1);
1257eafddf6eSjca }
1258eafddf6eSjca #endif
1259eafddf6eSjca }
1260df930be7Sderaadt break;
1261df930be7Sderaadt }
1262df930be7Sderaadt
12638f3d3452Smickey strncpy(hd->magic, TMAGIC, TMAGLEN);
12648f3d3452Smickey strncpy(hd->version, TVERSION, TVERSLEN);
1265df930be7Sderaadt
1266df930be7Sderaadt /*
1267df930be7Sderaadt * set the remaining fields. Some versions want all 16 bits of mode
1268df930be7Sderaadt * we better humor them (they really do not meet spec though)....
1269df930be7Sderaadt */
12709a58b8c6Sguenther if (ul_oct(arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 3)) {
1271b45d27d7Sotto if (uid_nobody == 0) {
1272f84583feSmillert if (uid_from_user("nobody", &uid_nobody) == -1)
1273b45d27d7Sotto goto out;
1274b45d27d7Sotto }
1275b45d27d7Sotto if (uid_warn != arcn->sb.st_uid) {
1276b45d27d7Sotto uid_warn = arcn->sb.st_uid;
1277b45d27d7Sotto paxwarn(1,
1278b45d27d7Sotto "Ustar header field is too small for uid %lu, "
1279b45d27d7Sotto "using nobody", (u_long)arcn->sb.st_uid);
1280b45d27d7Sotto }
12819a58b8c6Sguenther if (ul_oct(uid_nobody, hd->uid, sizeof(hd->uid), 3))
1282b45d27d7Sotto goto out;
1283b45d27d7Sotto }
12849a58b8c6Sguenther if (ul_oct(arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 3)) {
1285b45d27d7Sotto if (gid_nobody == 0) {
1286f84583feSmillert if (gid_from_group("nobody", &gid_nobody) == -1)
1287b45d27d7Sotto goto out;
1288b45d27d7Sotto }
1289b45d27d7Sotto if (gid_warn != arcn->sb.st_gid) {
1290b45d27d7Sotto gid_warn = arcn->sb.st_gid;
1291b45d27d7Sotto paxwarn(1,
1292b45d27d7Sotto "Ustar header field is too small for gid %lu, "
1293b45d27d7Sotto "using nobody", (u_long)arcn->sb.st_gid);
1294b45d27d7Sotto }
12959a58b8c6Sguenther if (ul_oct(gid_nobody, hd->gid, sizeof(hd->gid), 3))
1296b45d27d7Sotto goto out;
1297b45d27d7Sotto }
1298554c9a8fSjca bad_mtime = ull_oct(arcn->sb.st_mtime < 0 ? 0 : arcn->sb.st_mtime,
1299554c9a8fSjca hd->mtime, sizeof(hd->mtime), 3);
1300554c9a8fSjca if (bad_mtime && ustar)
1301554c9a8fSjca goto out;
1302554c9a8fSjca #ifndef SMALL
1303554c9a8fSjca if (!ustar) {
1304554c9a8fSjca /*
1305554c9a8fSjca * The pax format can preserve atime and store
1306554c9a8fSjca * a possibly more accurate mtime.
1307554c9a8fSjca *
1308554c9a8fSjca * ctime isn't specified by POSIX so omit it.
1309554c9a8fSjca */
1310554c9a8fSjca if (xheader_add_ts(&xhdr, "atime", &arcn->sb.st_atim) == -1) {
1311554c9a8fSjca paxwarn(1, "Couldn't preserve %s in pax format for %s",
1312554c9a8fSjca "atime", arcn->org_name);
1313554c9a8fSjca xheader_free(&xhdr);
1314554c9a8fSjca return (1);
1315554c9a8fSjca }
1316554c9a8fSjca if ((bad_mtime || arcn->sb.st_mtime < 0 ||
1317554c9a8fSjca arcn->sb.st_mtim.tv_nsec != 0) &&
1318554c9a8fSjca xheader_add_ts(&xhdr, "mtime", &arcn->sb.st_mtim) == -1) {
1319554c9a8fSjca paxwarn(1, "Couldn't preserve %s in pax format for %s",
1320554c9a8fSjca "mtime", arcn->org_name);
1321554c9a8fSjca xheader_free(&xhdr);
1322554c9a8fSjca return (1);
1323554c9a8fSjca }
1324554c9a8fSjca }
1325554c9a8fSjca #endif
1326554c9a8fSjca if (ul_oct(arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3))
1327df930be7Sderaadt goto out;
13285e460854Stedu if (!Nflag) {
13294c043852Sguenther if ((name = user_from_uid(arcn->sb.st_uid, 1)) != NULL)
13304c043852Sguenther strncpy(hd->uname, name, sizeof(hd->uname));
13314c043852Sguenther if ((name = group_from_gid(arcn->sb.st_gid, 1)) != NULL)
13324c043852Sguenther strncpy(hd->gname, name, sizeof(hd->gname));
13335e460854Stedu }
1334df930be7Sderaadt
1335013e174aSjca #ifndef SMALL
1336013e174aSjca /* write out a pax extended header if needed */
1337013e174aSjca if (!SLIST_EMPTY(&xhdr)) {
1338013e174aSjca int ret;
1339013e174aSjca
1340013e174aSjca ret = wr_xheader(arcn, &xhdr);
1341013e174aSjca xheader_free(&xhdr);
1342013e174aSjca if (ret == -1)
1343013e174aSjca return(-1);
1344013e174aSjca }
1345013e174aSjca #endif
1346013e174aSjca
1347df930be7Sderaadt /*
1348df930be7Sderaadt * calculate and store the checksum write the header to the archive
1349df930be7Sderaadt * return 0 tells the caller to now write the file data, 1 says no data
1350df930be7Sderaadt * needs to be written
1351df930be7Sderaadt */
1352df930be7Sderaadt if (ul_oct(tar_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum,
1353df930be7Sderaadt sizeof(hd->chksum), 3))
1354df930be7Sderaadt goto out;
1355df930be7Sderaadt if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0)
1356df930be7Sderaadt return(-1);
13579a58b8c6Sguenther if (wr_skip(BLKMULT - sizeof(HD_USTAR)) < 0)
1358df930be7Sderaadt return(-1);
13595316f7a4Sguenther if (PAX_IS_REG(arcn->type))
1360df930be7Sderaadt return(0);
1361df930be7Sderaadt return(1);
1362df930be7Sderaadt
1363df930be7Sderaadt out:
1364013e174aSjca #ifndef SMALL
1365013e174aSjca xheader_free(&xhdr);
1366013e174aSjca #endif
1367df930be7Sderaadt /*
1368df930be7Sderaadt * header field is out of range
1369df930be7Sderaadt */
137042cf9836Stholo paxwarn(1, "Ustar header field is too small for %s", arcn->org_name);
1371df930be7Sderaadt return(1);
1372df930be7Sderaadt }
1373df930be7Sderaadt
1374df930be7Sderaadt /*
1375013e174aSjca * ustar_wr()
1376013e174aSjca * Write out a ustar format archive.
1377013e174aSjca * Have to check for file types that cannot be stored and file names that
1378013e174aSjca * are too long. Be careful of the term (last arg) to ul_oct, we only use
1379013e174aSjca * '\0' for the termination character (this is different than picky tar).
1380013e174aSjca * ASSUMED: space after header in header block is zero filled
1381013e174aSjca * Return:
1382013e174aSjca * 0 if file has data to be written after the header, 1 if file has NO
1383013e174aSjca * data to write after the header, -1 if archive write failed
1384013e174aSjca */
1385013e174aSjca int
ustar_wr(ARCHD * arcn)1386013e174aSjca ustar_wr(ARCHD *arcn)
1387013e174aSjca {
1388013e174aSjca return wr_ustar_or_pax(arcn, 1);
1389013e174aSjca }
1390013e174aSjca
1391013e174aSjca /*
1392ce1e26fbSjca * pax_id()
1393ce1e26fbSjca * determine if a block given to us is a valid pax header.
1394ce1e26fbSjca * Return:
1395ce1e26fbSjca * 0 if a pax header, -1 otherwise
1396ce1e26fbSjca */
1397ce1e26fbSjca #ifndef SMALL
1398ce1e26fbSjca int
pax_id(char * blk,int size)1399ce1e26fbSjca pax_id(char *blk, int size)
1400ce1e26fbSjca {
1401ce1e26fbSjca HD_USTAR *hd;
1402ce1e26fbSjca
1403ce1e26fbSjca if (size < BLKMULT)
1404ce1e26fbSjca return(-1);
1405ce1e26fbSjca hd = (HD_USTAR *)blk;
1406ce1e26fbSjca
1407ce1e26fbSjca /*
1408ce1e26fbSjca * check for block of zero's first, a simple and fast test then check
1409ce1e26fbSjca * ustar magic cookie. We should use TMAGLEN, but some USTAR archive
1410ce1e26fbSjca * programs are fouled up and create archives missing the \0. Last we
1411ce1e26fbSjca * check the checksum and the type flag. If ok we have to assume it is
1412ce1e26fbSjca * a valid pax header.
1413ce1e26fbSjca */
1414ce1e26fbSjca if (hd->prefix[0] == '\0' && hd->name[0] == '\0')
1415ce1e26fbSjca return(-1);
1416ce1e26fbSjca if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0)
1417ce1e26fbSjca return(-1);
1418ce1e26fbSjca if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT))
1419ce1e26fbSjca return(-1);
1420ce1e26fbSjca /*
1421ce1e26fbSjca * It is valid for a pax formatted archive not to start with
1422ce1e26fbSjca * a global header nor with an extended header. In that case
1423ce1e26fbSjca * we'll fall back to ustar in append mode.
1424ce1e26fbSjca */
1425ce1e26fbSjca if (hd->typeflag == XHDRTYPE || hd->typeflag == GHDRTYPE)
1426ce1e26fbSjca return(0);
1427ce1e26fbSjca return (-1);
1428ce1e26fbSjca }
1429ce1e26fbSjca #endif
1430ce1e26fbSjca
1431ce1e26fbSjca /*
1432013e174aSjca * pax_wr()
1433013e174aSjca * Write out a pax format archive.
1434013e174aSjca * Have to check for file types that cannot be stored. Be careful of the
1435013e174aSjca * term (last arg) to ul_oct, we only use '\0' for the termination
1436013e174aSjca * character (this is different than picky tar).
1437013e174aSjca * ASSUMED: space after header in header block is zero filled
1438013e174aSjca * Return:
1439013e174aSjca * 0 if file has data to be written after the header, 1 if file has NO
1440013e174aSjca * data to write after the header, -1 if archive write failed
1441013e174aSjca */
1442013e174aSjca #ifndef SMALL
1443013e174aSjca int
pax_wr(ARCHD * arcn)1444013e174aSjca pax_wr(ARCHD *arcn)
1445013e174aSjca {
1446013e174aSjca return wr_ustar_or_pax(arcn, 0);
1447013e174aSjca }
1448013e174aSjca #endif
1449013e174aSjca
1450013e174aSjca /*
1451*8df76133Sjca * pax_opt()
1452*8df76133Sjca * handle pax format specific -o options
1453*8df76133Sjca * Return:
1454*8df76133Sjca * 0 if ok -1 otherwise
1455*8df76133Sjca */
1456*8df76133Sjca #ifndef SMALL
1457*8df76133Sjca int
pax_opt(void)1458*8df76133Sjca pax_opt(void)
1459*8df76133Sjca {
1460*8df76133Sjca OPLIST *opt;
1461*8df76133Sjca
1462*8df76133Sjca while ((opt = opt_next()) != NULL) {
1463*8df76133Sjca if (1) {
1464*8df76133Sjca paxwarn(1, "Unknown pax format -o option/value pair %s=%s",
1465*8df76133Sjca opt->name, opt->value);
1466*8df76133Sjca return(-1);
1467*8df76133Sjca }
1468*8df76133Sjca }
1469*8df76133Sjca return 0;
1470*8df76133Sjca }
1471*8df76133Sjca #endif
1472*8df76133Sjca
1473*8df76133Sjca /*
1474df930be7Sderaadt * name_split()
1475df930be7Sderaadt * see if the name has to be split for storage in a ustar header. We try
1476df930be7Sderaadt * to fit the entire name in the name field without splitting if we can.
1477df930be7Sderaadt * The split point is always at a /
1478df930be7Sderaadt * Return
1479df930be7Sderaadt * character pointer to split point (always the / that is to be removed
1480df930be7Sderaadt * if the split is not needed, the points is set to the start of the file
1481df930be7Sderaadt * name (it would violate the spec to split there). A NULL is returned if
1482df930be7Sderaadt * the file name is too long
1483df930be7Sderaadt */
1484df930be7Sderaadt
1485df930be7Sderaadt static char *
name_split(char * name,int len)1486be87792eSmillert name_split(char *name, int len)
1487df930be7Sderaadt {
1488be87792eSmillert char *start;
1489df930be7Sderaadt
1490df930be7Sderaadt /*
1491df930be7Sderaadt * check to see if the file name is small enough to fit in the name
1492df930be7Sderaadt * field. if so just return a pointer to the name.
14937f82ad52Sotto * The strings can fill the complete name and prefix fields
14947f82ad52Sotto * without a NUL terminator.
1495df930be7Sderaadt */
14967f82ad52Sotto if (len <= TNMSZ)
1497df930be7Sderaadt return(name);
14987f82ad52Sotto if (len > (TPFSZ + TNMSZ + 1))
1499df930be7Sderaadt return(NULL);
1500df930be7Sderaadt
1501df930be7Sderaadt /*
1502df930be7Sderaadt * we start looking at the biggest sized piece that fits in the name
15034eb0b000Smillert * field. We walk forward looking for a slash to split at. The idea is
1504df930be7Sderaadt * to find the biggest piece to fit in the name field (or the smallest
15057f82ad52Sotto * prefix we can find) (the -1 is correct the biggest piece would
15067f82ad52Sotto * include the slash between the two parts that gets thrown away)
1507df930be7Sderaadt */
15087f82ad52Sotto start = name + len - TNMSZ - 1;
1509315c96b9Sguenther
1510315c96b9Sguenther /*
1511315c96b9Sguenther * the prefix may not be empty, so skip the first character when
1512315c96b9Sguenther * trying to split a path of exactly TNMSZ+1 characters.
1513315c96b9Sguenther * NOTE: This means the ustar format can't store /str if
1514315c96b9Sguenther * str contains no slashes and the length of str == TNMSZ
1515315c96b9Sguenther */
1516315c96b9Sguenther if (start == name)
1517315c96b9Sguenther ++start;
1518315c96b9Sguenther
1519df930be7Sderaadt while ((*start != '\0') && (*start != '/'))
1520df930be7Sderaadt ++start;
1521df930be7Sderaadt
1522df930be7Sderaadt /*
1523df930be7Sderaadt * if we hit the end of the string, this name cannot be split, so we
1524df930be7Sderaadt * cannot store this file.
1525df930be7Sderaadt */
1526df930be7Sderaadt if (*start == '\0')
1527df930be7Sderaadt return(NULL);
1528df930be7Sderaadt
1529df930be7Sderaadt /*
1530315c96b9Sguenther * the split point isn't valid if it results in a prefix
1531315c96b9Sguenther * longer than TPFSZ
1532df930be7Sderaadt */
1533315c96b9Sguenther if ((start - name) > TPFSZ)
1534df930be7Sderaadt return(NULL);
1535df930be7Sderaadt
1536df930be7Sderaadt /*
1537df930be7Sderaadt * ok have a split point, return it to the caller
1538df930be7Sderaadt */
1539df930be7Sderaadt return(start);
1540df930be7Sderaadt }
1541a79a0570Smillert
1542a79a0570Smillert static size_t
expandname(char * buf,size_t len,char ** gnu_name,const char * name,size_t limit)1543386e027cSotto expandname(char *buf, size_t len, char **gnu_name, const char *name,
1544386e027cSotto size_t limit)
1545a79a0570Smillert {
1546a79a0570Smillert size_t nlen;
1547a79a0570Smillert
1548a79a0570Smillert if (*gnu_name) {
154964e70991Sotto /* *gnu_name is NUL terminated */
1550a79a0570Smillert if ((nlen = strlcpy(buf, *gnu_name, len)) >= len)
1551a79a0570Smillert nlen = len - 1;
1552a79a0570Smillert free(*gnu_name);
1553a79a0570Smillert *gnu_name = NULL;
155464e70991Sotto } else
155564e70991Sotto nlen = fieldcpy(buf, len, name, limit);
1556a79a0570Smillert return(nlen);
1557a79a0570Smillert }
1558734dd9a4Sfgsch
1559734dd9a4Sfgsch static int
rd_time(struct timespec * ts,const char * keyword,char * p)15603e1094ecSsthen rd_time(struct timespec *ts, const char *keyword, char *p)
15613e1094ecSsthen {
15623e1094ecSsthen const char *errstr;
15633e1094ecSsthen char *q;
15643e1094ecSsthen int multiplier;
15653e1094ecSsthen
15663e1094ecSsthen if ((q = strchr(p, '.')) != NULL)
15673e1094ecSsthen *q = '\0';
15683e1094ecSsthen
15693e1094ecSsthen ts->tv_sec = strtonum(p, 0, MAX_TIME_T, &errstr);
15703e1094ecSsthen if (errstr != NULL) {
15713e1094ecSsthen paxwarn(1, "%s is %s: %s", keyword, errstr, p);
15723e1094ecSsthen return -1;
15733e1094ecSsthen }
15743e1094ecSsthen
15753e1094ecSsthen ts->tv_nsec = 0;
15763e1094ecSsthen
15773e1094ecSsthen if (q == NULL)
15783e1094ecSsthen return 0;
15793e1094ecSsthen
15803e1094ecSsthen multiplier = 100000000;
15813e1094ecSsthen for (q++; *q != '\0'; q++) {
15823e1094ecSsthen if (!isdigit((unsigned char)*q)) {
15833e1094ecSsthen paxwarn(1, "%s contains non-digit", keyword);
15843e1094ecSsthen return -1;
15853e1094ecSsthen }
15863e1094ecSsthen ts->tv_nsec += (*q - '0') * multiplier;
15873e1094ecSsthen multiplier /= 10;
15883e1094ecSsthen }
15893e1094ecSsthen
15903e1094ecSsthen return 0;
15913e1094ecSsthen }
15923e1094ecSsthen
15933e1094ecSsthen static int
rd_size(off_t * size,const char * keyword,char * p)1594eafddf6eSjca rd_size(off_t *size, const char *keyword, char *p)
1595eafddf6eSjca {
1596eafddf6eSjca const char *errstr;
1597eafddf6eSjca
1598eafddf6eSjca /* Assume off_t is a long long. */
1599cd67bb40Sjca *size = strtonum(p, 0, LLONG_MAX, &errstr);
1600eafddf6eSjca if (errstr != NULL) {
1601eafddf6eSjca paxwarn(1, "%s is %s: %s", keyword, errstr, p);
1602eafddf6eSjca return -1;
1603eafddf6eSjca }
1604eafddf6eSjca
1605eafddf6eSjca return 0;
1606eafddf6eSjca }
1607eafddf6eSjca
1608eafddf6eSjca static int
rd_xheader(ARCHD * arcn,int global,off_t size)16092dbd6dc5Sguenther rd_xheader(ARCHD *arcn, int global, off_t size)
1610734dd9a4Sfgsch {
1611fe4b30a0Sjca /*
1612fe4b30a0Sjca * The pax format supposedly supports arbitrarily sized extended
1613fe4b30a0Sjca * record headers, this implementation doesn't.
1614fe4b30a0Sjca */
1615fe4b30a0Sjca char buf[sizeof("30xx linkpath=") - 1 + PAXPATHLEN + sizeof("\n")];
161679ba65b3Sotto long len;
1617734dd9a4Sfgsch char *delim, *keyword;
16182dbd6dc5Sguenther char *nextp, *p, *end;
16192dbd6dc5Sguenther int pad, ret = 0;
1620734dd9a4Sfgsch
16212dbd6dc5Sguenther /* before we alter size, make note of how much we have to skip */
16222dbd6dc5Sguenther pad = TAR_PAD((unsigned)size);
16232dbd6dc5Sguenther
16242dbd6dc5Sguenther p = end = buf;
16252dbd6dc5Sguenther while (size > 0 || p < end) {
16262dbd6dc5Sguenther if (size > 0) {
16272dbd6dc5Sguenther int rdlen;
16282dbd6dc5Sguenther
16292dbd6dc5Sguenther /* shift stuff down */
16302dbd6dc5Sguenther if (p > buf) {
16312dbd6dc5Sguenther memmove(buf, p, end - p);
16322dbd6dc5Sguenther end -= p - buf;
16332dbd6dc5Sguenther p = buf;
1634734dd9a4Sfgsch }
1635734dd9a4Sfgsch
16362dbd6dc5Sguenther /* fill starting at end */
16372dbd6dc5Sguenther rdlen = MINIMUM(size, (buf + sizeof buf) - end);
16382dbd6dc5Sguenther if (rd_wrbuf(end, rdlen) != rdlen) {
16392dbd6dc5Sguenther ret = -1;
16402dbd6dc5Sguenther break;
16412dbd6dc5Sguenther }
16422dbd6dc5Sguenther size -= rdlen;
16432dbd6dc5Sguenther end += rdlen;
16442dbd6dc5Sguenther }
16452dbd6dc5Sguenther
16462dbd6dc5Sguenther /* [p, end) is good */
16472dbd6dc5Sguenther if (memchr(p, ' ', end - p) == NULL ||
16482dbd6dc5Sguenther !isdigit((unsigned char)*p)) {
1649734dd9a4Sfgsch paxwarn(1, "Invalid extended header record");
16502dbd6dc5Sguenther ret = -1;
16512dbd6dc5Sguenther break;
1652734dd9a4Sfgsch }
1653734dd9a4Sfgsch errno = 0;
165479ba65b3Sotto len = strtol(p, &delim, 10);
165579ba65b3Sotto if (*delim != ' ' || (errno == ERANGE && len == LONG_MAX) ||
1656734dd9a4Sfgsch len < MINXHDRSZ) {
1657734dd9a4Sfgsch paxwarn(1, "Invalid extended header record length");
16582dbd6dc5Sguenther ret = -1;
16592dbd6dc5Sguenther break;
1660734dd9a4Sfgsch }
16612dbd6dc5Sguenther if (len > end - p) {
16622dbd6dc5Sguenther paxwarn(1, "Extended header record length %lu is "
16632dbd6dc5Sguenther "out of range", len);
16642dbd6dc5Sguenther /* if we can just toss this record, do so */
16652dbd6dc5Sguenther len -= end - p;
16662dbd6dc5Sguenther if (len <= size && rd_skip(len) == 0) {
16672dbd6dc5Sguenther size -= len;
16682dbd6dc5Sguenther p = end = buf;
16692dbd6dc5Sguenther continue;
16702dbd6dc5Sguenther }
16712dbd6dc5Sguenther ret = -1;
16722dbd6dc5Sguenther break;
1673734dd9a4Sfgsch }
1674734dd9a4Sfgsch nextp = p + len;
1675734dd9a4Sfgsch keyword = p = delim + 1;
1676734dd9a4Sfgsch p = memchr(p, '=', len);
1677734dd9a4Sfgsch if (!p || nextp[-1] != '\n') {
1678734dd9a4Sfgsch paxwarn(1, "Malformed extended header record");
16792dbd6dc5Sguenther ret = -1;
16802dbd6dc5Sguenther break;
1681734dd9a4Sfgsch }
1682734dd9a4Sfgsch *p++ = nextp[-1] = '\0';
16832dbd6dc5Sguenther if (!global) {
1684734dd9a4Sfgsch if (!strcmp(keyword, "path")) {
1685734dd9a4Sfgsch arcn->nlen = strlcpy(arcn->name, p,
1686734dd9a4Sfgsch sizeof(arcn->name));
1687734dd9a4Sfgsch } else if (!strcmp(keyword, "linkpath")) {
1688734dd9a4Sfgsch arcn->ln_nlen = strlcpy(arcn->ln_name, p,
1689734dd9a4Sfgsch sizeof(arcn->ln_name));
16903e1094ecSsthen } else if (!strcmp(keyword, "mtime")) {
16913e1094ecSsthen ret = rd_time(&arcn->sb.st_mtim, keyword, p);
16923e1094ecSsthen if (ret < 0)
16933e1094ecSsthen break;
16943e1094ecSsthen } else if (!strcmp(keyword, "atime")) {
16953e1094ecSsthen ret = rd_time(&arcn->sb.st_atim, keyword, p);
16963e1094ecSsthen if (ret < 0)
16973e1094ecSsthen break;
16983e1094ecSsthen } else if (!strcmp(keyword, "ctime")) {
16993e1094ecSsthen ret = rd_time(&arcn->sb.st_ctim, keyword, p);
17003e1094ecSsthen if (ret < 0)
17013e1094ecSsthen break;
1702eafddf6eSjca } else if (!strcmp(keyword, "size")) {
1703eafddf6eSjca ret = rd_size(&arcn->sb.st_size, keyword, p);
1704eafddf6eSjca if (ret < 0)
1705eafddf6eSjca break;
1706734dd9a4Sfgsch }
1707734dd9a4Sfgsch }
17082dbd6dc5Sguenther p = nextp;
1709734dd9a4Sfgsch }
1710734dd9a4Sfgsch
17112dbd6dc5Sguenther if (rd_skip(size + pad) < 0)
1712734dd9a4Sfgsch return (-1);
17132dbd6dc5Sguenther return (ret);
1714734dd9a4Sfgsch }
1715