From owner-ctm-announce Mon Jun 10 08:36:11 1996 Return-Path: owner-announce Received: (from root@localhost) by freefall.freebsd.org (8.7.5/8.7.3) id IAA17162 for ctm-announce-outgoing; Mon, 10 Jun 1996 08:36:11 -0700 (PDT) Received: from critter.tfs.com ([140.145.16.108]) by freefall.freebsd.org (8.7.5/8.7.3) with ESMTP id IAA17157 for ; Mon, 10 Jun 1996 08:36:07 -0700 (PDT) Received: from critter.tfs.com (localhost [127.0.0.1]) by critter.tfs.com (8.7.5/8.7.3) with ESMTP id IAA01976 for ; Mon, 10 Jun 1996 08:35:36 -0700 (PDT) To: ctm-announce@freebsd.org Subject: CTM: You may want to look at this... Date: Mon, 10 Jun 1996 08:35:35 -0700 Message-ID: <1974.834420935@critter.tfs.com> From: Poul-Henning Kamp Sender: owner-announce@freebsd.org X-Loop: FreeBSD.org Precedence: bulk Danny Zerkel has too much time on his hands, I'm sure you guys will appreciate that he spends it on CTM. I certainly do :-) Much appreciated Danny! Comments to Danny and me please Poul-Henning ------- Forwarded Message Return-Path: dzerkel@cas.org Received: from tfs.com (tfs.com [140.145.250.1]) by freefall.freebsd.org (8.7.5/8.7.3) with SMTP id FAA29899 for ; Mon, 10 Jun 1996 05:50:42 -0700 (PDT) Received: from srv01s4 by tfs.com (smail3.1.28.1) with SMTP id m0uT6Qa-0003vuC; Mon, 10 Jun 96 05:50 PDT Date: Mon, 10 Jun 1996 08:50:36 -0400 From: dzerkel@cas.org (Danny Zerkel - D26) Message-Id: <9606101250.AA25788@cas.org> To: phk@critter.tfs.com Subject: CTM changes Content-Type: X-sun-attachment - ---------- X-Sun-Data-Type: text X-Sun-Data-Description: text X-Sun-Data-Name: text X-Sun-Charset: us-ascii X-Sun-Content-Lines: 23 Poul-Henning, Life has kept me busy, lately. I have finally gotten back to CTM, however. Here is my new version. It includes the -e reg_expr option, plus -x reg_expr, - -l, and -k. The -x adds a regular expression which, if found, will remove the file from the list. All -e's are processed before the -x's. The -l option causes CTM to list the actions and filenames in the input files only. (This is a good way to verify -e's and -x's.) The -k option prohibits removes of files or directories. Danny J. Zerkel - ---------- X-Sun-Data-Type: default-app X-Sun-Data-Description: default X-Sun-Data-Name: ctm.diff X-Sun-Charset: us-ascii X-Sun-Content-Lines: 1054 - --- ctm.1.ctm Sun May 5 20:02:11 1996 +++ ctm.1 Sun Jun 9 23:21:33 1996 @@ -10,7 +10,7 @@ .\" .\" CTM and ctm(1) by .\" - -.\" $Id: ctm.1,v 1.2.4.1 1996/01/31 02:01:56 nate Exp $ +.\" $Id: ctm.1,v 1.5 1996/04/29 21:02:26 phk Exp $ .\" .Dd Mar 25, 1995 .Os @@ -20,8 +20,12 @@ .Nd source code mirror program .Sh SYNOPSIS .Nm ctm - -.Op Fl cFpPqv +.Op Fl cklFquv +.Op Fl b Ar basedir +.Op Fl e Ar reg_expr . . . +.Op Fl x Ar reg_expr . . . .Op Fl T Ar tmpdir +.Op Fl V Ar level .Ar file Op ... .Sh DESCRIPTION .Nm Ctm @@ -53,15 +57,23 @@ command runs in a number of passes. It will process the entire input file in each pass, before commencing with the next pass. - -Pass 1 will validate that the input file is OK. The syntax, the data +Before working on a file +.Ar name +.Nm ctm +first checks for the existence of the file +.Ar name.ctm . +If this file exists, +.Nm ctm +works on it instead. + +Pass 1 will verify that the input file is okay. The syntax, the data and the global MD5 checksum will be checked. If any of these fail, .Nm ctm - -will never be able to do anything with the file, so it will simply - -reject it. +will simply reject the input file. Pass 2 will validate that the directory tree is in the state expected by the CTM delta. This is done by looking for files and directories which - -should/should not exists and by checking the MD5 checksums of files. +should/should not exist and by checking the MD5 checksums of files. Pass 3 will actually apply the delta. @@ -74,20 +86,49 @@ are explicitly prohibited as a security measure. .Ss Options - - .Bl -tag -width indent -compact +.It Fl b Ar basedir +Prepend the path +.Ar basedir +to every filename. + .It Fl c Check it out, don't do anything. +.It Fl e Ar reg_expr +Match each name in the CTM file against +.Ar reg_expr , +if it matches, +process the file, otherwise skip it. There many be any number of these +expressions, included as seperate arguments. Use of this option +disables the +.Pa .ctm_status +sequence number checks. The expression '^usr.sbin/ctm', for example, +will select the +.Nm ctm +source directory and all files and directories under it. + +.It Fl x Ar reg_expr +Not match each name in the input file against +.Ar reg_expr , +if it doesn't match, +process the file, otherwise skip it. Like +.Fl e , +there may be many of these. Note that all matches ( +.Fl e ) +are tested then all the not matches ( +.Fl x ) . + .It Fl F Force. - -.It Fl p - -Less paranoid. +.It Fl k +Keep files and directories. Ignore removes for files and directories. - -.It Fl P - -Paranoid. +.It Fl l +Just list the actions and filenames. Especially useful for checking that +matches are working correctly, before actually using them. .It Fl q Tell us less. @@ -96,8 +137,17 @@ Put temporary files under .Ar tmpdir . +.It Fl u +Set modification time of created and modified files to the CTM delta +creation time. + .It Fl v +Tell us more. Multiple use increases verbosity. + +.It Fl V Ar level Tell us more. +.Ar Level +is the level of verbosity. .El @@ -120,9 +170,10 @@ .Sh DIAGNOSTICS - -Numerous messages, hopefully self-explaining. The +Numerous messages, hopefully self-explanatory. The .Dq noise level can be adjusted with the +.Fl V , .Fl q and .Fl v @@ -131,10 +182,11 @@ .Sh SEE ALSO .Xr ctm 5 +.Xr re_format 7 .Sh HISTORY - -Initial trials ran during the FreeBSD 1.1.5, and many bugs and +Initial trials were run during the work on FreeBSD 1.1.5, and many bugs and methods were hashed out. The - --- ctm.5.ctm Sun May 5 23:10:45 1996 +++ ctm.5 Mon May 6 22:15:44 1996 @@ -123,6 +123,18 @@ .Xr diff 1 -n script which should be applied to the file in question. +.It \&FE Ar name uid gid mode md5before md5after count + +.Em OBSOLETE +.Pp +Edit the file +.Ar name +using +.Xr ed 1 . +The arguments are as above, but the data sections contains an +.Xr diff 1 +-e script which should be applied to the file in question. + .It \&FR Ar name md5 Remove the file - --- ctm.c.ctm Sun May 5 19:17:28 1996 +++ ctm.c Sun Jun 9 22:23:29 1996 @@ -6,7 +6,7 @@ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * - - * $Id: ctm.c,v 1.11 1995/05/30 03:47:19 rgrimes Exp $ + * $Id: ctm.c,v 1.13 1996/04/29 21:02:28 phk Exp $ * * This is the client program of 'CTM'. It will apply a CTM-patch to a * collection of files. @@ -14,7 +14,6 @@ * Options we'd like to see: * * -a Attempt best effort. - - * -b Base-dir * -B Backup to tar-file. * -d Debug TBD. * -m Email me instead. @@ -22,13 +21,22 @@ * -R Read list of files to reconstruct. * * Options we have: + * -b Base-dir for update. * -c Check it out, don't do anything. + * -e Regular expression match on file names. + * -x Regular expression unmatch on file names. * -F Force - - * -p Less paranoid. - - * -P Paranoid. + * -k Keep files and directories that would have been removed. + * -l List actions and filenames only. * -q Tell us less. * -T . Temporary files. + * -u Set all file modification times to the timestamp. * -v Tell us more. + * -V Tell us more level = number of -v. + * + * Options we don't actually use: + * -p Less paranoid. + * -P Paranoid. * */ @@ -46,23 +54,74 @@ int c; extern int optopt,optind; extern char * optarg; - - FILE *statfile; unsigned applied = 0; + FILE *statfile; + u_char * basedir; + struct CTM_expr *last_expr = 0; + struct CTM_expr *last_nexpr = 0; + struct CTM_expr *new_expr = 0; + int err; + basedir = NULL; Verbose = 1; Paranoid = 1; + SetTime = 0; + ListIt = 0; + KeepIt = 0; setbuf(stderr,0); setbuf(stdout,0); - - while((c=getopt(argc,argv,"ab:B:cd:Fm:pPqr:R:T:Vv")) != -1) { + while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:T:uV:vx:")) != -1) { switch (c) { + case 'b': basedir = optarg; break; /* Base Directory */ case 'c': CheckIt++; break; /* Only check it */ + case 'k': KeepIt++; break; /* Don't do removes */ + case 'l': ListIt++; break; /* Only list actions and files*/ case 'p': Paranoid--; break; /* Less Paranoid */ case 'P': Paranoid++; break; /* More Paranoid */ case 'q': Verbose--; break; /* Quiet */ case 'v': Verbose++; break; /* Verbose */ case 'T': TmpDir = optarg; break; case 'F': Force = 1; break; + case 'u': SetTime++; break; /* Set timestamp on files */ + case 'e': + case 'x': + new_expr = (struct CTM_expr*) Malloc(sizeof(struct CTM_expr)); + if (new_expr == 0) { + fprintf(stderr,"Out of memory for expressions: %s\n", + optarg); + stat++; + break; + } + (void) memset(new_expr, 0, sizeof(struct CTM_expr)); + err = regcomp(&new_expr->cmp_ex, optarg, REG_NOSUB); + if (err != 0) { + char errmsg[128]; + + regerror(err, &new_expr->cmp_ex, errmsg, sizeof(errmsg)); + fprintf(stderr, "Regular expression: %s\n", errmsg); + stat++; + break; + } + if (c == 'e') { + if (last_expr == 0) { + expr_list = new_expr; + } else { + last_expr->next = new_expr; + } + last_expr = new_expr; + } else { + if (last_nexpr == 0) { + nexpr_list = new_expr; + } else { + last_nexpr->next = new_expr; + } + last_nexpr = new_expr; + } + break; + case 'V': sscanf(optarg,"%d", &c); /* Verbose */ + Verbose += c; + break; case ':': fprintf(stderr,"Option '%c' requires an argument.\n",optopt); stat++; @@ -85,11 +144,30 @@ argc -= optind; argv += optind; - - if((statfile = fopen(CTM_STATUS, "r")) == NULL) - - fprintf(stderr, "Warning: " CTM_STATUS " not found.\n"); - - else { - - fscanf(statfile, "%*s %u", &applied); - - fclose(statfile); + if (basedir == NULL) { + Buffer = (u_char *)Malloc(BUFSIZ + strlen(SUBSUFF) +1); + CatPtr = Buffer; + *Buffer = '\0'; + } else { + Buffer = (u_char *)Malloc(strlen(basedir)+ BUFSIZ + strlen(SUBSUFF) +1); + strcpy(Buffer, basedir); + CatPtr = Buffer + strlen(basedir); + if (CatPtr[-1] != '/') { + strcat(Buffer, "/"); + CatPtr++; + } + } + strcat(Buffer, CTM_STATUS); + + if (expr_list || ListIt) { + applied = 0; + } else { + if((statfile = fopen(Buffer, "r")) == NULL) + fprintf(stderr, "Warning: %s not found.\n", Buffer); + else { + fscanf(statfile, "%*s %u", &applied); + fclose(statfile); + } } if(!argc) @@ -103,7 +181,7 @@ if(stat == Exit_Done) stat = Exit_OK; - - if(Verbose) + if(Verbose && !(Verbose == 1 && ListIt)) fprintf(stderr,"Exit(%d)\n",stat); return stat; } @@ -119,7 +197,7 @@ p = 0; f = stdin; } else if(p && (!strcmp(p,".gz") || !strcmp(p,".Z"))) { - - p = Malloc(100); + p = alloca(20 + strlen(filename)); strcpy(p,"gunzip < "); strcat(p,filename); f = popen(p,"r"); @@ -136,7 +214,7 @@ if(Verbose > 1) fprintf(stderr,"Working on <%s>\n",filename); - - if(FileName) Free(FileName); + Delete(FileName); FileName = String(filename); /* If we cannot seek, we're doomed, so copy to a tmp-file in that case */ @@ -167,6 +245,19 @@ if((i=Pass1(f, applied))) goto exit_and_close; + if (!Pass1Match) { + if (Verbose > 1) { + fprintf(stderr, "No matches in <%s>.\n", filename); + } + i = Exit_Done; + goto exit_and_close; + } + + if (ListIt) { + i = Exit_Done; + goto exit_and_close; + } + if(!p) { rewind(f); } else { @@ -199,12 +290,11 @@ i=Pass3(f); exit_and_close: - - if(!p) { + if(!p) fclose(f); - - } else { + else pclose(f); - - Free(p); - - } + if(i) return i; - --- ctm.h.ctm Sun May 5 19:08:42 1996 +++ ctm.h Sun Jun 9 22:23:57 1996 @@ -6,7 +6,7 @@ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * - - * $Id: ctm.h,v 1.7 1995/05/30 03:47:21 rgrimes Exp $ + * $Id: ctm.h,v 1.9 1996/04/29 21:02:29 phk Exp $ * */ @@ -17,13 +17,19 @@ #include #include #include +#include #include #include #include +#include +#include #define VERSION "2.0" #define MAXSIZE (1024*1024*10) +#define SUBSUFF ".ctm" +#define TMPSUFF ".ctmtmp" + /* The fields... */ #define CTM_F_MASK 0xff #define CTM_F_Name 0x01 @@ -39,6 +45,7 @@ #define CTM_Q_Name_File 0x0100 #define CTM_Q_Name_Dir 0x0200 #define CTM_Q_Name_New 0x0400 +#define CTM_Q_Name_Subst 0x0800 #define CTM_Q_MD5_After 0x0100 #define CTM_Q_MD5_Before 0x0200 #define CTM_Q_MD5_Chunk 0x0400 @@ -46,13 +53,21 @@ struct CTM_Syntax { char *Key; + char *Name; int *List; }; extern struct CTM_Syntax Syntax[]; +struct CTM_expr { + struct CTM_expr* next; + regex_t cmp_ex; +}; + #define Malloc malloc #define Free free +#define Delete(foo) if (!foo) ; else {Free(foo); foo = 0; } +#define String(foo) strdup(foo) #ifndef EXTERN # define EXTERN extern @@ -63,8 +78,12 @@ EXTERN u_char *TimeStamp; EXTERN u_char *Prefix; EXTERN u_char *FileName; - -EXTERN u_char *BaseDir; EXTERN u_char *TmpDir; +EXTERN u_char *CatPtr; +EXTERN u_char *Buffer; + +EXTERN struct CTM_expr* expr_list; +EXTERN struct CTM_expr* nexpr_list; /* * Paranoid -- Just in case they should be after us... @@ -97,6 +116,11 @@ EXTERN int Exit; EXTERN int Force; EXTERN int CheckIt; +EXTERN int KeepIt; +EXTERN int ListIt; +EXTERN int SetTime; +EXTERN int Pass1Match; +EXTERN struct timeval Times[2]; #define Exit_OK 0 #define Exit_Garbage 1 @@ -108,13 +132,13 @@ #define Exit_Done 64 #define Exit_Version 128 - -char * String(char *s); void Fatal_(int ln, char *fn, char *kind); #define Fatal(foo) Fatal_(__LINE__,__FILE__,foo) #define Assert() Fatal_(__LINE__,__FILE__,"Assert failed.") #define WRONG {Assert(); return Exit_Mess;} 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); @@ -124,6 +148,7 @@ #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 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); int Pass2(FILE *fd); - --- ctm_pass1.c.ctm Sun May 5 22:27:10 1996 +++ ctm_pass1.c Sun Jun 9 22:33:03 1996 @@ -6,7 +6,7 @@ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * - - * $Id: ctm_pass1.c,v 1.10.4.1 1995/09/06 14:20:20 davidg Exp $ + * $Id: ctm_pass1.c,v 1.12 1996/02/05 16:06:50 phk Exp $ * */ @@ -24,8 +24,10 @@ MD5_CTX ctx; int i,j,sep,cnt; u_char *md5=0,*trash=0; + u_char *name = 0; struct CTM_Syntax *sp; int slashwarn=0; + int nomatch=0; unsigned current; char md5_1[33]; @@ -33,6 +35,10 @@ printf("Pass1 -- Checking integrity of incoming CTM-patch\n"); MD5Init (&ctx); + /* Count number of CTM entries matched. If no matches, ctm skips */ + /* pass 2 & 3 for this ctm_file. */ + Pass1Match = 0; + GETFIELD(p,' '); /* CTM_BEGIN */ if(strcmp(p,"CTM_BEGIN")) { Fatal("Probably not a CTM-patch at all."); @@ -55,6 +61,9 @@ GETFIELDCOPY(Prefix,'\n'); /* */ sscanf(Nbr, "%u", ¤t); + if (expr_list || nexpr_list) { + current = 0; /* Ignore status when we have regular expressions */ + } if(current && current <= applied) { if(Verbose) fprintf(stderr,"Delta number %u is already applied; ignoring.\n", @@ -63,9 +72,11 @@ } for(;;) { - - if(md5) {Free(md5), md5 = 0;} - - if(trash) {Free(trash), trash = 0;} + Delete(md5); + Delete(name); + Delete(trash); cnt = -1; + nomatch = 0; GETFIELD(p,' '); /* CTM_something */ @@ -92,31 +103,66 @@ sep = ' '; else sep = '\n'; - - if(Verbose > 5) - - fprintf(stderr," %x(%d)",sp->List[i],sep); + if(Verbose > 5) + fprintf(stderr," %x(%d)",sp->List[i],sep); switch (j & CTM_F_MASK) { case CTM_F_Name: /* XXX check for garbage and .. */ - - GETFIELD(p,sep); - - j = strlen(p); - - if(p[j-1] == '/' && !slashwarn) { + GETFIELDCOPY(name,sep); + j = strlen(name); + if(name[j-1] == '/' && !slashwarn) { fprintf(stderr,"Warning: contains trailing slash\n"); slashwarn++; } - - if (p[0] == '/') { + if (name[0] == '/') { Fatal("Absolute paths are illegal."); return Exit_Mess; } + q = name; for (;;) { - - if (p[0] == '.' && p[1] == '.') - - if (p[2] == '/' || p[2] == '\0') { + if (q[0] == '.' && q[1] == '.') + if (q[2] == '/' || q[2] == '\0') { Fatal("Paths containing '..' are illegal."); return Exit_Mess; } - - if ((p = strchr(p, '/')) == NULL) + if ((q = strchr(q, '/')) == NULL) break; - - p++; + q++; + } + + /* Skip removes... */ + if (KeepIt && + (!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR"))) { + nomatch = 1; + break; + } + + /* Only need to check things that match regular expr's */ + + if (expr_list || nexpr_list) { + struct CTM_expr *expr; + + for (expr = expr_list; expr; expr = expr->next) { + if (regexec(&expr->cmp_ex, name, 0, 0, 0) == 0) { + break; + } + } + if (expr_list && expr == 0) { + nomatch = 1; + break; + } + for (expr = nexpr_list; expr; expr = expr->next) { + if (regexec(&expr->cmp_ex, name, 0, 0, 0) != 0) { + break; + } + } + if (nexpr_list && expr == 0) { + nomatch = 1; + break; + } } + + Pass1Match++; break; case CTM_F_Uid: GETFIELD(p,sep); @@ -182,8 +228,18 @@ } if(Verbose > 5) putc('\n',stderr); - - continue; + if (ListIt && !nomatch) { + if (Pass1Match == 1 && strcmp(FileName, "-")) { + printf("%s\n", FileName); + } + printf("%s: %s\n", sp->Name, name); + } } + + Delete(md5); + Delete(name); + Delete(trash); + q = MD5End (&ctx,md5_1); if(Verbose > 2) printf("Expecting Global MD5 <%s>\n",q); - --- ctm_pass2.c.ctm Sun May 5 21:52:21 1996 +++ ctm_pass2.c Sun Jun 9 22:43:58 1996 @@ -6,7 +6,7 @@ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * - - * $Id: ctm_pass2.c,v 1.8.4.1 1995/09/06 14:20:21 davidg Exp $ + * $Id: ctm_pass2.c,v 1.12 1996/04/29 21:02:30 phk Exp $ * */ @@ -27,6 +27,7 @@ struct CTM_Syntax *sp; struct stat st; int ret = 0; + int matches = 0; char md5_1[33]; if(Verbose>3) @@ -44,10 +45,11 @@ /* XXX drop or use ? */ for(;;) { - - if(trash) {Free(trash), trash = 0;} - - if(name) {Free(name), name = 0;} - - if(md5) {Free(md5), md5 = 0;} + Delete(trash); + Delete(name); + Delete(md5); cnt = -1; + matches = 1; GETFIELD(p,' '); @@ -69,7 +71,39 @@ switch (j & CTM_F_MASK) { case CTM_F_Name: - - GETFIELDCOPY(name,sep); + GETNAMECOPY(name,sep,j,0); + + /* Skip removes... */ + if (KeepIt && + (!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR"))) { + matches = 0; + break; + } + + /* Only need to check things that match regular expr's */ + if (expr_list || nexpr_list) { + struct CTM_expr *expr; + + for (expr = expr_list; expr; expr = expr->next) { + if (regexec(&expr->cmp_ex, name, 0, 0, 0) == 0) { + break; + } + } + if (expr_list && expr == 0) { + matches = 0; + break; + } + for (expr = nexpr_list; expr; expr = expr->next) { + if (regexec(&expr->cmp_ex, name, 0, 0, 0) != 0) { + break; + } + } + if (nexpr_list && expr == 0) { + matches = 0; + break; + } + } + /* XXX Check DR DM rec's for parent-dir */ if(j & CTM_Q_Name_New) { /* XXX Check DR FR rec's for item */ @@ -89,6 +123,12 @@ ret |= Exit_NotOK; break; } + if (SetTime && getuid() && (getuid() != st.st_uid)) { + fprintf(stderr, + " %s: %s not mine, cannot set time.\n", + sp->Key,name); + ret |= Exit_NotOK; + } if (j & CTM_Q_Name_Dir) { if((st.st_mode & S_IFMT) != S_IFDIR) { fprintf(stderr, @@ -116,9 +156,11 @@ case CTM_F_MD5: if(!name) WRONG if(j & CTM_Q_MD5_Before) { + char *tmp; GETFIELD(p,sep); - - if((st.st_mode & S_IFMT) == S_IFREG && - - strcmp(MD5File(name,md5_1),p)) { + if(matches && (st.st_mode & S_IFMT) == S_IFREG && + (tmp = MD5File(name,md5_1)) != NULL && + strcmp(tmp,p)) { fprintf(stderr," %s: %s md5 mismatch.\n", sp->Key,name); if(j & CTM_Q_MD5_Force) { @@ -146,22 +188,26 @@ case CTM_F_Bytes: if(cnt < 0) WRONG GETDATA(trash,cnt); - - if(!strcmp(sp->Key,"FN")) { + if(matches && !strcmp(sp->Key,"FN")) { p = tempnam(TmpDir,"CTMclient"); j = ctm_edit(trash,cnt,name,p); if(j) { fprintf(stderr," %s: %s edit returned %d.\n", sp->Key,name,j); ret |= j; + unlink(p); + Free(p); return ret; } else if(strcmp(md5,MD5File(p,md5_1))) { fprintf(stderr," %s: %s edit fails.\n", sp->Key,name); ret |= Exit_Mess; + unlink(p); + Free(p); return ret; } - - unlink(p); - - free(p); + unlink(p); + Free(p); } break; @@ -169,6 +215,11 @@ } } } + + Delete(trash); + Delete(name); + Delete(md5); + q = MD5End (&ctx,md5_1); GETFIELD(p,'\n'); /* */ if(strcmp(q,p)) WRONG - --- ctm_pass3.c.ctm Sun May 5 20:02:58 1996 +++ ctm_pass3.c Sun Jun 9 22:26:15 1996 @@ -6,7 +6,7 @@ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * - - * $Id: ctm_pass3.c,v 1.10.4.1 1995/09/06 14:20:22 davidg Exp $ + * $Id: ctm_pass3.c,v 1.13 1996/04/29 21:02:32 phk Exp $ * */ @@ -18,6 +18,17 @@ */ int +settime(const char *name, const struct timeval *times) +{ + if (SetTime) + if (utimes(name,times)) { + fprintf(stderr, " utimes(): %s: %s\n", name, strerror(errno)); + return -1; + } + return 0; +} + +int Pass3(FILE *fd) { u_char *p,*q,buf[BUFSIZ]; @@ -28,6 +39,7 @@ FILE *ed=0; struct stat st; char md5_1[33]; + struct timeval times[2]; if(Verbose>3) printf("Pass3 -- Applying the CTM-patch\n"); @@ -40,14 +52,67 @@ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG + /* + * This would be cleaner if mktime() worked in UTC rather than + * local time. + */ + if (SetTime) { + struct tm tm; + char *tz; + char buf[5]; + int i; + +#define SUBSTR(off,len) strncpy(buf, &TimeStamp[off], len), buf[len] = '\0' +#define WRONGDATE { fprintf(stderr, " %s failed date validation\n",\ + TimeStamp); WRONG} + + if (strlen(TimeStamp) != 15 || TimeStamp[14] != 'Z') WRONGDATE + for (i = 0; i < 14; i++) + if (!isdigit(TimeStamp[i])) WRONGDATE + + tz = getenv("TZ"); + if (setenv("TZ", "UTC", 1) < 0) WRONG + tzset(); + + tm.tm_isdst = tm.tm_gmtoff = 0; + + SUBSTR(0, 4); + tm.tm_year = atoi(buf) - 1900; + SUBSTR(4, 2); + tm.tm_mon = atoi(buf) - 1; + if (tm.tm_mon < 0 || tm.tm_mon > 11) WRONGDATE + SUBSTR(6, 2); + tm.tm_mday = atoi(buf); + if (tm.tm_mday < 1 || tm.tm_mday > 31) WRONG; + SUBSTR(8, 2); + tm.tm_hour = atoi(buf); + if (tm.tm_hour > 24) WRONGDATE + SUBSTR(10, 2); + tm.tm_min = atoi(buf); + if (tm.tm_min > 59) WRONGDATE + SUBSTR(12, 2); + tm.tm_sec = atoi(buf); + if (tm.tm_min > 62) WRONGDATE /* allow leap seconds */ + + times[0].tv_sec = times[1].tv_sec = mktime(&tm); + if (times[0].tv_sec == -1) WRONGDATE + times[0].tv_usec = times[1].tv_usec = 0; + + if (tz) { + if (setenv("TZ", tz, 1) < 0) WRONGDATE + } else { + unsetenv("TZ"); + } + } + for(;;) { - - if(md5) {Free(md5), md5 = 0;} - - if(uid) {Free(uid), uid = 0;} - - if(gid) {Free(gid), gid = 0;} - - if(mode) {Free(mode), mode = 0;} - - if(md5before) {Free(md5before), md5before = 0;} - - if(trash) {Free(trash), trash = 0;} - - if(name) {Free(name), name = 0;} + Delete(md5); + Delete(uid); + Delete(gid); + Delete(mode); + Delete(md5before); + Delete(trash); + Delete(name); cnt = -1; GETFIELD(p,' '); @@ -69,7 +134,7 @@ sep = '\n'; switch (j & CTM_F_MASK) { - - case CTM_F_Name: GETFIELDCOPY(name,sep); break; + case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break; case CTM_F_Uid: GETFIELDCOPY(uid,sep); break; case CTM_F_Gid: GETFIELDCOPY(gid,sep); break; case CTM_F_Mode: GETFIELDCOPY(mode,sep); break; @@ -88,6 +153,32 @@ j = strlen(name)-1; if(name[j] == '/') name[j] = '\0'; + if (expr_list || nexpr_list) { + struct CTM_expr *expr; + + for (expr = expr_list; expr; expr = expr->next) { + if (regexec(&expr->cmp_ex, name, 0, 0, 0) == 0) { + break; + } + } + if (expr_list && expr == 0) { + if (Verbose>3) + fprintf(stderr, "skipped %s %s (matched)\n", sp->Key, name); + continue; + } + for (expr = nexpr_list; expr; expr = expr->next) { + if (regexec(&expr->cmp_ex, name, 0, 0, 0) != 0) { + break; + } + } + if (nexpr_list && expr == 0) { + if (Verbose>3) + fprintf(stderr, "skipped %s %s (not matched)\n", sp->Key, + name); + continue; + } + } + 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); @@ -105,6 +196,7 @@ sp->Key,name); WRONG } + if (settime(name,times)) WRONG continue; } if(!strcmp(sp->Key,"FE")) { @@ -128,11 +220,12 @@ sp->Key,name); WRONG } + if (settime(name,times)) WRONG continue; } if(!strcmp(sp->Key,"FN")) { strcpy(buf,name); - - strcat(buf,".ctm"); + strcat(buf,TMPSUFF); i = ctm_edit(trash,cnt,name,buf); if(i) { fprintf(stderr," %s %s Edit failed with code %d.\n", @@ -145,6 +238,7 @@ sp->Key,name); WRONG } + if (settime(name,times)) WRONG continue; } if(!strcmp(sp->Key,"DM")) { @@ -156,10 +250,15 @@ fprintf(stderr,"<%s> mkdir failed\n",name); WRONG } + if (settime(name,times)) WRONG continue; } if(!strcmp(sp->Key,"FR")) { - - if (0 != unlink(name)) { + if (KeepIt) { + if (Verbose > 1) { + fprintf(stderr, "<%s> kept\n", name); + } + } else if (0 != unlink(name)) { fprintf(stderr,"<%s> unlink failed\n",name); if (!Force) WRONG @@ -171,12 +270,27 @@ * We cannot use rmdir() because we do not get the directories * in '-depth' order (cvs-cur.0018.gz for examples) */ - - sprintf(buf,"rm -rf %s",name); - - system(buf); + if (KeepIt) { + if (Verbose > 1) { + fprintf(stderr, "<%s> kept\n", name); + } + } else { + sprintf(buf,"rm -rf %s",name); + system(buf); + } continue; } WRONG } + + Delete(md5); + Delete(uid); + Delete(gid); + Delete(mode); + Delete(md5before); + Delete(trash); + Delete(name); + q = MD5End (&ctx,md5_1); GETFIELD(p,'\n'); if(strcmp(q,p)) WRONG - --- ctm_syntax.c.ctm Sun Jun 9 17:11:42 1996 +++ ctm_syntax.c Sun Jun 9 17:14:22 1996 @@ -56,12 +56,12 @@ { Name|Dir, 0 }; struct CTM_Syntax Syntax[] = { - - { "FM", ctmFM }, - - { "FS", ctmFS }, - - { "FE", ctmFE }, - - { "FN", ctmFE }, - - { "FR", ctmFR }, - - { "AS", ctmAS }, - - { "DM", ctmDM }, - - { "DR", ctmDR }, + { "FM", "create", ctmFM }, + { "FS", "replace", ctmFS }, + { "FE", "edit", ctmFE }, + { "FN", "modify", ctmFE }, + { "FR", "remove", ctmFR }, + { "AS", "change", ctmAS }, + { "DM", "mkdir", ctmDM }, + { "DR", "rmdir", ctmDR }, { 0, 0} }; ------- End of Forwarded Message