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