]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/hgfs/revlog.c
hgfs: get previous file revisions with appending .n or .revn
[plan9front.git] / sys / src / cmd / hgfs / revlog.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "dat.h"
5 #include "fns.h"
6
7 int
8 fmktemp(void)
9 {
10         char temp[MAXPATH];
11
12         snprint(temp, sizeof(temp), "/tmp/hgXXXXXXXXXXX");
13         return create(mktemp(temp), OTRUNC|ORCLOSE|ORDWR, 0666);
14 }
15
16 void
17 revlogupdate(Revlog *r)
18 {
19         uchar buf[64];
20         Revmap *m;
21         int rev;
22
23         if(seek(r->ifd, r->ioff, 0) < 0)
24                 return;
25         for(rev=r->nmap;;rev++){
26                 if(readn(r->ifd, buf, sizeof(buf)) != sizeof(buf))
27                         break;
28                 if((rev % 16) == 0)
29                         r->map = realloc(r->map, sizeof(r->map[0])*(rev+16));
30                 m = &r->map[rev];
31                 memset(m, 0, sizeof(*m));
32                 m->hoff = (vlong)buf[0]<<40 | 
33                         (vlong)buf[1]<<32 |
34                         (vlong)buf[2]<<24 | 
35                         (vlong)buf[3]<<16 | 
36                         (vlong)buf[4]<<8 | 
37                         (vlong)buf[5];
38                 if(rev == 0)
39                         m->hoff &= 0xFFFF;
40                 m->rev = rev;
41                 m->flags = buf[6]<<8 | buf[7];
42                 m->hlen = buf[8]<<24 | buf[9]<<16 | buf[10]<<8 | buf[11];
43                 m->flen = buf[12]<<24 | buf[13]<<16 | buf[14]<<8 | buf[15];
44                 m->baserev = buf[16]<<24 | buf[17]<<16 | buf[18]<<8 | buf[19];
45                 m->linkrev = buf[20]<<24 | buf[21]<<16 | buf[22]<<8 | buf[23];
46                 m->p1rev = buf[24]<<24 | buf[25]<<16 | buf[26]<<8 | buf[27];
47                 m->p2rev = buf[28]<<24 | buf[29]<<16 | buf[30]<<8 | buf[31];
48                 memmove(m->hash, buf+32, HASHSZ);
49
50                 if(r->dfd < 0){
51                         m->hoff = seek(r->ifd, 0, 1);
52                         r->ioff = seek(r->ifd, m->hlen, 1);
53                 } else
54                         r->ioff = seek(r->ifd, 0, 1);
55         }
56         r->nmap = rev;
57 }
58
59 int
60 revlogopen(Revlog *r, char *path, int mode)
61 {
62         r->ifd = -1;
63         r->dfd = -1;
64         path = smprint("%s.i", path);
65         if((r->ifd = open(path, mode)) < 0){
66                 free(path);
67                 return -1;
68         }
69         path[strlen(path)-1] = 'd';
70         r->dfd = open(path, mode);
71
72         path[strlen(path)-2] = 0;
73         r->path = path;
74
75         r->ioff = 0;
76         r->nmap = 0;
77         r->map = nil;
78         revlogupdate(r);
79         return 0;
80 }
81
82 void
83 revlogclose(Revlog *r)
84 {
85         if(r->ifd >= 0){
86                 close(r->ifd);
87                 r->ifd = -1;
88         }
89         if(r->dfd >= 0){
90                 close(r->dfd);
91                 r->dfd = -1;
92         }
93         free(r->map);
94         r->map = nil;
95         r->nmap = 0;
96         free(r->path);
97 }
98
99 uchar*
100 revhash(Revlog *r, int rev)
101 {
102         static uchar nullid[HASHSZ];
103         if(rev < 0 || rev >= r->nmap)
104                 return nullid;
105         return r->map[rev].hash;
106 }
107
108 int
109 hashrev(Revlog *r, uchar hash[])
110 {
111         int rev;
112
113         for(rev=0; rev<r->nmap; rev++)
114                 if(memcmp(r->map[rev].hash, hash, HASHSZ) == 0)
115                         return rev;
116         return -1;
117 }
118
119 static int
120 prevlogrev(Revlog *r, int rev)
121 {
122         if(r->map[rev].baserev == rev)
123                 return -1;
124         return rev-1;
125 }
126
127 static int
128 prevbundlerev(Revlog *r, int rev)
129 {
130         if(r->map[rev].baserev == rev)
131                 return -1;
132         return r->map[rev].p1rev;
133 }
134
135 static Revmap**
136 getchain1(Revlog *r, int rev, int *count, int (*next)(Revlog *, int))
137 {
138         Revmap **chain;
139
140         if(rev < 0 || rev >= r->nmap){
141                 if(*count <= 0)
142                         return nil;
143                 chain = malloc(sizeof(chain[0]) * ((*count)+1));
144                 chain[*count] = nil;
145                 *count = 0;
146         }else{
147                 (*count)++;
148                 if(chain = getchain1(r, (*next)(r, rev), count, next))
149                         chain[(*count)++] = &r->map[rev];
150         }
151         return chain;
152 }
153
154 static Revmap**
155 getchain(Revlog *r, int rev, int (*next)(Revlog *, int))
156 {
157         int count = 0;
158         return getchain1(r, rev, &count, next);
159 }
160
161 int
162 revlogextract(Revlog *r, int rev, int ofd)
163 {
164         int err, hfd, pfd, bfd;
165         uchar hash[HASHSZ];
166         Revmap **chain, *m;
167         char buf[32];
168         vlong off;
169         int i;
170
171         err = -1;
172         bfd = -1;
173         pfd = -1;
174
175         if((chain = getchain(r, rev, prevlogrev)) == nil){
176                 werrstr("bad patch chain");
177                 goto errout;
178         }
179
180         off = seek(ofd, 0, 1);
181         if(off < 0){
182                 werrstr("seek outfile: %r");
183                 goto errout;
184         }
185         hfd = r->dfd < 0 ? r->ifd : r->dfd;
186         for(i=0; m = chain[i]; i++){
187                 if(seek(hfd, m->hoff, 0) < 0){
188                         werrstr("seek index: %r");
189                         goto errout;
190                 }
191                 if(m == chain[0] && m->baserev == m->rev){
192                         if(chain[1]){
193                                 if(bfd >= 0 && bfd != ofd)
194                                         close(bfd);
195                                 if((bfd = fmktemp()) < 0){
196                                         werrstr("create basefile: %r");
197                                         goto errout;
198                                 }
199                         } else
200                                 bfd = ofd;
201                         if(funzip(bfd, hfd, m->hlen) < 0){
202                                 werrstr("unzip basefile: %r");
203                                 goto errout;
204                         }
205                 } else {
206                         if(pfd < 0){
207                                 if((pfd = fmktemp()) < 0){
208                                         werrstr("create patchfile: %r");
209                                         goto errout;
210                                 }
211                         }
212
213                         /* put a mark before the patch data */
214                         snprint(buf, sizeof(buf), "%H", m->hash);
215                         if(fpatchmark(pfd, buf) < 0){
216                                 werrstr("patchmark: %r");
217                                 goto errout;
218                         }
219
220                         if(funzip(pfd, hfd, m->hlen) < 0){
221                                 werrstr("unzip patchfile: %r");
222                                 goto errout;
223                         }
224                 }
225         }
226         m = chain[i-1];
227
228         if(pfd >= 0 && bfd >= 0 && bfd != ofd){
229                 if(seek(pfd, 0, 0) < 0){
230                         werrstr("seek patchfile: %r");
231                         goto errout;
232                 }
233                 if(fpatch(ofd, bfd, pfd) < 0){
234                         werrstr("patch: %r");
235                         goto errout;
236                 }
237         }
238
239         if(seek(ofd, off, 0) < 0){
240                 werrstr("seek outfile: %r");
241                 goto errout;
242         }
243         if(fhash(ofd, revhash(r, m->p1rev), revhash(r, m->p2rev), hash) < 0){
244                 werrstr("hash outfile: %r");
245                 goto errout;
246         }
247         if(memcmp(m->hash, hash, HASHSZ)){
248                 werrstr("got bad hash");
249                 goto errout;
250         }
251         err = 0;
252
253 errout:
254         if(pfd >= 0)
255                 close(pfd);
256         if(bfd >= 0 && bfd != ofd)
257                 close(bfd);
258         free(chain);
259
260         return err;
261 }
262
263 int
264 revlogopentemp(Revlog *r, int rev)
265 {
266         int fd;
267
268         if((fd = fmktemp()) < 0)
269                 return -1;
270         if(revlogextract(r, rev, fd) < 0){
271                 close(fd);
272                 return -1;
273         }
274         if(seek(fd, 0, 0) < 0){
275                 close(fd);
276                 return -1;
277         }
278         return fd;
279 }