1 #include <u.h> 2 #include <libc.h> 3 4 #define MINUTE(x) ((long)(x)*60L) 5 #define HOUR(x) (MINUTE(x)*60L) 6 #define YEAR(x) (HOUR(x)*24L*360L) 7 8 int verb; 9 int uflag; 10 int force; 11 int diff; 12 int diffb; 13 char* sflag; 14 15 void ysearch(char*, char*); 16 long starttime(char*); 17 void lastbefore(ulong, char*, char*, char*); 18 char* prtime(ulong); 19 20 void 21 main(int argc, char *argv[]) 22 { 23 int i; 24 char *ndump; 25 26 ndump = nil; 27 ARGBEGIN { 28 default: 29 goto usage; 30 case 'v': 31 verb = 1; 32 break; 33 case 'f': 34 force = 1; 35 break; 36 case 'd': 37 ndump = ARGF(); 38 break; 39 case 'D': 40 diff = 1; 41 break; 42 case 'b': 43 diffb = 1; 44 break; 45 case 's': 46 sflag = ARGF(); 47 break; 48 case 'u': 49 uflag = 1; 50 break; 51 } ARGEND 52 53 if(argc == 0) { 54 usage: 55 fprint(2, "usage: history [-bDfuv] [-d 9fsname] [-s yyyymmdd] files\n"); 56 exits(0); 57 } 58 59 for(i=0; i<argc; i++) 60 ysearch(argv[i], ndump); 61 exits(0); 62 } 63 64 void 65 ysearch(char *file, char *ndump) 66 { 67 char fil[400], buf[500], nbuf[100], pair[2][500], *p; 68 Tm *tm; 69 Waitmsg *w; 70 Dir *dir, *d; 71 ulong otime, dt; 72 int toggle, started, missing; 73 74 fil[0] = 0; 75 if(file[0] != '/') { 76 getwd(strchr(fil, 0), 100); 77 strcat(fil, "/"); 78 } 79 strcat(fil, file); 80 if(memcmp(fil, "/n/", 3) == 0){ 81 p = strchr(fil+3, '/'); 82 if(p == nil) 83 p = fil+strlen(fil); 84 if(ndump == nil){ 85 if(p-fil >= sizeof nbuf-10){ 86 fprint(2, "%s: dump name too long", fil); 87 return; 88 } 89 memmove(nbuf, fil+3, p-(fil+3)); 90 nbuf[p-(fil+3)] = 0; 91 strcat(nbuf, "dump"); 92 ndump = nbuf; 93 } 94 memmove(fil, p, strlen(p)+1); 95 } 96 if(ndump == nil) 97 ndump = "dump"; 98 99 tm = localtime(time(0)); 100 snprint(buf, sizeof buf, "/n/%s/%.4d/", ndump, tm->year+1900); 101 if(access(buf, AREAD) < 0) { 102 if(verb) 103 print("mounting dump %s\n", ndump); 104 if(rfork(RFFDG|RFPROC) == 0) { 105 execl("/bin/rc", "rc", "9fs", ndump, 0); 106 exits(0); 107 } 108 w = wait(); 109 if(w == nil){ 110 fprint(2, "history: wait error: %r\n"); 111 exits("wait"); 112 } 113 if(w->msg[0] != '\0'){ 114 fprint(2, "9fs failed: %s\n", w->msg); 115 exits(w->msg); 116 } 117 free(w); 118 } 119 120 started = 0; 121 dir = dirstat(file); 122 if(dir == nil) 123 fprint(2, "history: warning: %s does not exist\n", file); 124 else{ 125 print("%s %s %lld [%s]\n", prtime(dir->mtime), file, dir->length, dir->muid); 126 started = 1; 127 strecpy(pair[1], pair[1]+sizeof pair[1], file); 128 } 129 free(dir); 130 otime = starttime(sflag); 131 toggle = 0; 132 for(;;) { 133 lastbefore(otime, fil, buf, ndump); 134 dir = dirstat(buf); 135 if(dir == nil) { 136 if(!force) 137 return; 138 dir = malloc(sizeof(Dir)); 139 nulldir(dir); 140 dir->mtime = otime + 1; 141 } 142 dt = HOUR(12); 143 missing = 0; 144 while(otime <= dir->mtime){ 145 if(verb) 146 print("backup %ld, %ld\n", dir->mtime, otime-dt); 147 lastbefore(otime-dt, fil, buf, ndump); 148 d = dirstat(buf); 149 if(d == nil){ 150 if(!force) 151 return; 152 if(!missing) 153 print("removed %s\n", buf); 154 missing = 1; 155 }else{ 156 free(dir); 157 dir = d; 158 } 159 dt += HOUR(12); 160 } 161 strcpy(pair[toggle], buf); 162 if(diff && started){ 163 switch(rfork(RFFDG|RFPROC)){ 164 case 0: 165 if(diffb) 166 execl("/bin/diff", "diff", "-nb", pair[toggle ^ 1], pair[toggle], 0); 167 else 168 execl("/bin/diff", "diff", "-n", pair[toggle ^ 1], pair[toggle], 0); 169 fprint(2, "can't exec diff: %r\n"); 170 exits(0); 171 case -1: 172 fprint(2, "can't fork diff: %r\n"); 173 break; 174 default: 175 while(waitpid() != -1) 176 ; 177 break; 178 } 179 } 180 print("%s %s %lld [%s]\n", prtime(dir->mtime), buf, dir->length, dir->muid); 181 toggle ^= 1; 182 started = 1; 183 otime = dir->mtime; 184 free(dir); 185 } 186 } 187 188 void 189 lastbefore(ulong t, char *f, char *b, char *ndump) 190 { 191 Tm *tm; 192 Dir *dir; 193 int vers, try; 194 ulong t0, mtime; 195 int i, n, fd; 196 197 t0 = t; 198 if(verb) 199 print("%ld lastbefore %s\n", t0, f); 200 mtime = 0; 201 for(try=0; try<30; try++) { 202 tm = localtime(t); 203 sprint(b, "/n/%s/%.4d/%.2d%.2d", ndump, 204 tm->year+1900, tm->mon+1, tm->mday); 205 dir = dirstat(b); 206 if(dir){ 207 mtime = dir->mtime; 208 free(dir); 209 } 210 if(dir==nil || mtime > t0) { 211 if(verb) 212 print("%ld earlier %s\n", mtime, b); 213 t -= HOUR(24); 214 continue; 215 } 216 if(strstr(ndump, "snap")){ 217 fd = open(b, OREAD); 218 if(fd < 0) 219 continue; 220 n = dirreadall(fd, &dir); 221 close(fd); 222 if(n == 0) 223 continue; 224 for(i = n-1; i > 0; i--){ 225 if(dir[i].mtime > t0) 226 break; 227 } 228 sprint(b, "/n/%s/%.4d/%.2d%.2d/%s%s", ndump, 229 tm->year+1900, tm->mon+1, tm->mday, dir[i].name, f); 230 free(dir); 231 } else { 232 for(vers=0;; vers++) { 233 sprint(b, "/n/%s/%.4d/%.2d%.2d%d", ndump, 234 tm->year+1900, tm->mon+1, tm->mday, vers+1); 235 dir = dirstat(b); 236 if(dir){ 237 mtime = dir->mtime; 238 free(dir); 239 } 240 if(dir==nil || mtime > t0) 241 break; 242 if(verb) 243 print("%ld later %s\n", mtime, b); 244 } 245 sprint(b, "/n/%s/%.4d/%.2d%.2d%s", ndump, 246 tm->year+1900, tm->mon+1, tm->mday, f); 247 if(vers) 248 sprint(b, "/n/%s/%.4d/%.2d%.2d%d%s", ndump, 249 tm->year+1900, tm->mon+1, tm->mday, vers, f); 250 } 251 return; 252 } 253 strcpy(b, "XXX"); /* error */ 254 } 255 256 char* 257 prtime(ulong t) 258 { 259 static char buf[100]; 260 char *b; 261 Tm *tm; 262 263 if(uflag) 264 tm = gmtime(t); 265 else 266 tm = localtime(t); 267 b = asctime(tm); 268 memcpy(buf, b+4, 24); 269 buf[24] = 0; 270 return buf; 271 } 272 273 long 274 starttime(char *s) 275 { 276 Tm *tm; 277 long t, dt; 278 int i, yr, mo, da; 279 280 t = time(0); 281 if(s == 0) 282 return t; 283 for(i=0; s[i]; i++) 284 if(s[i] < '0' || s[i] > '9') { 285 fprint(2, "bad start time: %s\n", s); 286 return t; 287 } 288 if(strlen(s)==6){ 289 yr = (s[0]-'0')*10 + s[1]-'0'; 290 mo = (s[2]-'0')*10 + s[3]-'0' - 1; 291 da = (s[4]-'0')*10 + s[5]-'0'; 292 if(yr < 70) 293 yr += 100; 294 }else if(strlen(s)==8){ 295 yr = (((s[0]-'0')*10 + s[1]-'0')*10 + s[2]-'0')*10 + s[3]-'0'; 296 yr -= 1900; 297 mo = (s[4]-'0')*10 + s[5]-'0' - 1; 298 da = (s[6]-'0')*10 + s[7]-'0'; 299 }else{ 300 fprint(2, "bad start time: %s\n", s); 301 return t; 302 } 303 t = 0; 304 dt = YEAR(10); 305 for(i=0; i<50; i++) { 306 tm = localtime(t+dt); 307 if(yr > tm->year || 308 (yr == tm->year && mo > tm->mon) || 309 (yr == tm->year && mo == tm->mon) && da > tm->mday) { 310 t += dt; 311 continue; 312 } 313 dt /= 2; 314 if(dt == 0) 315 break; 316 } 317 t += HOUR(12); /* .5 day to get to noon of argument */ 318 return t; 319 } 320