00001 #include <ustar.h>
00002
00003 #include <limits.h>
00004 #include <packed.h>
00005 #include <stdio.h>
00006 #include <string.h>
00007
00008
00009
00010
00011 struct ustar_header
00012 {
00013 char name[100];
00014 char mode[8];
00015 char uid[8];
00016 char gid[8];
00017 char size[12];
00018 char mtime[12];
00019
00020 char chksum[8];
00021 char typeflag;
00022 char linkname[100];
00023
00024 char magic[6];
00025 char version[2];
00026 char uname[32];
00027 char gname[32];
00028 char devmajor[8];
00029 char devminor[8];
00030 char prefix[155];
00031
00032 char padding[12];
00033 }
00034 PACKED;
00035
00036
00037 static unsigned int
00038 calculate_chksum (const struct ustar_header *h)
00039 {
00040 const uint8_t *header = (const uint8_t *) h;
00041 unsigned int chksum;
00042 size_t i;
00043
00044 chksum = 0;
00045 for (i = 0; i < USTAR_HEADER_SIZE; i++)
00046 {
00047
00048
00049 const size_t chksum_start = offsetof (struct ustar_header, chksum);
00050 const size_t chksum_end = chksum_start + sizeof h->chksum;
00051 bool in_chksum_field = i >= chksum_start && i < chksum_end;
00052 chksum += in_chksum_field ? ' ' : header[i];
00053 }
00054 return chksum;
00055 }
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066 static const char *
00067 strip_antisocial_prefixes (const char *file_name)
00068 {
00069 while (*file_name == '/'
00070 || !memcmp (file_name, "./", 2)
00071 || !memcmp (file_name, "../", 3))
00072 file_name = strchr (file_name, '/') + 1;
00073 return *file_name == '\0' || !strcmp (file_name, "..") ? "." : file_name;
00074 }
00075
00076
00077
00078
00079
00080
00081
00082
00083 bool
00084 ustar_make_header (const char *file_name, enum ustar_type type,
00085 int size, char header[USTAR_HEADER_SIZE])
00086 {
00087 struct ustar_header *h = (struct ustar_header *) header;
00088
00089 ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE);
00090 ASSERT (type == USTAR_REGULAR || type == USTAR_DIRECTORY);
00091
00092
00093 file_name = strip_antisocial_prefixes (file_name);
00094 if (strlen (file_name) > 99)
00095 {
00096 printf ("%s: file name too long\n", file_name);
00097 return false;
00098 }
00099
00100
00101 memset (h, 0, sizeof *h);
00102 strlcpy (h->name, file_name, sizeof h->name);
00103 snprintf (h->mode, sizeof h->mode, "%07o",
00104 type == USTAR_REGULAR ? 0644 : 0755);
00105 strlcpy (h->uid, "0000000", sizeof h->uid);
00106 strlcpy (h->gid, "0000000", sizeof h->gid);
00107 snprintf (h->size, sizeof h->size, "%011o", size);
00108 snprintf (h->mtime, sizeof h->size, "%011o", 1136102400);
00109 h->typeflag = type;
00110 strlcpy (h->magic, "ustar", sizeof h->magic);
00111 h->version[0] = h->version[1] = '0';
00112 strlcpy (h->gname, "root", sizeof h->gname);
00113 strlcpy (h->uname, "root", sizeof h->uname);
00114
00115
00116 snprintf (h->chksum, sizeof h->chksum, "%07o", calculate_chksum (h));
00117
00118 return true;
00119 }
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130 static bool
00131 parse_octal_field (const char *s, size_t size, unsigned long int *value)
00132 {
00133 size_t ofs;
00134
00135 *value = 0;
00136 for (ofs = 0; ofs < size; ofs++)
00137 {
00138 char c = s[ofs];
00139 if (c >= '0' && c <= '7')
00140 {
00141 if (*value > ULONG_MAX / 8)
00142 {
00143
00144 return false;
00145 }
00146 *value = c - '0' + *value * 8;
00147 }
00148 else if (c == ' ' || c == '\0')
00149 {
00150
00151
00152 return ofs > 0;
00153 }
00154 else
00155 {
00156
00157 return false;
00158 }
00159 }
00160
00161
00162 return false;
00163 }
00164
00165
00166
00167 static bool
00168 is_all_zeros (const char *block, size_t cnt)
00169 {
00170 while (cnt-- > 0)
00171 if (*block++ != 0)
00172 return false;
00173 return true;
00174 }
00175
00176
00177
00178
00179
00180
00181
00182 const char *
00183 ustar_parse_header (const char header[USTAR_HEADER_SIZE],
00184 const char **file_name, enum ustar_type *type, int *size)
00185 {
00186 const struct ustar_header *h = (const struct ustar_header *) header;
00187 unsigned long int chksum, size_ul;
00188
00189 ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE);
00190
00191
00192 if (is_all_zeros (header, USTAR_HEADER_SIZE))
00193 {
00194 *file_name = NULL;
00195 *type = USTAR_EOF;
00196 *size = 0;
00197 return NULL;
00198 }
00199
00200
00201 if (memcmp (h->magic, "ustar", 6))
00202 return "not a ustar archive";
00203 else if (h->version[0] != '0' || h->version[1] != '0')
00204 return "invalid ustar version";
00205 else if (!parse_octal_field (h->chksum, sizeof h->chksum, &chksum))
00206 return "corrupt chksum field";
00207 else if (chksum != calculate_chksum (h))
00208 return "checksum mismatch";
00209 else if (h->name[sizeof h->name - 1] != '\0' || h->prefix[0] != '\0')
00210 return "file name too long";
00211 else if (h->typeflag != USTAR_REGULAR && h->typeflag != USTAR_DIRECTORY)
00212 return "unimplemented file type";
00213 if (h->typeflag == USTAR_REGULAR)
00214 {
00215 if (!parse_octal_field (h->size, sizeof h->size, &size_ul))
00216 return "corrupt file size field";
00217 else if (size_ul > INT_MAX)
00218 return "file too large";
00219 }
00220 else
00221 size_ul = 0;
00222
00223
00224 *file_name = strip_antisocial_prefixes (h->name);
00225 *type = h->typeflag;
00226 *size = size_ul;
00227 return NULL;
00228 }
00229