1 #include "c.h"
2 #include "pgtar.h"
3 #include <sys/stat.h>
4 
5 /*
6  * Print a numeric field in a tar header.  The field starts at *s and is of
7  * length len; val is the value to be written.
8  *
9  * Per POSIX, the way to write a number is in octal with leading zeroes and
10  * one trailing space (or NUL, but we use space) at the end of the specified
11  * field width.
12  *
13  * However, the given value may not fit in the available space in octal form.
14  * If that's true, we use the GNU extension of writing \200 followed by the
15  * number in base-256 form (ie, stored in binary MSB-first).  (Note: here we
16  * support only non-negative numbers, so we don't worry about the GNU rules
17  * for handling negative numbers.)
18  */
19 void
print_tar_number(char * s,int len,uint64 val)20 print_tar_number(char *s, int len, uint64 val)
21 {
22 	if (val < (((uint64) 1) << ((len - 1) * 3)))
23 	{
24 		/* Use octal with trailing space */
25 		s[--len] = ' ';
26 		while (len)
27 		{
28 			s[--len] = (val & 7) + '0';
29 			val >>= 3;
30 		}
31 	}
32 	else
33 	{
34 		/* Use base-256 with leading \200 */
35 		s[0] = '\200';
36 		while (len > 1)
37 		{
38 			s[--len] = (val & 255);
39 			val >>= 8;
40 		}
41 	}
42 }
43 
44 
45 /*
46  * Read a numeric field in a tar header.  The field starts at *s and is of
47  * length len.
48  *
49  * The POSIX-approved format for a number is octal, ending with a space or
50  * NUL.  However, for values that don't fit, we recognize the GNU extension
51  * of \200 followed by the number in base-256 form (ie, stored in binary
52  * MSB-first).  (Note: here we support only non-negative numbers, so we don't
53  * worry about the GNU rules for handling negative numbers.)
54  */
55 uint64
read_tar_number(const char * s,int len)56 read_tar_number(const char *s, int len)
57 {
58 	uint64		result = 0;
59 
60 	if (*s == '\200')
61 	{
62 		/* base-256 */
63 		while (--len)
64 		{
65 			result <<= 8;
66 			result |= (unsigned char) (*++s);
67 		}
68 	}
69 	else
70 	{
71 		/* octal */
72 		while (len-- && *s >= '0' && *s <= '7')
73 		{
74 			result <<= 3;
75 			result |= (*s - '0');
76 			s++;
77 		}
78 	}
79 	return result;
80 }
81 
82 
83 /*
84  * Calculate the tar checksum for a header. The header is assumed to always
85  * be 512 bytes, per the tar standard.
86  */
87 int
tarChecksum(char * header)88 tarChecksum(char *header)
89 {
90 	int			i,
91 				sum;
92 
93 	/*
94 	 * Per POSIX, the checksum is the simple sum of all bytes in the header,
95 	 * treating the bytes as unsigned, and treating the checksum field (at
96 	 * offset 148) as though it contained 8 spaces.
97 	 */
98 	sum = 8 * ' ';				/* presumed value for checksum field */
99 	for (i = 0; i < 512; i++)
100 		if (i < 148 || i >= 156)
101 			sum += 0xFF & header[i];
102 	return sum;
103 }
104 
105 
106 /*
107  * Fill in the buffer pointed to by h with a tar format header. This buffer
108  * must always have space for 512 characters, which is a requirement of
109  * the tar format.
110  */
111 enum tarError
tarCreateHeader(char * h,const char * filename,const char * linktarget,pgoff_t size,mode_t mode,uid_t uid,gid_t gid,time_t mtime)112 tarCreateHeader(char *h, const char *filename, const char *linktarget,
113 				pgoff_t size, mode_t mode, uid_t uid, gid_t gid, time_t mtime)
114 {
115 	if (strlen(filename) > 99)
116 		return TAR_NAME_TOO_LONG;
117 
118 	if (linktarget && strlen(linktarget) > 99)
119 		return TAR_SYMLINK_TOO_LONG;
120 
121 	memset(h, 0, 512);			/* assume tar header size */
122 
123 	/* Name 100 */
124 	strlcpy(&h[0], filename, 100);
125 	if (linktarget != NULL || S_ISDIR(mode))
126 	{
127 		/*
128 		 * We only support symbolic links to directories, and this is
129 		 * indicated in the tar format by adding a slash at the end of the
130 		 * name, the same as for regular directories.
131 		 */
132 		int			flen = strlen(filename);
133 
134 		flen = Min(flen, 99);
135 		h[flen] = '/';
136 		h[flen + 1] = '\0';
137 	}
138 
139 	/* Mode 8 - this doesn't include the file type bits (S_IFMT)  */
140 	print_tar_number(&h[100], 8, (mode & 07777));
141 
142 	/* User ID 8 */
143 	print_tar_number(&h[108], 8, uid);
144 
145 	/* Group 8 */
146 	print_tar_number(&h[116], 8, gid);
147 
148 	/* File size 12 */
149 	if (linktarget != NULL || S_ISDIR(mode))
150 		/* Symbolic link or directory has size zero */
151 		print_tar_number(&h[124], 12, 0);
152 	else
153 		print_tar_number(&h[124], 12, size);
154 
155 	/* Mod Time 12 */
156 	print_tar_number(&h[136], 12, mtime);
157 
158 	/* Checksum 8 cannot be calculated until we've filled all other fields */
159 
160 	if (linktarget != NULL)
161 	{
162 		/* Type - Symbolic link */
163 		h[156] = '2';
164 		/* Link Name 100 */
165 		strlcpy(&h[157], linktarget, 100);
166 	}
167 	else if (S_ISDIR(mode))
168 	{
169 		/* Type - directory */
170 		h[156] = '5';
171 	}
172 	else
173 	{
174 		/* Type - regular file */
175 		h[156] = '0';
176 	}
177 
178 	/* Magic 6 */
179 	strcpy(&h[257], "ustar");
180 
181 	/* Version 2 */
182 	memcpy(&h[263], "00", 2);
183 
184 	/* User 32 */
185 	/* XXX: Do we need to care about setting correct username? */
186 	strlcpy(&h[265], "postgres", 32);
187 
188 	/* Group 32 */
189 	/* XXX: Do we need to care about setting correct group name? */
190 	strlcpy(&h[297], "postgres", 32);
191 
192 	/* Major Dev 8 */
193 	print_tar_number(&h[329], 8, 0);
194 
195 	/* Minor Dev 8 */
196 	print_tar_number(&h[337], 8, 0);
197 
198 	/* Prefix 155 - not used, leave as nulls */
199 
200 	/* Finally, compute and insert the checksum */
201 	print_tar_number(&h[148], 8, tarChecksum(h));
202 
203 	return TAR_OK;
204 }
205