PKUOS - Pintos
Pintos source browser for PKU Operating System course
ustar.c
Go to the documentation of this file.
1#include <ustar.h>
2#include <limits.h>
3#include <packed.h>
4#include <stdio.h>
5#include <string.h>
6
7/** Header for ustar-format tar archive. See the documentation of
8 the "pax" utility in [SUSv3] for the the "ustar" format
9 specification. */
11 {
12 char name[100]; /**< File name. Null-terminated if room. */
13 char mode[8]; /**< Permissions as octal string. */
14 char uid[8]; /**< User ID as octal string. */
15 char gid[8]; /**< Group ID as octal string. */
16 char size[12]; /**< File size in bytes as octal string. */
17 char mtime[12]; /**< Modification time in seconds
18 from Jan 1, 1970, as octal string. */
19 char chksum[8]; /**< Sum of octets in header as octal string. */
20 char typeflag; /**< An enum ustar_type value. */
21 char linkname[100]; /**< Name of link target.
22 Null-terminated if room. */
23 char magic[6]; /**< "ustar\0" */
24 char version[2]; /**< "00" */
25 char uname[32]; /**< User name, always null-terminated. */
26 char gname[32]; /**< Group name, always null-terminated. */
27 char devmajor[8]; /**< Device major number as octal string. */
28 char devminor[8]; /**< Device minor number as octal string. */
29 char prefix[155]; /**< Prefix to file name.
30 Null-terminated if room. */
31 char padding[12]; /**< Pad to 512 bytes. */
32 }
34
35/** Returns the checksum for the given ustar format HEADER. */
36static unsigned int
38{
39 const uint8_t *header = (const uint8_t *) h;
40 unsigned int chksum;
41 size_t i;
42
43 chksum = 0;
44 for (i = 0; i < USTAR_HEADER_SIZE; i++)
45 {
46 /* The ustar checksum is calculated as if the chksum field
47 were all spaces. */
48 const size_t chksum_start = offsetof (struct ustar_header, chksum);
49 const size_t chksum_end = chksum_start + sizeof h->chksum;
50 bool in_chksum_field = i >= chksum_start && i < chksum_end;
51 chksum += in_chksum_field ? ' ' : header[i];
52 }
53 return chksum;
54}
55
56/** Drop possibly dangerous prefixes from FILE_NAME and return the
57 stripped name. An archive with file names that start with "/"
58 or "../" could cause a naive tar extractor to write to
59 arbitrary parts of the file system, not just the destination
60 directory. We don't want to create such archives or be such a
61 naive extractor.
62
63 The return value can be a suffix of FILE_NAME or a string
64 literal. */
65static const char *
67{
68 while (*file_name == '/'
69 || !memcmp (file_name, "./", 2)
70 || !memcmp (file_name, "../", 3))
71 file_name = strchr (file_name, '/') + 1;
72 return *file_name == '\0' || !strcmp (file_name, "..") ? "." : file_name;
73}
74
75/** Composes HEADER as a USTAR_HEADER_SIZE (512)-byte archive
76 header in ustar format for a SIZE-byte file named FILE_NAME of
77 the given TYPE. The caller is responsible for writing the
78 header to a file or device.
79
80 If successful, returns true. On failure (due to an
81 excessively long file name), returns false. */
82bool
83ustar_make_header (const char *file_name, enum ustar_type type,
84 int size, char header[USTAR_HEADER_SIZE])
85{
86 struct ustar_header *h = (struct ustar_header *) header;
87
88 ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE);
89 ASSERT (type == USTAR_REGULAR || type == USTAR_DIRECTORY);
90
91 /* Check file name. */
93 if (strlen (file_name) > 99)
94 {
95 printf ("%s: file name too long\n", file_name);
96 return false;
97 }
98
99 /* Fill in header except for final checksum. */
100 memset (h, 0, sizeof *h);
101 strlcpy (h->name, file_name, sizeof h->name);
102 snprintf (h->mode, sizeof h->mode, "%07o",
103 type == USTAR_REGULAR ? 0644 : 0755);
104 strlcpy (h->uid, "0000000", sizeof h->uid);
105 strlcpy (h->gid, "0000000", sizeof h->gid);
106 snprintf (h->size, sizeof h->size, "%011o", size);
107 snprintf (h->mtime, sizeof h->size, "%011o", 1136102400);
108 h->typeflag = type;
109 strlcpy (h->magic, "ustar", sizeof h->magic);
110 h->version[0] = h->version[1] = '0';
111 strlcpy (h->gname, "root", sizeof h->gname);
112 strlcpy (h->uname, "root", sizeof h->uname);
113
114 /* Compute and fill in final checksum. */
115 snprintf (h->chksum, sizeof h->chksum, "%07o", calculate_chksum (h));
116
117 return true;
118}
119
120/** Parses a SIZE-byte octal field in S in the format used by
121 ustar format. If successful, stores the field's value in
122 *VALUE and returns true; on failure, returns false.
123
124 ustar octal fields consist of a sequence of octal digits
125 terminated by a space or a null byte. The ustar specification
126 seems ambiguous as to whether these fields must be padded on
127 the left with '0's, so we accept any field that fits in the
128 available space, regardless of whether it fills the space. */
129static bool
130parse_octal_field (const char *s, size_t size, unsigned long int *value)
131{
132 size_t ofs;
133
134 *value = 0;
135 for (ofs = 0; ofs < size; ofs++)
136 {
137 char c = s[ofs];
138 if (c >= '0' && c <= '7')
139 {
140 if (*value > ULONG_MAX / 8)
141 {
142 /* Overflow. */
143 return false;
144 }
145 *value = c - '0' + *value * 8;
146 }
147 else if (c == ' ' || c == '\0')
148 {
149 /* End of field, but disallow completely empty
150 fields. */
151 return ofs > 0;
152 }
153 else
154 {
155 /* Bad character. */
156 return false;
157 }
158 }
159
160 /* Field did not end in space or null byte. */
161 return false;
162}
163
164/** Returns true if the CNT bytes starting at BLOCK are all zero,
165 false otherwise. */
166static bool
167is_all_zeros (const char *block, size_t cnt)
168{
169 while (cnt-- > 0)
170 if (*block++ != 0)
171 return false;
172 return true;
173}
174
175/** Parses HEADER as a ustar-format archive header for a regular
176 file or directory. If successful, stores the archived file's
177 name in *FILE_NAME (as a pointer into HEADER or a string
178 literal), its type in *TYPE, and its size in bytes in *SIZE,
179 and returns a null pointer. On failure, returns a
180 human-readable error message. */
181const char *
183 const char **file_name, enum ustar_type *type, int *size)
184{
185 const struct ustar_header *h = (const struct ustar_header *) header;
186 unsigned long int chksum, size_ul;
187
188 ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE);
189
190 /* Detect end of archive. */
191 if (is_all_zeros (header, USTAR_HEADER_SIZE))
192 {
193 *file_name = NULL;
194 *type = USTAR_EOF;
195 *size = 0;
196 return NULL;
197 }
198
199 /* Validate ustar header. */
200 if (memcmp (h->magic, "ustar", 6))
201 return "not a ustar archive";
202 else if (h->version[0] != '0' || h->version[1] != '0')
203 return "invalid ustar version";
204 else if (!parse_octal_field (h->chksum, sizeof h->chksum, &chksum))
205 return "corrupt chksum field";
206 else if (chksum != calculate_chksum (h))
207 return "checksum mismatch";
208 else if (h->name[sizeof h->name - 1] != '\0' || h->prefix[0] != '\0')
209 return "file name too long";
210 else if (h->typeflag != USTAR_REGULAR && h->typeflag != USTAR_DIRECTORY)
211 return "unimplemented file type";
212 if (h->typeflag == USTAR_REGULAR)
213 {
214 if (!parse_octal_field (h->size, sizeof h->size, &size_ul))
215 return "corrupt file size field";
216 else if (size_ul > INT_MAX)
217 return "file too large";
218 }
219 else
220 size_ul = 0;
221
222 /* Success. */
224 *type = h->typeflag;
225 *size = size_ul;
226 return NULL;
227}
228
#define ASSERT(CONDITION)
This is outside the header guard so that debug.h may be included multiple times with different settin...
Definition: debug.h:31
int snprintf(char *buffer, size_t buf_size, const char *format,...)
Like printf(), except that output is stored into BUFFER, which must have space for BUF_SIZE character...
Definition: stdio.c:62
int printf(const char *format,...)
Writes formatted output to the console.
Definition: stdio.c:79
#define ULONG_MAX
Definition: limits.h:28
#define INT_MAX
Definition: limits.h:22
static uint8_t s[256]
RC4-based pseudo-random number generator (PRNG).
Definition: random.c:17
#define NULL
Definition: stddef.h:4
#define offsetof(TYPE, MEMBER)
Definition: stddef.h:5
unsigned char uint8_t
Definition: stdint.h:20
char * strchr(const char *string, int c_)
Finds and returns the first occurrence of C in STRING, or a null pointer if C does not appear in STRI...
Definition: string.c:113
int memcmp(const void *a_, const void *b_, size_t size)
Find the first differing byte in the two blocks of SIZE bytes at A and B.
Definition: string.c:53
size_t strlen(const char *string)
Returns the length of STRING.
Definition: string.c:293
void * memset(void *dst_, int value, size_t size)
Sets the SIZE bytes in DST to VALUE.
Definition: string.c:279
size_t strlcpy(char *dst, const char *src, size_t size)
Copies string SRC to DST.
Definition: string.c:326
int strcmp(const char *a_, const char *b_)
Finds the first differing characters in strings A and B.
Definition: string.c:73
A block device.
Definition: block.c:10
Header for ustar-format tar archive.
Definition: ustar.c:11
char gname[32]
Group name, always null-terminated.
Definition: ustar.c:26
char prefix[155]
Prefix to file name.
Definition: ustar.c:29
char uid[8]
User ID as octal string.
Definition: ustar.c:14
char mtime[12]
Modification time in seconds from Jan 1, 1970, as octal string.
Definition: ustar.c:17
char uname[32]
User name, always null-terminated.
Definition: ustar.c:25
char size[12]
File size in bytes as octal string.
Definition: ustar.c:16
char magic[6]
"ustar\0"
Definition: ustar.c:23
char name[100]
File name.
Definition: ustar.c:12
char devminor[8]
Device minor number as octal string.
Definition: ustar.c:28
char version[2]
"00"
Definition: ustar.c:24
char devmajor[8]
Device major number as octal string.
Definition: ustar.c:27
char padding[12]
Pad to 512 bytes.
Definition: ustar.c:31
char chksum[8]
Sum of octets in header as octal string.
Definition: ustar.c:19
char gid[8]
Group ID as octal string.
Definition: ustar.c:15
char mode[8]
Permissions as octal string.
Definition: ustar.c:13
char typeflag
An enum ustar_type value.
Definition: ustar.c:20
char linkname[100]
Name of link target.
Definition: ustar.c:21
A linked list element.
Definition: list.c:23
static const char file_name[]
tests/filesys/base/syn-read.h
Definition: syn-read.h:5
static unsigned int calculate_chksum(const struct ustar_header *h)
Returns the checksum for the given ustar format HEADER.
Definition: ustar.c:37
static bool parse_octal_field(const char *s, size_t size, unsigned long int *value)
Parses a SIZE-byte octal field in S in the format used by ustar format.
Definition: ustar.c:130
bool ustar_make_header(const char *file_name, enum ustar_type type, int size, char header[USTAR_HEADER_SIZE])
Composes HEADER as a USTAR_HEADER_SIZE (512)-byte archive header in ustar format for a SIZE-byte file...
Definition: ustar.c:83
static bool is_all_zeros(const char *block, size_t cnt)
Returns true if the CNT bytes starting at BLOCK are all zero, false otherwise.
Definition: ustar.c:167
const char * ustar_parse_header(const char header[USTAR_HEADER_SIZE], const char **file_name, enum ustar_type *type, int *size)
Parses HEADER as a ustar-format archive header for a regular file or directory.
Definition: ustar.c:182
struct ustar_header PACKED
static const char * strip_antisocial_prefixes(const char *file_name)
Drop possibly dangerous prefixes from FILE_NAME and return the stripped name.
Definition: ustar.c:66
ustar_type
Support for the standard Posix "ustar" format.
Definition: ustar.h:14
@ USTAR_REGULAR
Ordinary file.
Definition: ustar.h:15
@ USTAR_EOF
End of archive (not an official value).
Definition: ustar.h:17
@ USTAR_DIRECTORY
Directory.
Definition: ustar.h:16
#define USTAR_HEADER_SIZE
Size of a ustar archive header, in bytes.
Definition: ustar.h:21