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 |
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 |
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 |
88 | tarChecksum(char *) |
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 |
112 | (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 | |