sltar

a simple tar implementation
git clone git://git.suckless.org/sltar
Log | Files | Refs | LICENSE

sltar.c (4274B)


      1 /* sltar - suckless tar
      2  * Copyright (C) <2007> Enno boland <g s01 de>
      3  *
      4  * See LICENSE for further informations
      5  */
      6 #include <stdlib.h>
      7 #include <unistd.h>
      8 #include <stdio.h>
      9 #include <string.h>
     10 #include <sys/stat.h>
     11 #include <sys/types.h>
     12 #include <limits.h>
     13 #include <ftw.h>
     14 #include <grp.h>
     15 #include <pwd.h>
     16 
     17 #define MIN(a, b) (((a)<(b))?(a):(b))
     18 
     19 enum Header {
     20 	NAME=0, MODE = 100, UID = 108, GID = 116, SIZE = 124, MTIME = 136, CHKSUM=148,
     21 	TYPE = 156, LINK = 157, MAGIC=257, VERS=263, UNAME=265, GNAME=297, MAJ = 329, 
     22 	MIN = 337, END = 512
     23 };
     24 
     25 enum Type {
     26 	REG = '0', HARDLINK = '1', SYMLINK = '2', CHARDEV='3', BLOCKDEV='4',
     27 	DIRECTORY='5', FIFO='6' 
     28 };
     29 
     30 int archive(const char* path, const struct stat* sta, int type){
     31 	char b[END];
     32 	FILE *f = NULL;
     33 	struct stat s = *sta, *st = &s;
     34 	lstat(path, st);
     35 	memset(b, 0, END);
     36 	snprintf(b+NAME, 100, "%s", path);
     37 	snprintf(b+MODE, 8, "%.7o", (unsigned)st->st_mode&0777);
     38 	snprintf(b+UID,  8, "%.7o", (unsigned)st->st_uid);
     39 	snprintf(b+GID,  8, "%.7o", (unsigned)st->st_gid);
     40 	snprintf(b+SIZE, 12, "%.11o", 0);
     41 	snprintf(b+MTIME,12, "%.11o", (unsigned)st->st_mtime);
     42 	memcpy(b+MAGIC, "ustar", strlen("ustar")+1);
     43 	memcpy(b+VERS, "00", strlen("00"));
     44 	struct passwd *pw = getpwuid(st->st_uid);
     45 	snprintf(b+UNAME, 32, "%s", pw->pw_name);
     46 	struct group *gr = getgrgid(st->st_gid);
     47 	snprintf(b+GNAME, 32, "%s", gr->gr_name);	
     48 	mode_t mode = st->st_mode;
     49 	if(S_ISREG(mode)){
     50 		b[TYPE] = REG;
     51 		snprintf(b+SIZE, 12, "%.11o", (unsigned)st->st_size);
     52 		f = fopen(path, "r");
     53 	}else if(S_ISDIR(mode)){
     54 		b[TYPE] = DIRECTORY;
     55 	}else if(S_ISLNK(mode)){
     56 		b[TYPE] = SYMLINK;
     57 		readlink(path, b+LINK, 99);
     58 	}else if(S_ISCHR(mode)){
     59 		b[TYPE] = CHARDEV;
     60 		snprintf(b+MAJ,  8, "%.7o", (unsigned)major(st->st_dev));
     61 		snprintf(b+MIN,  8, "%.7o", (unsigned)minor(st->st_dev));
     62 	}else if(S_ISBLK(mode)){
     63 		b[TYPE] = BLOCKDEV;
     64 		snprintf(b+MAJ,  8, "%.7o", (unsigned)major(st->st_dev));
     65 		snprintf(b+MIN,  8, "%.7o", (unsigned)minor(st->st_dev));
     66 	}else if(S_ISFIFO(mode)){
     67 		b[TYPE] = FIFO;
     68 	}
     69 	unsigned sum=0, x;
     70 	memset(b+CHKSUM, ' ', 8);
     71 	for(x=0; x<END; x++)
     72 		sum+=b[x];
     73 	snprintf(b+CHKSUM, 8, "%.7o", sum);
     74 	fwrite(b, END, 1, stdout);
     75 	if(!f)
     76 		return 0;
     77 	int l;
     78 	while((l = fread(b, 1, END, f))>0){
     79 		if(l<END)
     80 			memset(b+l, 0, END-l);
     81 		fwrite(b, END, 1, stdout);
     82 	}
     83 	fclose(f);
     84 	return 0;	
     85 }
     86 
     87 int unarchive(char *fname, int l, char b[END]){
     88 	static char lname[101] = {0};
     89 	FILE *f = NULL;
     90 	memcpy(lname, b+LINK, 100);
     91 
     92 	unlink(fname);
     93 	switch(b[TYPE]) {
     94 	case REG:
     95 		if(!(f = fopen(fname,"w")) || chmod(fname,strtoul(b + MODE,0,8)))
     96 			perror(fname);
     97 		break;
     98 	case HARDLINK:
     99 		if(!link(lname,fname))
    100 			perror(fname);
    101 		break;
    102 	case SYMLINK:
    103 		if(!symlink(lname,fname))
    104 			perror(fname);
    105 		break;
    106 	case DIRECTORY:
    107 		if(mkdir(fname,(mode_t) strtoull(b + MODE,0,8)))
    108 			perror(fname);
    109 		break;
    110 	case CHARDEV:
    111 	case BLOCKDEV:
    112 		if(mknod(fname, (b[TYPE] == '3' ? S_IFCHR : S_IFBLK) | strtoul(b + MODE,0,8),
    113 				makedev(strtoul(b + MAJ,0,8),
    114 					strtoul(b + MIN,0,8))))
    115 			perror(fname);
    116 		break;
    117 	case FIFO:
    118 		if(mknod(fname, S_IFIFO | strtoul(b + MODE,0,8), 0))
    119 			perror(fname);
    120 		break;
    121 	default:
    122 		fprintf(stderr,"not supported filetype %c\n",b[TYPE]);
    123 	}
    124 	if(getuid() == 0 && chown(fname, strtoul(b + UID,0,8),strtoul(b + GID,0,8)))
    125 		perror(fname);
    126 
    127 	for(;l>0; l-=END){
    128 		fread(b, END, 1, stdin);
    129 		if(f)
    130 			fwrite(b, MIN(l, 512), 1, f);
    131 	}
    132 	if(f)
    133 		fclose(f);
    134 	return 0;
    135 }
    136 
    137 int print(char * fname, int l, char b[END]){
    138 	puts(fname);
    139 	for(;l>0; l-=END)
    140 		fread(b, END, 1, stdin);
    141 	return 0;
    142 }
    143 
    144 int c(char * dir) {
    145 	ftw(dir, archive, 128);//OPEN_MAX);
    146 	return EXIT_SUCCESS;
    147 }
    148 
    149 int xt(int (*fn)(char*, int, char[END])) {
    150 	int l;
    151 	char b[END],fname[101];
    152 	fname[100] = '\0';
    153 
    154 	while(fread(b, END, 1, stdin)){
    155 		if(*b == '\0')
    156 			break;
    157 		memcpy(fname, b, 100);
    158 		l = strtol(b+SIZE, 0, 8);
    159 		fn(fname, l, b);
    160 	}
    161 	return EXIT_SUCCESS;
    162 }
    163 
    164 void usage(){
    165 	fputs("sltar-" VERSION " - suckless tar\nsltar [ctx]\n",stderr);
    166 	exit(EXIT_SUCCESS);
    167 }
    168 
    169 int main(int argc, char *argv[]) {
    170 	if(argc < 2 || argc > 3 || strlen(argv[1])!=1)
    171 		usage();
    172 	switch(argv[1][0]) {
    173 	case 'c':
    174 		if(argc<3)
    175 			usage();
    176 		return c(argv[2]);
    177 	case 'x':
    178 		return xt(unarchive);
    179 	case 't':
    180 		return xt(print);
    181 	default:
    182 		usage();
    183 	}
    184 	return EXIT_FAILURE;
    185 }