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