sltar

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

commit 31779c429b85ee384455d13522bbdc7a50dbbd2d
parent 54944c3ffa7aceba3b62e4b823c3188ea335a9d7
Author: Enno Boland (Gottox) <g@s01.de>
Date:   Sun, 11 Mar 2012 11:25:15 +0100

Creating tar support, thanks David Galos
Diffstat:
LICENSE | 2+-
sltar.c | 226++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
2 files changed, 157 insertions(+), 71 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -1,6 +1,6 @@ MIT/X Consortium License -(c) 2007 Enno Boland <g s01 de> +(c) 2012 Enno Boland <g s01 de> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/sltar.c b/sltar.c @@ -8,92 +8,178 @@ #include <stdio.h> #include <string.h> #include <sys/stat.h> +#include <sys/types.h> +#include <limits.h> +#include <ftw.h> +#include <grp.h> +#include <pwd.h> + +#define MIN(a, b) (((a)<(b))?(a):(b)) enum Header { - MODE = 100, UID = 108, GID = 116, SIZE = 124, MTIME = 136, - TYPE = 156, LINK = 157, MAJ = 329, MIN = 337, END = 512 + NAME=0, MODE = 100, UID = 108, GID = 116, SIZE = 124, MTIME = 136, CHKSUM=148, + TYPE = 156, LINK = 157, MAGIC=257, VERS=263, UNAME=265, GNAME=297, MAJ = 329, + MIN = 337, END = 512 }; -int c() { - fputs("Creating tars does not work yet\n", stderr); - return EXIT_FAILURE; -} -int xt(char a) { + +enum Type { + REG = '0', HARDLINK = '1', SYMLINK = '2', CHARDEV='3', BLOCKDEV='4', + DIRECTORY='5', FIFO='6' +}; + +int archive(const char* path, const struct stat* sta, int type){ + char b[END]; + FILE *f = NULL; + struct stat s = *sta, *st = &s; + lstat(path, st); + memset(b, 0, END); + snprintf(b+NAME, 100, "%s", path); + snprintf(b+MODE, 8, "%.7o", (unsigned)st->st_mode&0777); + snprintf(b+UID, 8, "%.7o", (unsigned)st->st_uid); + snprintf(b+GID, 8, "%.7o", (unsigned)st->st_gid); + snprintf(b+SIZE, 12, "%.11o", 0); + snprintf(b+MTIME,12, "%.11o", (unsigned)st->st_mtime); + memcpy(b+MAGIC, "ustar", strlen("ustar")+1); + memcpy(b+VERS, "00", strlen("00")); + struct passwd *pw = getpwuid(st->st_uid); + snprintf(b+UNAME, 32, "%s", pw->pw_name); + struct group *gr = getgrgid(st->st_gid); + snprintf(b+GNAME, 32, "%s", gr->gr_name); + mode_t mode = st->st_mode; + if(S_ISREG(mode)){ + b[TYPE] = REG; + snprintf(b+SIZE, 12, "%.11o", (unsigned)st->st_size); + f = fopen(path, "r"); + }else if(S_ISDIR(mode)){ + b[TYPE] = DIRECTORY; + }else if(S_ISLNK(mode)){ + b[TYPE] = SYMLINK; + readlink(path, b+LINK, 99); + }else if(S_ISCHR(mode)){ + b[TYPE] = CHARDEV; + snprintf(b+MAJ, 8, "%.7o", (unsigned)major(st->st_dev)); + snprintf(b+MIN, 8, "%.7o", (unsigned)minor(st->st_dev)); + }else if(S_ISBLK(mode)){ + b[TYPE] = BLOCKDEV; + snprintf(b+MAJ, 8, "%.7o", (unsigned)major(st->st_dev)); + snprintf(b+MIN, 8, "%.7o", (unsigned)minor(st->st_dev)); + }else if(S_ISFIFO(mode)){ + b[TYPE] = FIFO; + } + unsigned sum=0, x; + memset(b+CHKSUM, ' ', 8); + for(x=0; x<END; x++) + sum+=b[x]; + snprintf(b+CHKSUM, 8, "%.7o", sum); + fwrite(b, END, 1, stdout); + if(!f) + return 0; int l; - char b[END],fname[101],lname[101]; + while((l = fread(b, 1, END, f))>0){ + if(l<END) + memset(b+l, 0, END-l); + fwrite(b, END, 1, stdout); + } + fclose(f); + return 0; +} + +int unarchive(char *fname, int l, char b[END]){ + static char lname[101] = {0}; FILE *f = NULL; + memcpy(lname, b+LINK, 100); - for(lname[100] = fname[100] = l = 0; fread(b,END,1,stdin); l -= END) - if(l <= 0) { - if(*b == '\0') - break; - memcpy(fname,b,100); - memcpy(lname,b + LINK,100); - l = strtoull(b + SIZE,0,8) + END; - if(a == 't') { - puts(fname); - continue; - } - if(f) { - fclose(f); - f = 0; - } - unlink(fname); - switch(b[TYPE]) { - case '0': /* file */ - if(!(f = fopen(fname,"w")) || chmod(fname,strtoul(b + MODE,0,8))) - perror(fname); - break; - case '1': /* hardlink */ - if(!link(lname,fname)) - perror(fname); - break; - case '2': /* symlink */ - if(!symlink(lname,fname)) - perror(fname); - break; - case '5': /* directory */ - if(mkdir(fname,(mode_t) strtoull(b + MODE,0,8))) - perror(fname); - break; - case '3': /* char device */ - case '4': /* block device */ - if(mknod(fname, (b[TYPE] == '3' ? S_IFCHR : S_IFBLK) | strtoul(b + MODE,0,8), - makedev(strtoul(b + MAJ,0,8), - strtoul(b + MIN,0,8)))) - perror(fname); - break; - case '6': /* fifo */ - if(mknod(fname, S_IFIFO | strtoul(b + MODE,0,8), 0)) - perror(fname); - break; - default: - fprintf(stderr,"not supported filetype %c\n",b[TYPE]); - } - if(getuid() == 0 && chown(fname, strtoul(b + UID,0,8),strtoul(b + GID,0,8))) - perror(fname); - } - else if(a == 'x' && f && !fwrite(b,l > 512 ? END : l,1,f)) { + unlink(fname); + switch(b[TYPE]) { + case REG: + if(!(f = fopen(fname,"w")) || chmod(fname,strtoul(b + MODE,0,8))) perror(fname); - break; - } + break; + case HARDLINK: + if(!link(lname,fname)) + perror(fname); + break; + case SYMLINK: + if(!symlink(lname,fname)) + perror(fname); + break; + case DIRECTORY: + if(mkdir(fname,(mode_t) strtoull(b + MODE,0,8))) + perror(fname); + break; + case CHARDEV: + case BLOCKDEV: + if(mknod(fname, (b[TYPE] == '3' ? S_IFCHR : S_IFBLK) | strtoul(b + MODE,0,8), + makedev(strtoul(b + MAJ,0,8), + strtoul(b + MIN,0,8)))) + perror(fname); + break; + case FIFO: + if(mknod(fname, S_IFIFO | strtoul(b + MODE,0,8), 0)) + perror(fname); + break; + default: + fprintf(stderr,"not supported filetype %c\n",b[TYPE]); + } + if(getuid() == 0 && chown(fname, strtoul(b + UID,0,8),strtoul(b + GID,0,8))) + perror(fname); + + for(;l>0; l-=END){ + fread(b, END, 1, stdin); + if(f) + fwrite(b, MIN(l, 512), 1, f); + } if(f) fclose(f); + return 0; +} + +int print(char * fname, int l, char b[END]){ + puts(fname); + for(;l>0; l-=END) + fread(b, END, 1, stdin); + return 0; +} + +int c(char * dir) { + ftw(dir, archive, 128);//OPEN_MAX); return EXIT_SUCCESS; } -int main(int argc, char *argv[]) { - char a = 0; +int xt(int (*fn)(char*, int, char[END])) { + int l; + char b[END],fname[101]; + fname[100] = '\0'; - if(argc == 2 && argv[1][0] != 0 && argv[1][1] == 0) - a = argv[1][0]; - switch(a) { + while(fread(b, END, 1, stdin)){ + if(*b == '\0') + break; + memcpy(fname, b, 100); + l = strtol(b+SIZE, 0, 8); + fn(fname, l, b); + } + return EXIT_SUCCESS; +} + +void usage(){ + fputs("sltar-" VERSION " - suckless tar\nsltar [ctx]\n",stderr); + exit(EXIT_SUCCESS); +} + +int main(int argc, char *argv[]) { + if(argc < 2 || argc > 3 || strlen(argv[1])!=1) + usage(); + switch(argv[1][0]) { case 'c': - return c(); + if(argc<3) + usage(); + return c(argv[2]); case 'x': + return xt(unarchive); case 't': - return xt(a); + return xt(print); default: - fputs("sltar-" VERSION " - suckless tar\nsltar [ctx]\n",stderr); - return EXIT_SUCCESS; + usage(); } + return EXIT_FAILURE; }