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