CS318 - Pintos
Pintos source browser for JHU CS318 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  }
33 PACKED;
34 
35 /** Returns the checksum for the given ustar format HEADER. */
36 static unsigned int
37 calculate_chksum (const struct ustar_header *h)
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. */
65 static 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. */
82 bool
83 ustar_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. */
129 static bool
130 parse_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. */
166 static bool
167 is_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. */
181 const 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 
ustar_header::devminor
char devminor[8]
Device minor number as octal string.
Definition: ustar.c:28
snprintf
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
ustar_header::mtime
char mtime[12]
Modification time in seconds from Jan 1, 1970, as octal string.
Definition: ustar.c:17
uint8_t
unsigned char uint8_t
Definition: stdint.h:20
strchr
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
ustar_header::devmajor
char devmajor[8]
Device major number as octal string.
Definition: ustar.c:27
ustar_header::size
char size[12]
File size in bytes as octal string.
Definition: ustar.c:16
PACKED
struct ustar_header PACKED
ustar_header::uid
char uid[8]
User ID as octal string.
Definition: ustar.c:14
ustar_header::magic
char magic[6]
"ustar\0"
Definition: ustar.c:23
USTAR_REGULAR
Ordinary file.
Definition: ustar.h:15
NULL
#define NULL
Definition: stddef.h:4
INT_MAX
#define INT_MAX
Definition: limits.h:22
string.h
USTAR_DIRECTORY
Directory.
Definition: ustar.h:16
strcmp
int strcmp(const char *a_, const char *b_)
Finds the first differing characters in strings A and B.
Definition: string.c:73
ustar_header::version
char version[2]
"00"
Definition: ustar.c:24
ustar_header::mode
char mode[8]
Permissions as octal string.
Definition: ustar.c:13
ustar_header::name
char name[100]
File name.
Definition: ustar.c:12
ustar_header::chksum
char chksum[8]
Sum of octets in header as octal string.
Definition: ustar.c:19
ustar_type
ustar_type
Support for the standard Posix "ustar" format.
Definition: ustar.h:13
offsetof
#define offsetof(TYPE, MEMBER)
Definition: stddef.h:5
limits.h
ustar_header::gid
char gid[8]
Group ID as octal string.
Definition: ustar.c:15
printf
int printf(const char *format,...)
Writes formatted output to the console.
Definition: stdio.c:79
packed.h
ustar_header::typeflag
char typeflag
An enum ustar_type value.
Definition: ustar.c:20
ULONG_MAX
#define ULONG_MAX
Definition: limits.h:28
memcmp
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
stdio.h
USTAR_HEADER_SIZE
#define USTAR_HEADER_SIZE
Size of a ustar archive header, in bytes.
Definition: ustar.h:21
ASSERT
#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
block
A block device.
Definition: block.c:9
strip_antisocial_prefixes
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_header::padding
char padding[12]
Pad to 512 bytes.
Definition: ustar.c:31
value
A linked list element.
Definition: list.c:22
file_name
static const char file_name[]
tests/filesys/base/syn-read.h
Definition: syn-read.h:5
strlen
size_t strlen(const char *string)
Returns the length of STRING.
Definition: string.c:293
ustar.h
s
static uint8_t s[256]
RC4-based pseudo-random number generator (PRNG).
Definition: random.c:17
ustar_make_header
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
ustar_header::gname
char gname[32]
Group name, always null-terminated.
Definition: ustar.c:26
memset
void * memset(void *dst_, int value, size_t size)
Sets the SIZE bytes in DST to VALUE.
Definition: string.c:279
is_all_zeros
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
ustar_header::linkname
char linkname[100]
Name of link target.
Definition: ustar.c:21
strlcpy
size_t strlcpy(char *dst, const char *src, size_t size)
Copies string SRC to DST.
Definition: string.c:326
ustar_header::uname
char uname[32]
User name, always null-terminated.
Definition: ustar.c:25
ustar_header
Header for ustar-format tar archive.
Definition: ustar.c:10
parse_octal_field
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
ustar_header::prefix
char prefix[155]
Prefix to file name.
Definition: ustar.c:29
USTAR_EOF
End of archive (not an official value).
Definition: ustar.h:17
ustar_parse_header
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
calculate_chksum
static unsigned int calculate_chksum(const struct ustar_header *h)
Returns the checksum for the given ustar format HEADER.
Definition: ustar.c:37