Line data Source code
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 0 : print_tar_number(char *s, int len, uint64 val)
21 : {
22 0 : if (val < (((uint64) 1) << ((len - 1) * 3)))
23 : {
24 : /* Use octal with trailing space */
25 0 : s[--len] = ' ';
26 0 : while (len)
27 : {
28 0 : s[--len] = (val & 7) + '0';
29 0 : val >>= 3;
30 : }
31 : }
32 : else
33 : {
34 : /* Use base-256 with leading \200 */
35 0 : s[0] = '\200';
36 0 : while (len > 1)
37 : {
38 0 : s[--len] = (val & 255);
39 0 : val >>= 8;
40 : }
41 : }
42 0 : }
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 0 : read_tar_number(const char *s, int len)
57 : {
58 0 : uint64 result = 0;
59 :
60 0 : if (*s == '\200')
61 : {
62 : /* base-256 */
63 0 : while (--len)
64 : {
65 0 : result <<= 8;
66 0 : result |= (unsigned char) (*++s);
67 : }
68 : }
69 : else
70 : {
71 : /* octal */
72 0 : while (len-- && *s >= '0' && *s <= '7')
73 : {
74 0 : result <<= 3;
75 0 : result |= (*s - '0');
76 0 : s++;
77 : }
78 : }
79 0 : 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 0 : 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 0 : sum = 8 * ' '; /* presumed value for checksum field */
99 0 : for (i = 0; i < 512; i++)
100 0 : if (i < 148 || i >= 156)
101 0 : sum += 0xFF & header[i];
102 0 : 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 0 : 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 0 : if (strlen(filename) > 99)
116 0 : return TAR_NAME_TOO_LONG;
117 :
118 0 : if (linktarget && strlen(linktarget) > 99)
119 0 : return TAR_SYMLINK_TOO_LONG;
120 :
121 0 : memset(h, 0, 512); /* assume tar header size */
122 :
123 : /* Name 100 */
124 0 : strlcpy(&h[0], filename, 100);
125 0 : 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 0 : int flen = strlen(filename);
133 :
134 0 : flen = Min(flen, 99);
135 0 : h[flen] = '/';
136 0 : h[flen + 1] = '\0';
137 : }
138 :
139 : /* Mode 8 - this doesn't include the file type bits (S_IFMT) */
140 0 : print_tar_number(&h[100], 8, (mode & 07777));
141 :
142 : /* User ID 8 */
143 0 : print_tar_number(&h[108], 8, uid);
144 :
145 : /* Group 8 */
146 0 : print_tar_number(&h[116], 8, gid);
147 :
148 : /* File size 12 */
149 0 : if (linktarget != NULL || S_ISDIR(mode))
150 : /* Symbolic link or directory has size zero */
151 0 : print_tar_number(&h[124], 12, 0);
152 : else
153 0 : print_tar_number(&h[124], 12, size);
154 :
155 : /* Mod Time 12 */
156 0 : print_tar_number(&h[136], 12, mtime);
157 :
158 : /* Checksum 8 cannot be calculated until we've filled all other fields */
159 :
160 0 : if (linktarget != NULL)
161 : {
162 : /* Type - Symbolic link */
163 0 : h[156] = '2';
164 : /* Link Name 100 */
165 0 : strlcpy(&h[157], linktarget, 100);
166 : }
167 0 : else if (S_ISDIR(mode))
168 : {
169 : /* Type - directory */
170 0 : h[156] = '5';
171 : }
172 : else
173 : {
174 : /* Type - regular file */
175 0 : h[156] = '0';
176 : }
177 :
178 : /* Magic 6 */
179 0 : strcpy(&h[257], "ustar");
180 :
181 : /* Version 2 */
182 0 : memcpy(&h[263], "00", 2);
183 :
184 : /* User 32 */
185 : /* XXX: Do we need to care about setting correct username? */
186 0 : strlcpy(&h[265], "postgres", 32);
187 :
188 : /* Group 32 */
189 : /* XXX: Do we need to care about setting correct group name? */
190 0 : strlcpy(&h[297], "postgres", 32);
191 :
192 : /* Major Dev 8 */
193 0 : print_tar_number(&h[329], 8, 0);
194 :
195 : /* Minor Dev 8 */
196 0 : 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 0 : print_tar_number(&h[148], 8, tarChecksum(h));
202 :
203 0 : return TAR_OK;
204 : }
|