]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/disk/9660/write.c
merge
[plan9front.git] / sys / src / cmd / disk / 9660 / write.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <libsec.h>
5
6 #include "iso9660.h"
7
8 static void
9 writelittlebig4(uchar *buf, ulong x)
10 {
11         buf[0] = buf[7] = x;
12         buf[1] = buf[6] = x>>8;
13         buf[2] = buf[5] = x>>16;
14         buf[3] = buf[4] = x>>24;
15 }
16
17 void
18 rewritedot(Cdimg *cd, Direc *d)
19 {
20         uchar buf[Blocksize];
21         Cdir *c;
22
23         Creadblock(cd, buf, d->block, Blocksize);
24         c = (Cdir*)buf;
25         assert(c->len != 0);
26         assert(c->namelen == 1 && c->name[0] == '\0');  /* dot */
27         writelittlebig4(c->dloc, d->block);
28         writelittlebig4(c->dlen, d->length);
29
30         Cwseek(cd, (vlong)d->block * Blocksize);
31         Cwrite(cd, buf, Blocksize);
32 }
33
34 void
35 rewritedotdot(Cdimg *cd, Direc *d, Direc *dparent)
36 {
37         uchar buf[Blocksize];
38         Cdir *c;
39
40         Creadblock(cd, buf, d->block, Blocksize);
41         c = (Cdir*)buf;
42         assert(c->len != 0);
43         assert(c->namelen == 1 && c->name[0] == '\0');  /* dot */
44
45         c = (Cdir*)(buf+c->len);
46         assert(c->len != 0);
47         assert(c->namelen == 1 && c->name[0] == '\001');        /* dotdot*/
48
49         writelittlebig4(c->dloc, dparent->block);
50         writelittlebig4(c->dlen, dparent->length);
51
52         Cwseek(cd, (vlong)d->block * Blocksize);
53         Cwrite(cd, buf, Blocksize);
54 }
55
56 /*
57  * Write each non-directory file.  We copy the file to
58  * the cd image, and then if it turns out that we've
59  * seen this stream of bits before, we push the next block
60  * pointer back.  This ensures consistency between the MD5s
61  * and the data on the CD image.  MD5 summing on one pass
62  * and copying on another would not ensure this.
63  */
64 void
65 writefiles(Dump *d, Cdimg *cd, Direc *direc)
66 {
67         int i;
68         uchar buf[8192], digest[MD5dlen];
69         ulong length, n, start;
70         Biobuf *b;
71         DigestState *s;
72         Dumpdir *dd;
73
74         if(direc->mode & DMDIR) {
75                 for(i=0; i<direc->nchild; i++)
76                         writefiles(d, cd, &direc->child[i]);
77                 return;
78         }
79
80         assert(direc->block == 0);
81
82         if((b = Bopen(direc->srcfile, OREAD)) == nil){
83                 fprint(2, "warning: cannot open '%s': %r\n", direc->srcfile);
84                 direc->block = 0;
85                 direc->length = 0;
86                 return;
87         }
88
89         start = cd->nextblock;
90         assert(start != 0);
91         if(blocksize && start%blocksize)
92                 start += blocksize-start%blocksize;
93
94         Cwseek(cd, (vlong)start * Blocksize);
95         
96         s = md5(nil, 0, nil, nil);
97         length = 0;
98         while((n = Bread(b, buf, sizeof buf)) > 0) {
99                 md5(buf, n, nil, s);
100                 Cwrite(cd, buf, n);
101                 length += n;
102         }
103         md5(nil, 0, digest, s);
104         Bterm(b);
105         Cpadblock(cd);
106
107         if(length != direc->length) {
108                 fprint(2, "warning: %s changed size underfoot\n", direc->srcfile);
109                 direc->length = length;
110         }
111
112         if(length == 0)
113                 direc->block = 0;
114         else if((dd = lookupmd5(d, digest))) {
115                 assert(dd->length == length);
116                 assert(dd->block != 0);
117                 direc->block = dd->block;
118                 cd->nextblock = start;
119         } else {
120                 direc->block = start;
121                 if(chatty > 1)
122                         fprint(2, "lookup %.16H %lud (%s) failed\n", digest, length, direc->name);
123                 insertmd5(d, atom(direc->name), digest, start, length);
124         }
125 }
126
127 /*
128  * Write a directory tree.  We work from the leaves, 
129  * and patch the dotdot pointers afterward.
130  */
131 static void
132 _writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level)
133 {
134         int i, l, ll;
135         ulong start, next;
136
137         if((d->mode & DMDIR) == 0)
138                 return;
139
140         if(chatty)
141                 fprint(2, "%*s%s\n", 4*level, "", d->name);
142
143         for(i=0; i<d->nchild; i++)
144                 _writedirs(cd, &d->child[i], put, level+1);
145
146         l = 0;
147         l += put(cd, d, (level == 0) ? DTrootdot : DTdot, 0, l);
148         l += put(cd, nil, DTdotdot, 0, l);
149         for(i=0; i<d->nchild; i++)
150                 l += put(cd, &d->child[i], DTiden, 0, l);
151
152         start = cd->nextblock;
153         cd->nextblock += (l+Blocksize-1)/Blocksize;
154         next = cd->nextblock;
155
156         Cwseek(cd, (vlong)start * Blocksize);
157         ll = 0;
158         ll += put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, ll);
159         ll += put(cd, nil, DTdotdot, 1, ll);
160         for(i=0; i<d->nchild; i++)
161                 ll += put(cd, &d->child[i], DTiden, 1, ll);
162         assert(ll == l);
163         Cpadblock(cd);
164         assert(Cwoffset(cd) == (vlong)next * Blocksize);
165
166         d->block = start;
167         d->length = (vlong)(next - start) * Blocksize;
168         rewritedot(cd, d);
169         rewritedotdot(cd, d, d);
170
171         for(i=0; i<d->nchild; i++)
172                 if(d->child[i].mode & DMDIR)
173                         rewritedotdot(cd, &d->child[i], d);
174 }
175
176 void
177 writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int))
178 {
179         /*
180          * If we're writing a mk9660 image, then the root really
181          * is the root, so start at level 0.  If we're writing a dump image,
182          * then the "root" is really going to be two levels down once
183          * we patch in the dump hierarchy above it, so start at level non-zero.
184          */
185         if(chatty)
186                 fprint(2, ">>> writedirs\n");
187         _writedirs(cd, d, put, mk9660 ? 0 : 1);
188 }
189
190
191 /*
192  * Write the dump tree.  This is like writedirs but once we get to
193  * the roots of the individual days we just patch the parent dotdot blocks.
194  */
195 static void
196 _writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level)
197 {
198         int i;
199         ulong start;
200
201         switch(level) {
202         case 0:
203                 /* write root, list of years, also conform.map */
204                 for(i=0; i<d->nchild; i++)
205                         if(d->child[i].mode & DMDIR)
206                                 _writedumpdirs(cd, &d->child[i], put, level+1);
207                 chat("write dump root dir at %lud\n", cd->nextblock);
208                 goto Writedir;
209
210         case 1: /* write year, list of days */
211                 for(i=0; i<d->nchild; i++)
212                         _writedumpdirs(cd, &d->child[i], put, level+1);
213                 chat("write dump %s dir at %lud\n", d->name, cd->nextblock);
214                 goto Writedir;
215
216         Writedir:
217                 start = cd->nextblock;
218                 Cwseek(cd, (vlong)start * Blocksize);
219
220                 put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, Cwoffset(cd));
221                 put(cd, nil, DTdotdot, 1, Cwoffset(cd));
222                 for(i=0; i<d->nchild; i++)
223                         put(cd, &d->child[i], DTiden, 1, Cwoffset(cd));
224                 Cpadblock(cd);
225
226                 d->block = start;
227                 d->length = (vlong)(cd->nextblock - start) * Blocksize;
228
229                 rewritedot(cd, d);
230                 rewritedotdot(cd, d, d);
231
232                 for(i=0; i<d->nchild; i++)
233                         if(d->child[i].mode & DMDIR)
234                                 rewritedotdot(cd, &d->child[i], d);
235                 break;
236
237         case 2: /* write day: already written, do nothing */
238                 break;
239
240         default:
241                 assert(0);
242         }
243 }
244
245 void
246 writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int))
247 {
248         _writedumpdirs(cd, d, put, 0);
249 }
250
251 static int
252 Cputplan9(Cdimg *cd, Direc *d, int dot, int dowrite)
253 {
254         int l, n;
255
256         if(dot != DTiden)
257                 return 0;
258
259         l = 0;
260         if(d->flags & Dbadname) {
261                 n = strlen(d->name);
262                 l += 1+n;
263                 if(dowrite) {
264                         Cputc(cd, n);
265                         Cputs(cd, d->name, n);
266                 }
267         } else {
268                 l++;
269                 if(dowrite)
270                         Cputc(cd, 0);
271         }
272
273         n = strlen(d->uid);
274         l += 1+n;
275         if(dowrite) {
276                 Cputc(cd, n);
277                 Cputs(cd, d->uid, n);
278         }
279
280         n = strlen(d->gid);
281         l += 1+n;
282         if(dowrite) {
283                 Cputc(cd, n);
284                 Cputs(cd, d->gid, n);
285         }
286
287         if(l & 1) {
288                 l++;
289                 if(dowrite)
290                         Cputc(cd, 0);
291         }
292         l += 8;
293         if(dowrite)
294                 Cputn(cd, d->mode, 4);
295
296         return l;
297 }
298
299 /*
300  * Write a directory entry.
301  */
302 static int
303 genputdir(Cdimg *cd, Direc *d, int dot, int joliet, int dowrite, int offset)
304 {
305         int f, n, l, lp;
306         vlong o;
307
308         f = 0;
309         if(dot != DTiden || (d->mode & DMDIR))
310                 f |= 2;
311
312         n = 1;
313         if(dot == DTiden) {
314                 if(joliet)
315                         n = 2*utflen(d->confname);
316                 else
317                         n = strlen(d->confname);
318         }
319
320         l = 33+n;
321         if(l & 1)
322                 l++;
323         assert(l <= 255);
324
325         if(joliet == 0) {
326                 if(cd->flags & CDplan9)
327                         l += Cputplan9(cd, d, dot, 0);
328                 else if(cd->flags & CDrockridge)
329                         l += Cputsysuse(cd, d, dot, 0, l);
330                 assert(l <= 255);
331         }
332
333         if(dowrite == 0) {
334                 if(Blocksize - offset%Blocksize < l)
335                         l += Blocksize - offset%Blocksize;
336                 return l;
337         }
338
339         assert(offset%Blocksize == Cwoffset(cd)%Blocksize);
340
341         o = Cwoffset(cd);
342         lp = 0;
343         if(Blocksize - Cwoffset(cd)%Blocksize < l) {
344                 lp = Blocksize - Cwoffset(cd)%Blocksize;
345                 Cpadblock(cd);
346         }
347
348         Cputc(cd, l);                   /* length of directory record */
349         Cputc(cd, 0);                   /* extended attribute record length */
350         if(d) {
351                 if((d->mode & DMDIR) == 0)
352                         assert(d->length == 0 || d->block >= 18);
353
354                 Cputn(cd, d->block, 4);         /* location of extent */
355                 Cputn(cd, d->length, 4);                /* data length */
356         } else {
357                 Cputn(cd, 0, 4);
358                 Cputn(cd, 0, 4);
359         }
360         Cputdate(cd, d ? d->mtime : now);               /* recorded date */
361         Cputc(cd, f);                   /* file flags */
362         Cputc(cd, 0);                   /* file unit size */
363         Cputc(cd, 0);                   /* interleave gap size */
364         Cputn(cd, 1, 2);                /* volume sequence number */
365         Cputc(cd, n);                   /* length of file identifier */
366
367         if(dot == DTiden) {             /* identifier */
368                 if(joliet)
369                         Cputrscvt(cd, d->confname, n);
370                 else
371                         Cputs(cd, d->confname, n);
372         }else
373         if(dot == DTdotdot)
374                 Cputc(cd, 1);
375         else
376                 Cputc(cd, 0);
377
378         if(Cwoffset(cd) & 1)                    /* pad */
379                 Cputc(cd, 0);
380
381         if(joliet == 0) {
382                 if(cd->flags & CDplan9)
383                         Cputplan9(cd, d, dot, 1);
384                 else if(cd->flags & CDrockridge)
385                         Cputsysuse(cd, d, dot, 1, Cwoffset(cd)-(o+lp));
386         }
387
388         assert(o+lp+l == Cwoffset(cd));
389         return lp+l;
390 }
391
392 int
393 Cputisodir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset)
394 {
395         return genputdir(cd, d, dot, 0, dowrite, offset);
396 }
397
398 int
399 Cputjolietdir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset)
400 {
401         return genputdir(cd, d, dot, 1, dowrite, offset);
402 }
403
404 void
405 Cputendvd(Cdimg *cd)
406 {
407         Cputc(cd, 255);                         /* volume descriptor set terminator */
408         Cputs(cd, "CD001", 5);                  /* standard identifier */
409         Cputc(cd, 1);                           /* volume descriptor version */
410         Cpadblock(cd);
411 }