6f70d973e0b022be4b6128a0d96d9e687c09fbbc
[pintos-anon] / src / tests / filesys / extended / tar.c
1 /* tar.c
2
3    Creates a tar archive. */
4
5 #include <syscall.h>
6 #include <stdio.h>
7 #include <string.h>
8
9 static void usage (void);
10 static bool make_tar_archive (const char *archive_name,
11                               char *files[], size_t file_cnt);
12
13 int
14 main (int argc, char *argv[]) 
15 {
16   if (argc < 3)
17     usage ();
18
19   return (make_tar_archive (argv[1], argv + 2, argc - 2)
20           ? EXIT_SUCCESS : EXIT_FAILURE);
21 }
22
23 static void
24 usage (void) 
25 {
26   printf ("tar, tar archive creator\n"
27           "Usage: tar ARCHIVE FILE...\n"
28           "where ARCHIVE is the tar archive to create\n"
29           "  and FILE... is a list of files or directories to put into it.\n"
30           "(ARCHIVE itself will not be included in the archive, even if it\n"
31           "is in a directory to be archived.)\n");
32   exit (EXIT_FAILURE);
33 }
34 \f
35 static bool archive_file (char file_name[], size_t file_name_size,
36                           int archive_fd, bool *write_error);
37
38 static bool archive_ordinary_file (const char *file_name, int file_fd,
39                                    int archive_fd, bool *write_error);
40 static bool archive_directory (char file_name[], size_t file_name_size,
41                                int file_fd, int archive_fd, bool *write_error);
42 static bool write_header (const char *file_name,
43                           char type_flag, int size, unsigned mode,
44                           int archive_fd, bool *write_error);
45
46 static bool do_write (int fd, const char *buffer, int size, bool *write_error);
47
48 static bool
49 make_tar_archive (const char *archive_name, char *files[], size_t file_cnt) 
50 {
51   static const char zeros[512];
52   int archive_fd;
53   bool success = true;
54   bool write_error = false;
55   size_t i;
56   
57   if (!create (archive_name, 0)) 
58     {
59       printf ("%s: create failed\n", archive_name);
60       return false;
61     }
62   archive_fd = open (archive_name);
63   if (archive_fd < 0)
64     {
65       printf ("%s: open failed\n", archive_name);
66       return false;
67     }
68
69   for (i = 0; i < file_cnt; i++) 
70     {
71       char file_name[128];
72       
73       strlcpy (file_name, files[i], sizeof file_name);
74       if (!archive_file (file_name, sizeof file_name,
75                          archive_fd, &write_error))
76         success = false;
77     }
78
79   if (!do_write (archive_fd, zeros, 512, &write_error)
80       || !do_write (archive_fd, zeros, 512, &write_error)) 
81     success = false;
82
83   close (archive_fd);
84
85   return success;
86 }
87
88 static bool
89 archive_file (char file_name[], size_t file_name_size,
90               int archive_fd, bool *write_error) 
91 {
92   int file_fd = open (file_name);
93   if (file_fd >= 0) 
94     {
95       bool success;
96
97       if (inumber (file_fd) != inumber (archive_fd)) 
98         {
99           if (!isdir (file_fd))
100             success = archive_ordinary_file (file_name, file_fd,
101                                              archive_fd, write_error);
102           else
103             success = archive_directory (file_name, file_name_size, file_fd,
104                                          archive_fd, write_error);      
105         }
106       else
107         {
108           /* Nothing to do: don't try to archive the archive file. */
109           success = true;
110         }
111   
112       close (file_fd);
113
114       return success;
115     }
116   else
117     {
118       printf ("%s: open failed\n", file_name);
119       return false;
120     }
121 }
122
123 static bool
124 archive_ordinary_file (const char *file_name, int file_fd,
125                        int archive_fd, bool *write_error)
126 {
127   bool read_error = false;
128   bool success = true;
129   int file_size = filesize (file_fd);
130
131   if (!write_header (file_name, '0', file_size, 0644, archive_fd, write_error))
132     return false;
133
134   while (file_size > 0) 
135     {
136       static char buf[512];
137       int chunk_size = file_size > 512 ? 512 : file_size;
138       int read_retval = read (file_fd, buf, chunk_size);
139       int bytes_read = read_retval > 0 ? read_retval : 0;
140
141       if (bytes_read != chunk_size && !read_error) 
142         {
143           printf ("%s: read error\n", file_name);
144           read_error = true;
145           success = false;
146         }
147
148       memset (buf + bytes_read, 0, 512 - bytes_read);
149       if (!do_write (archive_fd, buf, 512, write_error))
150         success = false;
151
152       file_size -= chunk_size;
153     }
154
155   return success;
156 }
157
158 static bool
159 archive_directory (char file_name[], size_t file_name_size, int file_fd,
160                    int archive_fd, bool *write_error)
161 {
162   size_t dir_len;
163   bool success = true;
164
165   dir_len = strlen (file_name);
166   if (dir_len + 1 + READDIR_MAX_LEN + 1 > file_name_size) 
167     {
168       printf ("%s: file name too long\n", file_name);
169       return false;
170     }
171
172   if (!write_header (file_name, '5', 0, 0755, archive_fd, write_error))
173     return false;
174       
175   file_name[dir_len] = '/';
176   while (readdir (file_fd, &file_name[dir_len + 1])) 
177     if (!archive_file (file_name, file_name_size, archive_fd, write_error))
178       success = false;
179   file_name[dir_len] = '\0';
180
181   return success;
182 }
183
184 static bool
185 write_header (const char *file_name,
186               char type_flag, int size, unsigned mode,
187               int archive_fd, bool *write_error) 
188 {
189   static char header[512];
190   unsigned chksum;
191   size_t i;
192
193   memset (header, 0, sizeof header);
194
195   /* Drop confusing and possibly dangerous prefixes from
196      FILE_NAME. */
197   while (*file_name == '/'
198          || !memcmp (file_name, "./", 2)
199          || !memcmp (file_name, "../", 3))
200     file_name = strchr (file_name, '/') + 1;
201   if (*file_name == '\0') 
202     {
203       /* Dropped *everything* from FILE_NAME.
204          Should only be possible for a directory. */
205       ASSERT (type_flag == '5');
206       return true; 
207     }
208   else if (strlen (file_name) > 99)
209     {
210       printf ("%s: file name too long\n", file_name);
211       return false;
212     }
213
214   /* Fill in header except for final checksum. */
215   strlcpy (header, file_name, 100);                 /* name */
216   snprintf (header + 100, 8, "%07o", mode);         /* mode */
217   strlcpy (header + 108, "0000000", 8);             /* uid */
218   strlcpy (header + 116, "0000000", 8);             /* gid */
219   snprintf (header + 124, 12, "%011o", size);       /* size */
220   snprintf (header + 136, 12, "%011o", 1136102400); /* mtime (2006-01-01) */
221   memset (header + 148, ' ', 8);                    /* chksum */
222   header[156] = type_flag;                          /* typeflag */
223   strlcpy (header + 257, "ustar", 6);               /* magic */
224   strlcpy (header + 263, "00", 3);                  /* version */
225
226   /* Compute and fill in final checksum. */
227   chksum = 0;
228   for (i = 0; i < 512; i++)
229     chksum += (uint8_t) header[i];
230   snprintf (header + 148, 8, "%07o", chksum);
231
232   /* Write header. */
233   return do_write (archive_fd, header, 512, write_error);
234 }
235
236 static bool
237 do_write (int fd, const char *buffer, int size, bool *write_error) 
238 {
239   if (write (fd, buffer, size) == size) 
240     return true;
241   else
242     {
243       if (!*write_error) 
244         {
245           printf ("error writing archive\n");
246           *write_error = true; 
247         }
248       return false; 
249     }
250 }