diff -ur /usr/src/usr.sbin/ctm/ctm/ctm.1 ctm/ctm.1 --- /usr/src/usr.sbin/ctm/ctm/ctm.1 2010-04-24 17:19:38.000000000 -0500 +++ ctm/ctm.1 2011-12-24 16:38:07.000000000 -0600 @@ -52,8 +52,11 @@ You can pass a CTM delta on stdin, or you can give the filename as an argument. If you do the latter, you make life a lot -easier for your self, since the program can accept gzip'ed files and +easier for your self, since the program can accept gzip'ed, +bzip2'ed, or xz'ed files and since it will not have to make a temporary copy of your file. +(If you pass it an xz'ed file, and xz is not part of your base system, +you will have to install xz from the ports.) You can specify multiple deltas at one time, they will be processed one at a time. diff -ur /usr/src/usr.sbin/ctm/ctm/ctm.c ctm/ctm.c --- /usr/src/usr.sbin/ctm/ctm/ctm.c 2010-04-24 17:19:38.000000000 -0500 +++ ctm/ctm.c 2012-12-16 21:39:50.000000000 -0600 @@ -211,6 +211,22 @@ strcat(p,filename); f = popen(p,"r"); if(!f) { warn("%s", p); return Exit_Garbage; } + } else if(p && !strcmp(p,".bz2")) { + p = alloca(20 + strlen(filename)); + strcpy(p,"bzcat < "); + strcat(p,filename); + f = popen(p,"r"); + if(!f) { warn("%s", p); return Exit_Garbage; } + } else if(p && !strcmp(p,".xz")) { + if (system("which -s xz") != 0) { + fprintf(stderr, "xz is not found in $PATH. You can install it from ports, or adjust $PATH.\n"); + return Exit_Garbage; + } + p = alloca(20 + strlen(filename)); + strcpy(p,"xz -dc < "); + strcat(p,filename); + f = popen(p,"r"); + if(!f) { warn("%s", p); return Exit_Garbage; } } else { p = 0; f = fopen(filename,"r"); diff -ur /usr/src/usr.sbin/ctm/ctm/ctm.h ctm/ctm.h --- /usr/src/usr.sbin/ctm/ctm/ctm.h 2010-04-24 17:19:38.000000000 -0500 +++ ctm/ctm.h 2012-09-08 22:55:17.000000000 -0500 @@ -24,6 +24,7 @@ #include #include #include +#include #define VERSION "2.0" @@ -40,6 +41,8 @@ #define CTM_F_MD5 0x05 #define CTM_F_Count 0x06 #define CTM_F_Bytes 0x07 +#define CTM_F_Release 0x08 +#define CTM_F_Forward 0x09 /* The qualifiers... */ #define CTM_Q_MASK 0xff00 @@ -47,10 +50,13 @@ #define CTM_Q_Name_Dir 0x0200 #define CTM_Q_Name_New 0x0400 #define CTM_Q_Name_Subst 0x0800 +#define CTM_Q_Name_Svnbase 0x1000 #define CTM_Q_MD5_After 0x0100 #define CTM_Q_MD5_Before 0x0200 #define CTM_Q_MD5_Chunk 0x0400 #define CTM_Q_MD5_Force 0x0800 +#define CTM_Q_Forward_Tar 0x0100 +#define CTM_Q_Forward_SVN 0x0200 struct CTM_Syntax { char *Key; /* CTM key for operation */ @@ -145,14 +151,16 @@ u_char * Ffield(FILE *fd, MD5_CTX *ctx,u_char term); u_char * Fname(FILE *fd, MD5_CTX *ctx,u_char term,int qual, int verbose); -int Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term); +intmax_t Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term); u_char * Fdata(FILE *fd, int u_chars, MD5_CTX *ctx); +int Fforward(FILE *fd, intmax_t u_chars, MD5_CTX *ctx, FILE *fd_to); #define GETFIELD(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD #define GETFIELDCOPY(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD; else p=String(p) #define GETBYTECNT(p,q) if(0 >((p)= Fbytecnt(fd,&ctx,(q)))) return BADREAD #define GETDATA(p,q) if(!((p) = Fdata(fd,(q),&ctx))) return BADREAD +#define GETFORWARD(p,q) if(!Fforward(fd,(p),&ctx,q)) return BADREAD #define GETNAMECOPY(p,q,r,v) if(!((p)=Fname(fd,&ctx,(q),(r),(v)))) return BADREAD; else p=String(p) int Pass1(FILE *fd, unsigned applied); diff -ur /usr/src/usr.sbin/ctm/ctm/ctm_input.c ctm/ctm_input.c --- /usr/src/usr.sbin/ctm/ctm/ctm_input.c 2010-04-24 17:19:38.000000000 -0500 +++ ctm/ctm_input.c 2011-12-24 16:38:07.000000000 -0600 @@ -61,11 +61,11 @@ return buf; } -int +intmax_t Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term) { u_char *p,*q; - int u_chars=0; + intmax_t u_chars=0; p = Ffield(fd,ctx,term); if(!p) return -1; @@ -100,6 +100,42 @@ return p; } +int Fforward(FILE *fd, intmax_t u_chars, MD5_CTX *ctx, FILE *fd_to) +{ + u_char buf[BUFSIZ]; + intmax_t amount_read = 0; + int amount_to_read; + + while (amount_read < u_chars) { + if (u_chars - amount_read >= BUFSIZ) + amount_to_read = BUFSIZ; + else + amount_to_read = u_chars - amount_read; + if(amount_to_read != fread(buf, 1, amount_to_read, fd)) { + Fatal("Truncated patch."); + return 0; + } + MD5Update(ctx,buf,amount_to_read); + if (fd_to != NULL) { + if (amount_to_read != fwrite(buf, 1, amount_to_read, fd_to)) { + Fatal("Write error."); + return 0; + } + } + amount_read += amount_to_read; + } + + if(getc(fd) != '\n') { + if(Verbose > 3) + printf("FileData wasn't followed by a newline.\n"); + Fatal("Corrupt patch."); + return 0; + } + MD5Update(ctx,"\n",1); + return 1; +} + + /*---------------------------------------------------------------------------*/ /* get the filename in the next field, prepend BaseDir and give back the result strings. The sustitute filename is return (the one with the suffix SUBSUFF) diff -ur /usr/src/usr.sbin/ctm/ctm/ctm_pass1.c ctm/ctm_pass1.c --- /usr/src/usr.sbin/ctm/ctm/ctm_pass1.c 2010-04-24 17:19:38.000000000 -0500 +++ ctm/ctm_pass1.c 2011-12-24 16:38:07.000000000 -0600 @@ -22,7 +22,8 @@ { u_char *p,*q; MD5_CTX ctx; - int i,j,sep,cnt; + int i,j,sep; + intmax_t cnt, rel; u_char *md5=0,*name=0,*trash=0; struct CTM_Syntax *sp; int slashwarn=0, match=0, total_matches=0; @@ -96,7 +97,7 @@ if(Verbose > 5) fprintf(stderr,"%s ",sp->Key); for(i=0;(j = sp->List[i]);i++) { - if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes) + if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Forward) sep = ' '; else sep = '\n'; @@ -209,6 +210,17 @@ if(md5 && strcmp(md5,p)) { Fatal("Internal MD5 failed."); return Exit_Garbage; + case CTM_F_Release: + GETBYTECNT(rel,sep); + break; + case CTM_F_Forward: + if(cnt < 0) WRONG + if ((j & CTM_Q_MASK) == CTM_Q_Forward_SVN && system("which -s svnadmin") != 0) { + fprintf(stderr, "subversion is not installed. You can install it from ports.\n"); + return Exit_Garbage; + } + GETFORWARD(cnt,NULL); + break; default: fprintf(stderr,"List = 0x%x\n",j); Fatal("List had garbage."); diff -ur /usr/src/usr.sbin/ctm/ctm/ctm_pass2.c ctm/ctm_pass2.c --- /usr/src/usr.sbin/ctm/ctm/ctm_pass2.c 2010-04-24 17:19:38.000000000 -0500 +++ ctm/ctm_pass2.c 2012-12-15 22:06:06.000000000 -0600 @@ -22,7 +22,11 @@ { u_char *p,*q,*md5=0; MD5_CTX ctx; - int i,j,sep,cnt,fdesc; + int i,j,sep,fdesc; + intmax_t cnt, rel; + int rel2; + FILE *current; + char *current_file_name = NULL; u_char *trash=0,*name=0; struct CTM_Syntax *sp; struct stat st; @@ -30,7 +34,7 @@ int match = 0; char md5_1[33]; struct CTM_Filter *filter; - FILE *ed = NULL; + FILE *ed = NULL, *fd_to = NULL; static char *template = NULL; if(Verbose>3) @@ -72,7 +76,7 @@ WRONG found: for(i=0;(j = sp->List[i]);i++) { - if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes) + if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Forward) sep = ' '; else sep = '\n'; @@ -128,6 +132,22 @@ sp->Key,name); ret |= Exit_NotOK; } + if (j & CTM_Q_Name_Svnbase) { + current_file_name = alloca(strlen(name)+128); + strcpy(current_file_name,name); + strcat(current_file_name,"/db/current"); + current = fopen(current_file_name,"r"); + if (current==NULL) { + fprintf(stderr,"Cannot open %s\n",current_file_name); + WRONG + } + if (fscanf(current,"%d",&rel2) != 1) { + fprintf(stderr,"Cannot find release number in %s\n",current_file_name); + fclose(current); + WRONG + } + fclose(current); + } break; } if (j & CTM_Q_Name_File) { @@ -284,6 +304,42 @@ } break; + case CTM_F_Release: + GETBYTECNT(rel,sep); + if(Verbose > 3) + printf("Expecting release number %jd\n",rel); + if(Verbose > 3) + printf("Actual release number %d\n",rel2); + if (rel != rel2) { + fprintf(stderr,"Release number mismatch: found %d, need %jd\n",rel2,rel); + WRONG + } + break; + case CTM_F_Forward: + if ((j & CTM_Q_MASK) == CTM_Q_Forward_SVN) { + if(Verbose>3) + printf("This is a svn dump file and there is no certainty that it will apply cleanly.\n"); + GETFORWARD(cnt,NULL); + } + else if ((j & CTM_Q_MASK) == CTM_Q_Forward_Tar) { + if(Verbose>3) { + printf("This is a tar file and there is no certainty that it will apply cleanly even if it passes the following test.\n"); + fd_to = popen("tar tvf -","w"); + } else + fd_to = popen("tar tf - >/dev/null 2>&1","w"); + if (fd_to == NULL) { + fprintf(stderr,"Cannot forward\n"); + WRONG + } + GETFORWARD(cnt,fd_to); + if (pclose(fd_to)) { + fprintf(stderr,"Tar failed to close properly\n"); + WRONG + } else + if (Verbose > 3) + printf("Tar file test was good\n"); + } + break; default: WRONG } } diff -ur /usr/src/usr.sbin/ctm/ctm/ctm_pass3.c ctm/ctm_pass3.c --- /usr/src/usr.sbin/ctm/ctm/ctm_pass3.c 2010-04-24 17:19:38.000000000 -0500 +++ ctm/ctm_pass3.c 2012-12-16 15:12:57.000000000 -0600 @@ -33,10 +33,12 @@ { u_char *p,*q,buf[BUFSIZ]; MD5_CTX ctx; - int i,j,sep,cnt; + int i,j,sep; + intmax_t cnt,rel; + char *svn_command = NULL; u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0; struct CTM_Syntax *sp; - FILE *ed=0; + FILE *ed=0, *fd_to; struct stat st; char md5_1[33]; int match=0; @@ -129,7 +131,7 @@ WRONG found: for(i=0;(j = sp->List[i]);i++) { - if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes) + if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Forward) sep = ' '; else sep = '\n'; @@ -147,53 +149,98 @@ break; case CTM_F_Count: GETBYTECNT(cnt,sep); break; case CTM_F_Bytes: GETDATA(trash,cnt); break; + case CTM_F_Release: GETBYTECNT(rel,sep); break; + case CTM_F_Forward: + if ((j & CTM_Q_MASK) == CTM_Q_Forward_Tar) { + if (Verbose > 0) + fd_to = popen("tar xvf -","w"); + else + fd_to = popen("tar xvf - >/dev/null 2>&1","w"); + } else if ((j & CTM_Q_MASK) == CTM_Q_Forward_SVN) { + svn_command = alloca(strlen(name)+128); + if (Verbose > 0) + snprintf(svn_command,strlen(name)+127,"svnadmin load %s\n", name); + else + snprintf(svn_command,strlen(name)+127,"svnadmin load %s > /dev/null 2>&1\n", name); + fd_to = popen(svn_command,"w"); + } else WRONG + if (fd_to == NULL) { + fprintf(stderr,"Cannot forward\n"); + WRONG + } + if (Verbose > 0) { + if (!strcmp(sp->Key,"TR")) + fprintf(stderr,"> %s\n",sp->Key); + else + fprintf(stderr,"> %s %s\n",sp->Key,name); + } + GETFORWARD(cnt,fd_to); + if (pclose(fd_to)) { + if ((j & CTM_Q_MASK) == CTM_Q_Forward_Tar) + fprintf(stderr,"Tar failed to close properly\n"); + else if ((j & CTM_Q_MASK) == CTM_Q_Forward_SVN) + fprintf(stderr,"Svnadmin failed to close properly\n"); + WRONG + } + if ((j & CTM_Q_MASK) == CTM_Q_Forward_SVN) { + snprintf(svn_command,strlen(name)+127,"svnadmin pack %s\n", name); + if (system(svn_command)) { + fprintf(stderr,"\"%s\" didn't work.", svn_command); + WRONG + } + } + break; default: WRONG } } - /* XXX This should go away. Disallow trailing '/' */ - j = strlen(name)-1; - if(name[j] == '/') name[j] = '\0'; - - /* - * If a filter list is specified, run thru the filter list and - * match `name' against filters. If the name matches, set the - * required action to that specified in the filter. - * The default action if no filterlist is given is to match - * everything. - */ - - match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE); - for (filter = FilterList; filter; filter = filter->Next) { - if (0 == regexec(&filter->CompiledRegex, name, - 0, 0, 0)) { - match = filter->Action; + + if (name) { + /* XXX This should go away. Disallow trailing '/' */ + j = strlen(name)-1; + if(name[j] == '/') name[j] = '\0'; + + /* + * If a filter list is specified, run thru the filter list and + * match `name' against filters. If the name matches, set the + * required action to that specified in the filter. + * The default action if no filterlist is given is to match + * everything. + */ + + match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE); + for (filter = FilterList; filter; filter = filter->Next) { + if (0 == regexec(&filter->CompiledRegex, name, + 0, 0, 0)) { + match = filter->Action; + } } - } - if (CTM_FILTER_DISABLE == match) /* skip file if disabled */ - continue; + if (CTM_FILTER_DISABLE == match) /* skip file if disabled */ + continue; - if (Verbose > 0) + if (Verbose > 0 && strcmp(sp->Key,"SV") && strcmp(sp->Key,"TR")) fprintf(stderr,"> %s %s\n",sp->Key,name); - if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) { - i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666); - if(i < 0) { - warn("%s", name); - WRONG - } - if(cnt != write(i,trash,cnt)) { - warn("%s", name); - WRONG - } - close(i); - if(strcmp(md5,MD5File(name,md5_1))) { - fprintf(stderr," %s %s MD5 didn't come out right\n", - sp->Key,name); - WRONG + if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) { + i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666); + if(i < 0) { + warn("%s", name); + WRONG + } + if(cnt != write(i,trash,cnt)) { + warn("%s", name); + WRONG + } + close(i); + if(strcmp(md5,MD5File(name,md5_1))) { + fprintf(stderr," %s %s MD5 didn't come out right\n", + sp->Key,name); + WRONG + } + if (settime(name,times)) WRONG + continue; } - if (settime(name,times)) WRONG - continue; } + if(!strcmp(sp->Key,"FE")) { ed = popen("ed","w"); if(!ed) { @@ -276,6 +323,8 @@ } continue; } + if(!strcmp(sp->Key,"TR") || !strcmp(sp->Key,"SV")) + continue; WRONG } diff -ur /usr/src/usr.sbin/ctm/ctm/ctm_syntax.c ctm/ctm_syntax.c --- /usr/src/usr.sbin/ctm/ctm/ctm_syntax.c 2010-04-24 17:19:38.000000000 -0500 +++ ctm/ctm_syntax.c 2012-09-09 08:42:32.000000000 -0500 @@ -20,16 +20,21 @@ #define MD5 CTM_F_MD5 #define Count CTM_F_Count #define Bytes CTM_F_Bytes +#define Release CTM_F_Release +#define Forward CTM_F_Forward /* The qualifiers... */ #define File CTM_Q_Name_File #define Dir CTM_Q_Name_Dir +#define Svnbase CTM_Q_Name_Svnbase #define New CTM_Q_Name_New #define Subst CTM_Q_Name_Subst #define After CTM_Q_MD5_After #define Before CTM_Q_MD5_Before #define Chunk CTM_Q_MD5_Chunk #define Force CTM_Q_MD5_Force +#define Tar CTM_Q_Forward_Tar +#define SVN CTM_Q_Forward_SVN static int ctmFM[] = /* File Make */ { Name|File|New|Subst, Uid, Gid, Mode, @@ -55,6 +60,12 @@ static int ctmDR[] = /* Directory Remove */ { Name|Dir, 0 }; +static int ctmTR[] = /* Forward to tar */ + { Count, Forward|Tar, 0 }; + +static int ctmSV[] = /* Forward to svnadmin load */ + { Name|Dir|Svnbase, Release, Count, Forward|SVN, 0 }; + struct CTM_Syntax Syntax[] = { { "FM", ctmFM }, { "FS", ctmFS }, @@ -64,4 +75,6 @@ { "AS", ctmAS }, { "DM", ctmDM }, { "DR", ctmDR }, + { "TR", ctmTR }, + { "SV", ctmSV }, { 0, 0} };