morpheus-base

morpheus base system
git clone git://git.2f30.org/morpheus-base
Log | Files | Refs

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 }