| 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 | |