/* jtar.c : Jon's Tar */ /* Copyright 2001 Jon Mayo * Updated May 14, 2008 - Jon Mayo */ #include #include #include #include #include #include #include static int verbose_fl=1; #define RECORD_LEN 512 #define NAME_LEN 100 #define USER_LEN 32 #define GROUP_LEN 32 #define MAGIC_TAR "ustar " #define MAGIC_GNUTAR "GNUtar " struct record { char name[NAME_LEN+1]; uint32_t mode; uint32_t uid; uint32_t gid; uint64_t size; time_t mtime; uint32_t chksum; char linkflag; char linkname[NAME_LEN+1]; /* char magic[8]; */ char uname[USER_LEN+1]; char gname[GROUP_LEN+1]; uint32_t devmajor; uint32_t devminor; }; /* make room to null terminate everything */ struct record_str { char name[NAME_LEN+1]; char mode[8+1]; char uid[8+1]; char gid[8+1]; char size[12+1]; char mtime[12+1]; char chksum[8+1]; char linkflag; char linkname[NAME_LEN+1]; char magic[8+1]; char uname[USER_LEN+1]; char gname[GROUP_LEN+1]; char devmajor[8+1]; char devminor[8+1]; }; /* loads data. * dest destination pointer to record * src source pointer from buffer * len length to read * ofs current offset. gets incremented * term NULL terminate the field after len */ /* TODO: handle legths and things */ void ldata(void *dest, const void *src, int len, int *ofs, int term) { memcpy(dest, (char*)src+*ofs, len); if(term) ((char*)dest)[len]=0; (*ofs)+=len; } const char *type2str(char linktype) { const char *ret; switch(linktype) { case '\0': ret="file-old"; break; case '0': ret="file"; break; case '1': ret="link"; break; /* link to previously dumped file */ case '2': ret="symlink"; break; /* symbolic link */ case '3': ret="dev-char"; break; /* character device */ case '4': ret="dev-block"; break; /* block device */ case '5': ret="directory"; break; /* directory */ case '6': ret="fifo"; break; /* fifo */ case '7': ret="file-contiguous"; break; /* contiguous file */ default: ret="unknown"; } return ret; } int convrec(struct record *r, const struct record_str *s) { if(strcmp(s->magic, MAGIC_TAR) && strcmp(s->magic, MAGIC_GNUTAR)) { return 0; } strcpy(r->name, s->name); r->mode=strtoul(s->mode, NULL, 8); r->uid=strtoul(s->uid, NULL, 8); r->gid=strtoul(s->gid, NULL, 8); r->size=strtoul(s->size, NULL, 8); r->mtime=strtoul(s->mtime, NULL, 8); r->chksum=strtoul(s->chksum, NULL, 8); r->linkflag=s->linkflag; strcpy(r->linkname, s->linkname); strcpy(r->uname, s->uname); strcpy(r->gname, s->gname); r->devmajor=strtoul(s->devmajor, NULL, 8); r->devminor=strtoul(s->devminor, NULL, 8); return 1; } void fillrec(struct record_str *r, const char *buf) { int i; i=0; ldata(&r->name, buf, NAME_LEN, &i, 1); ldata(&r->mode, buf, 8, &i, 1); ldata(&r->uid, buf, 8, &i, 1); ldata(&r->gid, buf, 8, &i, 1); ldata(&r->size, buf, 12, &i, 1); ldata(&r->mtime, buf, 12, &i, 1); ldata(&r->chksum, buf, 8, &i, 1); ldata(&r->linkflag, buf, 1, &i, 0); ldata(&r->linkname, buf, NAME_LEN, &i, 1); ldata(&r->magic, buf, 8, &i, 1); ldata(&r->uname, buf, USER_LEN, &i, 1); ldata(&r->gname, buf, GROUP_LEN, &i, 1); ldata(&r->devmajor, buf, 8, &i, 1); ldata(&r->devminor, buf, 8, &i, 1); } void dumprec(struct record *r) { struct tm *tm; char timebuf[32]; tm=gmtime(&r->mtime); strftime(timebuf, sizeof timebuf, "%c", tm); printf( "name :%s\n" "mode :%" PRIo32 "\n" "uid :%" PRIu32 "\n" "gid :%" PRIu32 "\n" "size :%" PRIu64 "\n" "mtime :%s\n" "chksum :%" PRIu32 "\n" "linkflag :(%s)\n" "linkname :%s\n" /* "magic :%s\n" */ "uname :%s\n" "gname :%s\n" "devmajor :%" PRIu32 "\n" "devminor :%" PRIu32 "\n" "-----------:------------------------------------------------------------------\n", r->name, r->mode, r->uid, r->gid, r->size, timebuf, r->chksum, type2str(r->linkflag), r->linkname, /* r->magic, */ r->uname, r->gname, r->devmajor, r->devminor ); } /* use fake flag to disable creation of files */ int procdata_regular(gzFile in, struct record *r, int fake) { char buf[RECORD_LEN]; int cnt; /* number of blocks. assume we're padded */ int i; FILE *f; if(!fake) { /* TODO: mkdir_p(basename(r->name)) */ f=fopen(r->name, "wb"); if(!f) { perror(r->name); return 0; /* failure */ } } if(verbose_fl) fprintf(stderr, "creating %s\n", r->name); /* Eats the Data */ cnt=(r->size+RECORD_LEN-1)/RECORD_LEN; for(i=0;ilinkflag, r->name); switch(r->linkflag) { case '\0': case '0': /* regular file */ return procdata_regular(in, r, fake); case '2': /* symbolic link */ /* TODO: make symlink */ if(verbose_fl) fprintf(stderr, "ln -- %s %s\n", r->name, r->linkname); return 1; case '5': /* directory */ /* TODO: make directory */ if(verbose_fl) fprintf(stderr, "mkdir -- %s\n", r->name); return 1; case '1': /* link to previously dumped file */ case '3': /* character device */ case '4': /* block device */ case '6': /* fifo */ case '7': /* contiguous file */ default: fprintf(stderr, "%s:cannot create unknown type 0x%02x\n", r->name, r->linkflag); return 0; /* unknown mode */ } } int iszero(const void *data, int len) { int i; int len2, len3; len2=len/sizeof(int); for(i=0;i