tar.c (6942B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <grp.h> 3 #include <limits.h> 4 #include <pwd.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <sys/stat.h> 9 #include <sys/time.h> 10 #include <sys/types.h> 11 #include <unistd.h> 12 13 #include "util.h" 14 15 typedef struct Header Header; 16 struct Header { 17 char name[100]; 18 char mode[8]; 19 char uid[8]; 20 char gid[8]; 21 char size[12]; 22 char mtime[12]; 23 char chksum[8]; 24 char type; 25 char link[100]; 26 char magic[6]; 27 char version[2]; 28 char uname[32]; 29 char gname[32]; 30 char major[8]; 31 char minor[8]; 32 char prefix[155]; 33 }; 34 35 enum { 36 Blksiz = 512 37 }; 38 39 enum Type { 40 REG = '0', AREG = '\0', HARDLINK = '1', SYMLINK = '2', CHARDEV = '3', 41 BLOCKDEV = '4', DIRECTORY = '5', FIFO = '6' 42 }; 43 44 static void putoctal(char *, unsigned, int); 45 static int archive(const char *); 46 static int unarchive(char *, int, char[Blksiz]); 47 static int print(char *, int , char[Blksiz]); 48 static void c(const char *); 49 static void xt(int (*)(char*, int, char[Blksiz])); 50 51 static FILE *tarfile; 52 static ino_t tarinode; 53 static dev_t tardev; 54 55 static int mflag = 0; 56 57 static void 58 usage(void) 59 { 60 eprintf("usage: tar [-f tarfile] [-C dir] [-]x[m]|t\n" 61 " tar [-f tarfile] [-C dir] [-]c dir\n" 62 " tar [-C dir] cf tarfile dir\n" 63 " tar [-C dir] x[m]|tf tarfile\n"); 64 } 65 66 int 67 main(int argc, char *argv[]) 68 { 69 struct stat st; 70 char *file = NULL, *dir = ".", *ap; 71 char mode = '\0'; 72 73 ARGBEGIN { 74 case 'x': 75 case 'c': 76 case 't': 77 if (mode) 78 usage(); 79 mode = ARGC(); 80 break; 81 case 'C': 82 dir = EARGF(usage()); 83 break; 84 case 'f': 85 file = EARGF(usage()); 86 break; 87 case 'm': 88 mflag = 1; 89 break; 90 default: 91 usage(); 92 } ARGEND; 93 94 if (!mode) { 95 if (argc < 1) 96 usage(); 97 98 for (ap = argv[0]; *ap; ap++) { 99 switch (*ap) { 100 case 'x': 101 case 'c': 102 case 't': 103 if (mode) 104 usage(); 105 mode = *ap; 106 break; 107 case 'f': 108 if (argc < 2) 109 usage(); 110 argc--, argv++; 111 file = argv[0]; 112 break; 113 case 'C': 114 if (argc < 2) 115 usage(); 116 argc--, argv++; 117 dir = argv[0]; 118 break; 119 case 'm': 120 mflag = 1; 121 break; 122 default: 123 usage(); 124 } 125 } 126 argc--, argv++; 127 } 128 129 if (!mode || argc != (mode == 'c')) 130 usage(); 131 132 if (file) { 133 tarfile = fopen(file, (mode == 'c') ? "wb" : "rb"); 134 if (!tarfile) 135 eprintf("tar: open '%s':", file); 136 if (lstat(file, &st) < 0) 137 eprintf("tar: stat '%s':", file); 138 tarinode = st.st_ino; 139 tardev = st.st_dev; 140 } else { 141 tarfile = (mode == 'c') ? stdout : stdin; 142 } 143 144 chdir(dir); 145 146 if (mode == 'c') { 147 c(argv[0]); 148 } else { 149 xt((mode == 'x') ? unarchive : print); 150 } 151 152 return 0; 153 } 154 155 static void 156 putoctal(char *dst, unsigned num, int n) 157 { 158 snprintf(dst, n, "%.*o", n-1, num); 159 } 160 161 static int 162 archive(const char* path) 163 { 164 unsigned char b[Blksiz]; 165 unsigned chksum; 166 int l, x; 167 Header *h = (void*)b; 168 FILE *f = NULL; 169 struct stat st; 170 struct passwd *pw; 171 struct group *gr; 172 mode_t mode; 173 174 lstat(path, &st); 175 if (st.st_ino == tarinode && st.st_dev == tardev) { 176 fprintf(stderr, "ignoring '%s'\n", path); 177 return 0; 178 } 179 pw = getpwuid(st.st_uid); 180 gr = getgrgid(st.st_gid); 181 182 memset(b, 0, sizeof b); 183 snprintf(h->name, sizeof h->name, "%s", path); 184 putoctal(h->mode, (unsigned)st.st_mode&0777, sizeof h->mode); 185 putoctal(h->uid, (unsigned)st.st_uid, sizeof h->uid); 186 putoctal(h->gid, (unsigned)st.st_gid, sizeof h->gid); 187 putoctal(h->size, 0, sizeof h->size); 188 putoctal(h->mtime, (unsigned)st.st_mtime, sizeof h->mtime); 189 memcpy(h->magic, "ustar", sizeof h->magic); 190 memcpy(h->version, "00", sizeof h->version); 191 snprintf(h->uname, sizeof h->uname, "%s", pw ? pw->pw_name : ""); 192 snprintf(h->gname, sizeof h->gname, "%s", gr ? gr->gr_name : ""); 193 194 mode = st.st_mode; 195 if (S_ISREG(mode)) { 196 h->type = REG; 197 putoctal(h->size, (unsigned)st.st_size, sizeof h->size); 198 f = fopen(path, "r"); 199 } else if (S_ISDIR(mode)) { 200 h->type = DIRECTORY; 201 } else if (S_ISLNK(mode)) { 202 h->type = SYMLINK; 203 readlink(path, h->link, (sizeof h->link)-1); 204 } else if (S_ISCHR(mode) || S_ISBLK(mode)) { 205 h->type = S_ISCHR(mode) ? CHARDEV : BLOCKDEV; 206 #if defined(major) && defined(minor) 207 putoctal(h->major, (unsigned)major(st.st_dev), sizeof h->major); 208 putoctal(h->minor, (unsigned)minor(st.st_dev), sizeof h->minor); 209 #else 210 return 0; 211 #endif 212 } else if (S_ISFIFO(mode)) { 213 h->type = FIFO; 214 } 215 216 memset(h->chksum, ' ', sizeof h->chksum); 217 for (x = 0, chksum = 0; x < sizeof *h; x++) 218 chksum += b[x]; 219 putoctal(h->chksum, chksum, sizeof h->chksum); 220 221 fwrite(b, Blksiz, 1, tarfile); 222 if (!f) 223 return 0; 224 while ((l = fread(b, 1, Blksiz, f)) > 0) { 225 if (l < Blksiz) 226 memset(b+l, 0, Blksiz-l); 227 fwrite(b, Blksiz, 1, tarfile); 228 } 229 fclose(f); 230 return 0; 231 } 232 233 static int 234 unarchive(char *fname, int l, char b[Blksiz]) 235 { 236 char lname[101]; 237 FILE *f = NULL; 238 unsigned long mode, major, minor, type, mtime; 239 struct timeval times[2]; 240 Header *h = (void*)b; 241 242 if (!mflag) 243 mtime = strtoul(h->mtime, 0, 8); 244 unlink(fname); 245 switch (h->type) { 246 case REG: 247 case AREG: 248 mode = strtoul(h->mode, 0, 8); 249 if (!(f = fopen(fname, "w")) || chmod(fname, mode)) 250 perror(fname); 251 break; 252 case HARDLINK: 253 case SYMLINK: 254 snprintf(lname, sizeof lname, "%s", h->link); 255 if (!((h->type == HARDLINK) ? link : symlink)(lname, fname)) 256 perror(fname); 257 break; 258 case DIRECTORY: 259 mode = strtoul(h->mode, 0, 8); 260 if (mkdir(fname, (mode_t)mode)) 261 perror(fname); 262 break; 263 case CHARDEV: 264 case BLOCKDEV: 265 #ifdef makedev 266 mode = strtoul(h->mode, 0, 8); 267 major = strtoul(h->major, 0, 8); 268 minor = strtoul(h->mode, 0, 8); 269 type = (h->type == CHARDEV) ? S_IFCHR : S_IFBLK; 270 if (mknod(fname, type | mode, makedev(major, minor))) 271 perror(fname); 272 #endif 273 break; 274 case FIFO: 275 mode = strtoul(h->mode, 0, 8); 276 if (mknod(fname, S_IFIFO | mode, 0)) 277 perror(fname); 278 break; 279 default: 280 fprintf(stderr, "usupported tarfiletype %c\n", h->type); 281 } 282 if (getuid() == 0 && chown(fname, strtoul(h->uid, 0, 8), 283 strtoul(h->gid, 0, 8))) 284 perror(fname); 285 286 for (; l > 0; l -= Blksiz) { 287 fread(b, Blksiz, 1, tarfile); 288 if (f) 289 fwrite(b, MIN(l, 512), 1, f); 290 } 291 if (f) 292 fclose(f); 293 294 if (!mflag) { 295 times[0].tv_sec = times[1].tv_sec = mtime; 296 times[0].tv_usec = times[1].tv_usec = 0; 297 if (utimes(fname, times)) 298 perror(fname); 299 } 300 return 0; 301 } 302 303 static int 304 print(char * fname, int l, char b[Blksiz]) 305 { 306 puts(fname); 307 for (; l > 0; l -= Blksiz) 308 fread(b, Blksiz, 1, tarfile); 309 return 0; 310 } 311 312 static void 313 c(const char * path) 314 { 315 archive(path); 316 recurse(path, c); 317 } 318 319 static void 320 xt(int (*fn)(char*, int, char[Blksiz])) 321 { 322 char b[Blksiz], fname[257], *s; 323 Header *h = (void*)b; 324 325 while (fread(b, Blksiz, 1, tarfile) && h->name[0] != '\0') { 326 s = fname; 327 if (h->prefix[0] != '\0') 328 s += sprintf(s, "%.*s/", (int)sizeof h->prefix, h->prefix); 329 sprintf(s, "%.*s", (int)sizeof h->name, h->name); 330 fn(fname, strtol(h->size, 0, 8), b); 331 } 332 }