xref: /inferno-os/appl/cmd/install/wrap.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Wrap;
2
3include "sys.m";
4	sys : Sys;
5include "draw.m";
6include "bufio.m";
7	bufio : Bufio;
8	Iobuf : import bufio;
9include "keyring.m";
10	keyring : Keyring;
11include "sh.m";
12include "arch.m";
13	arch : Arch;
14include "wrap.m";
15include "archfs.m";
16
17archpid := -1;
18gzfd: ref Sys->FD;
19gzfile: string;
20
21init(bio: Bufio)
22{
23	sys = load Sys Sys->PATH;
24	if(bio == nil)
25		bufio = load Bufio Bufio->PATH;
26	else
27		bufio = bio;
28	keyring = load Keyring Keyring->PATH;
29	arch = load Arch Arch->PATH;
30	arch->init(bufio);
31}
32
33end()
34{
35	if(gzfile != nil)
36		sys->remove(gzfile);
37	if (archpid > 0){
38		fd := sys->open("#p/" + string archpid + "/ctl", sys->OWRITE);
39		if (fd != nil)
40			sys->fprint(fd, "killgrp");
41	}
42}
43
44archfs(f : string, mtpt : string, all : int, c : chan of int)
45{
46	sys->pctl(Sys->NEWPGRP, nil);
47	cmd := "/dis/install/archfs.dis";
48	m := load Archfs Archfs->PATH;
49	if(m == nil) {
50		c <-= -1;
51		return;
52	}
53	ch := chan of int;
54	if (all)
55		spawn m->initc(cmd :: "-m" :: mtpt :: f :: nil, ch);
56	else
57		spawn m->initc(cmd :: "-s" :: "-m" :: mtpt :: f :: "/wrap" :: nil, ch);
58	pid := <- ch;
59	c <-= pid;
60}
61
62mountarch(f : string, mtpt : string, all : int) : int
63{
64	c := chan of int;
65	spawn archfs(f, mtpt, all, c);
66	pid := <- c;
67	if (pid < 0) {
68		if(pid == -1)
69			sys->fprint(sys->fildes(2), "fatal: cannot run archfs\n");
70		# else probably not an archive file
71		return -1;
72	}
73	archpid = pid;
74	return 0;
75}
76
77openmount(f : string, d : string) : ref Wrapped
78{
79	if (f == nil) {
80		p := d+"/wrap";
81		f = getfirstdir(p);
82		if (f == nil)
83			return nil;
84	}
85	w := ref Wrapped;
86	w.name = f;
87	w.root = d;
88	# p := d + "/wrap/" + f;
89	p := pathcat(d, pathcat("wrap", f));
90	(w.u, w.nu, w.tfull) = openupdate(p);
91	if (w.nu < 0) {
92		closewrap(w);
93		return nil;
94	}
95	return w;
96}
97
98closewrap(w : ref Wrapped)
99{
100	w = nil;
101}
102
103openwraphdr(f : string, d : string, argl : list of string, all : int) : ref Wrapped
104{
105	argl = nil;
106	(ok, dir) := sys->stat(f);
107	if (ok < 0 || dir.mode & Sys->DMDIR)
108		return openwrap(f, d, all);
109	(nf, fd) := arch->openarchgz(f);
110	if (nf != nil) {
111		gzfile = nf;
112		f = nf;
113		gzfd = fd;
114	}
115	return openwrap(f, "/mnt/wrap", all);
116}
117
118openwrap(f : string, d : string, all : int) : ref Wrapped
119{
120	if (d == nil)
121		d = "/";
122	if((w := openmount(f, d)) != nil)
123		return w;		# don't mess about if /wrap/ structure exists
124	(ok, dir) := sys->stat(f);
125	if (ok < 0)
126		return nil;
127	# accept root/ or root/wrap/pkgname
128	if (dir.mode & Sys->DMDIR) {
129		d = f;
130		if ((i := strstr(f, "/wrap/")) >= 0) {
131			f = f[i+6:];
132			d = d[0:i+6];
133		}
134		else
135			f = nil;
136		return openmount(f, d);
137	}
138	(ok, dir) = sys->stat(f);
139	if (ok < 0 || dir.mode & Sys->DMDIR)
140		return openmount(f, d);		# ?
141	if (mountarch(f, d, all) < 0)
142		return nil;
143	return openmount(nil, d);
144}
145
146getfirstdir(d : string) : string
147{
148	if ((fd := sys->open(d, Sys->OREAD)) == nil)
149		return nil;
150	for(;;){
151		(n, dir) := sys->dirread(fd);
152		if(n <= 0)
153			break;
154		for(i:=0; i<n; i++)
155			if(dir[i].mode & Sys->DMDIR)
156				return dir[i].name;
157	}
158	return nil;
159}
160
161NONE : con 0;
162
163sniffdir(base : string, elem : string) : (int, int)
164{
165	# t := int elem;
166	t := string2now(elem, 0);
167	if (t == 0)
168		return (NONE, 0);
169	# buf := sys->sprint("%ud", t);
170	# if (buf != elem)
171	#	return (NONE, 0);
172	rv := NONE;
173	p := base + "/" + elem + "/package";
174	(ok, nil) := sys->stat(p);
175	if (ok >= 0)
176		rv |= FULL;
177	p = base + "/" + elem + "/update";
178	(ok, nil) = sys->stat(p);
179	if (ok >= 0)
180		rv |= UPD;
181	return (rv, t);
182}
183
184openupdate(d : string) : (array of Update, int, int)
185{
186	u : array of Update;
187
188	if ((fd := sys->open(d, Sys->OREAD)) == nil)
189		return (nil, -1, 0);
190	#
191	# We are looking to find the most recent full
192	# package; anything before that is irrelevant.
193	# Also figure out the most recent package update.
194	# Non-package updates before that are irrelevant.
195	# If there are no packages installed,
196	# grab all the updates we can find.
197	#
198	tbase := -1;
199	tfull := -1;
200	nu := 0;
201	for(;;){
202		(n, dir) := sys->dirread(fd);
203		if(n <= 0)
204			break;
205		for(i := 0; i < n; i++){
206			(k, t) := sniffdir(d, dir[i].name);
207			case (k) {
208				FULL =>
209					nu++;
210					if (t > tfull)
211						tfull = t;
212					if (t > tbase)
213						tbase = t;
214				FULL|UPD =>
215					nu++;
216					if (t > tfull)
217						tfull = t;
218				UPD =>
219					nu++;
220			}
221		}
222	}
223	if (nu == 0)
224		return (nil, -1, 0);
225	u = nil;
226	nu = 0;
227	if ((fd = sys->open(d, Sys->OREAD)) == nil)
228		return (nil, -1, 0);
229	for(;;){
230		(n, dir) := sys->dirread(fd);
231		if(n <= 0)
232			break;
233		for(i := 0; i < n; i++){
234			(k, t) := sniffdir(d, dir[i].name);
235			if (k == 0)
236				continue;
237			if (t < tbase)
238				continue;
239			if (t < tfull && k == UPD)
240				continue;
241			if (nu%8 == 0) {
242				newu := array[nu+8] of Update;
243				newu[0:] = u[0:nu];
244				u = newu;
245			}
246			u[nu].typ = k;
247			if (readupdate(u, nu, d, dir[i].name) != nil)
248				nu++;
249		}
250	}
251	if (nu == 0)
252		return (nil, -1, 0);
253	qsort(u, nu);
254	return (u, nu, tfull);
255}
256
257readupdate(u : array of Update, ui : int, base : string, elem : string) : array of Update
258{
259	# u[ui].dir = base + "/" + elem;
260	u[ui].dir = pathcat(base, elem);
261	p := u[ui].dir + "/desc";
262	u[ui].desc = readfile(p);
263	# u[ui].time = int elem;
264	u[ui].time = string2now(elem, 0);
265	p = u[ui].dir + "/md5sum";
266	u[ui].bmd5 = bufio->open(p, Bufio->OREAD);
267	p = u[ui].dir + "/update";
268	q := readfile(p);
269	if (q != nil)
270		u[ui].utime = int q;
271	else
272		u[ui].utime = 0;
273	if (u[ui].bmd5 == nil)
274		return nil;
275	return u;
276}
277
278readfile(s : string) : string
279{
280	(ok, d) := sys->stat(s);
281	if (ok < 0)
282		return nil;
283	buf := array[int d.length] of byte;
284	if ((fd := sys->open(s, Sys->OREAD)) == nil || sys->read(fd, buf, int d.length) != int d.length)
285		return nil;
286	s = string buf;
287	ls := len s;
288	if (s[ls-1] == '\n')
289		s = s[0:ls-1];
290	return s;
291}
292
293hex(c : int) : int
294{
295	if (c >= '0' && c <= '9')
296		return c-'0';
297	if (c >= 'a' && c <= 'f')
298		return c-'a'+10;
299	if (c >= 'A' && c <= 'F')
300		return c-'A'+10;
301	return -1;
302}
303
304getfileinfo(w : ref Wrapped, f : string, rdigest : array of byte, wdigest : array of byte, ardigest: array of byte) : (int, int)
305{
306	p : string;
307
308	if (w == nil)
309		return (-1, 0);
310	digest := array[keyring->MD5dlen] of { * => byte 0 };
311	for (i := w.nu-1; i >= 0; i--){
312		if ((p = bsearch(w.u[i].bmd5, f)) == nil)
313			continue;
314		if (p == nil)
315			continue;
316		k := 0;
317		while (k < len p && p[k] != ' ')
318			k++;
319		if (k == len p)
320			continue;
321		q := p[k+1:];
322		if (q == nil)
323			continue;
324		if (len q != 2*Keyring->MD5dlen+1)
325			continue;
326		for (j := 0; j < Keyring->MD5dlen; j++) {
327			a := hex(q[2*j]);
328			b := hex(q[2*j+1]);
329			if (a < 0 || b < 0)
330				break;
331			digest[j] = byte ((a<<4)|b);
332		}
333		if(j != Keyring->MD5dlen)
334			continue;
335		if(rdigest == nil || memcmp(rdigest, digest, keyring->MD5dlen) == 0 || (ardigest != nil && memcmp(ardigest, digest, keyring->MD5dlen) == 0))
336			break;
337		else
338			return (-1, 0);	# NEW
339	}
340	if(i < 0)
341		return (-1, 0);
342	if(wdigest != nil)
343		wdigest[0:] = rdigest;
344	return (0, w.u[i].time);
345
346
347}
348
349bsearch(b : ref Bufio->Iobuf, p : string) : string
350{
351	if (b == nil)
352		return nil;
353	lo := 0;
354	b.seek(big 0, Bufio->SEEKEND);
355	hi := int b.offset();
356	l := len p;
357	while (lo < hi) {
358		m := (lo+hi)/2;
359		b.seek(big m, Bufio->SEEKSTART);
360		b.gets('\n');
361		if (int b.offset() == hi) {
362			bgetbackc(b);
363			m = int b.offset();
364			while (m-- > lo) {
365				if (bgetbackc(b) == '\n') {
366					b.getc();
367					break;
368				}
369			}
370		}
371		s := b.gets('\n');
372		if (len s >= l+1 && s[0:l] == p && (s[l] == ' ' || s[l] == '\n'))
373			return s;
374		if (s < p)
375			lo = int b.offset();
376		else
377			hi = int b.offset()-len s;
378	}
379	return nil;
380}
381
382bgetbackc(b : ref Bufio->Iobuf) : int
383{
384	m := int b.offset();
385	b.seek(big (m-1), Bufio->SEEKSTART);
386	c := b.getc();
387	b.ungetc();
388	return c;
389}
390
391strstr(s : string, p : string) : int
392{
393	lp := len p;
394	ls := len s;
395	for (i := 0; i < ls-lp; i++)
396		if (s[i:i+lp] == p)
397			return i;
398	return -1;
399}
400
401qsort(a : array of Update, n : int)
402{
403	i, j : int;
404	t : Update;
405
406	while(n > 1) {
407		i = n>>1;
408		t = a[0]; a[0] = a[i]; a[i] = t;
409		i = 0;
410		j = n;
411		for(;;) {
412			do
413				i++;
414			while(i < n && a[i].time < a[0].time);
415			do
416				j--;
417			while(j > 0 && a[j].time > a[0].time);
418			if(j < i)
419				break;
420			t = a[i]; a[i] = a[j]; a[j] = t;
421		}
422		t = a[0]; a[0] = a[j]; a[j] = t;
423		n = n-j-1;
424		if(j >= n) {
425			qsort(a, j);
426			a = a[j+1:];
427		} else {
428			qsort(a[j+1:], n);
429			n = j;
430		}
431	}
432}
433
434md5file(file : string, digest : array of byte) : int
435{
436	(ok, d) := sys->stat(file);
437	if (ok < 0)
438		return -1;
439	if (d.mode & Sys->DMDIR)
440		return 0;
441	bio := bufio->open(file, Bufio->OREAD);
442	if (bio == nil)
443		return -1;
444	# return md5sum(bio, digest, d.length);
445	buff := array[Sys->ATOMICIO] of byte;
446	ds := keyring->md5(nil, 0, nil, nil);
447	while ((n := bio.read(buff, len buff)) > 0)
448		keyring->md5(buff, n, nil, ds);
449	keyring->md5(nil, 0, digest, ds);
450	bio = nil;
451	return 0;
452}
453
454md5sum(b : ref Iobuf, digest : array of byte, leng : int) : int
455{
456	ds := keyring->md5(nil, 0, nil, nil);
457	buff := array[Sys->ATOMICIO] of byte;
458	while (leng > 0) {
459		if (leng > len buff)
460			n := len buff;
461		else
462			n = leng;
463		if ((n = b.read(buff, n)) <= 0)
464			return -1;
465		keyring->md5(buff, n, nil, ds);
466		leng -= n;
467	}
468	keyring->md5(nil, 0, digest, ds);
469	return 0;
470}
471
472md5conv(d : array of byte) : string
473{
474	s : string = nil;
475
476	for (i := 0; i < keyring->MD5dlen; i++)
477		s += sys->sprint("%.2ux", int d[i]);
478	return s;
479}
480
481zd : Sys->Dir;
482
483newd(time : int, uid : string, gid : string) : ref Sys->Dir
484{
485	d := ref Sys->Dir;
486	*d = zd;
487	d.uid = uid;
488	d.gid = gid;
489	d.mtime = time;
490	return d;
491}
492
493putwrapfile(b : ref Iobuf, name : string, time : int, elem : string, file : string, uid : string, gid : string)
494{
495	d := newd(time, uid, gid);
496	d.mode = 8r444;
497	(ok, dir) := sys->stat(file);
498	if (ok < 0)
499		sys->fprint(sys->fildes(2), "cannot stat %s: %r", file);
500	d.length = dir.length;
501	# s := "/wrap/"+name+"/"+sys->sprint("%ud", time)+"/"+elem;
502	s := "/wrap/"+name+"/"+now2string(time, 0)+"/"+elem;
503	arch->puthdr(b, s, d);
504	arch->putfile(b, file, int d.length);
505}
506
507putwrap(b : ref Iobuf, name : string, time : int, desc : string, utime : int, pkg : int, uid : string, gid : string)
508{
509	if (!(utime || pkg))
510		sys->fprint(sys->fildes(2), "bad precondition in putwrap()");
511	d := newd(time, uid, gid);
512	d.mode = Sys->DMDIR|8r775;
513	s := "/wrap";
514	arch->puthdr(b, s, d);
515	s += "/"+name;
516	arch->puthdr(b, s, d);
517	# s += "/"+sys->sprint("%ud", time);
518	s += "/"+now2string(time, 0);
519	arch->puthdr(b, s, d);
520	d.mode = 8r444;
521	s += "/";
522	dir := s;
523	if (utime) {
524		s = dir+"update";
525		d.length = big 23;
526		arch->puthdr(b, s, d);
527		arch->putstring(b, sys->sprint("%22ud\n", utime));
528	}
529	if (pkg) {
530		s = dir+"package";
531		d.length = big 0;
532		arch->puthdr(b, s, d);
533	}
534	if (desc != nil) {
535		s = dir+"desc";
536		d.length = big (len desc+1);
537		d.mode = 8r444;
538		arch->puthdr(b, s, d);
539		arch->putstring(b, desc+"\n");
540	}
541}
542
543memcmp(b1, b2 : array of byte, n : int) : int
544{
545	for (i := 0; i < n; i++)
546		if (b1[i] < b2[i])
547			return -1;
548		else if (b1[i] > b2[i])
549			return 1;
550	return 0;
551}
552
553strprefix(s: string, pre: string): int
554{
555	return len s >= (l := len pre) && s[0:l] == pre;
556}
557
558match(s: string, pre: list of string): int
559{
560	if(pre == nil || s == "/wrap" || strprefix(s, "/wrap/"))
561		return 1;
562	for( ; pre != nil; pre = tl pre)
563		if(strprefix(s, hd pre))
564			return 1;
565	return 0;
566}
567
568notmatch(s: string, pre: list of string): int
569{
570	if(pre == nil || s == "/wrap" || strprefix(s, "/wrap/"))
571		return 1;
572	for( ; pre != nil; pre = tl pre)
573		if(strprefix(s, hd pre))
574			return 0;
575	return 1;
576}
577
578pathcat(s : string, t : string) : string
579{
580	if (s == nil) return t;
581	if (t == nil) return s;
582	slashs := s[len s - 1] == '/';
583	slasht := t[0] == '/';
584	if (slashs && slasht)
585		return s + t[1:];
586	if (!slashs && !slasht)
587		return s + "/" + t;
588	return s + t;
589}
590
591md5filea(file : string, digest : array of byte) : int
592{
593	n, n0: int;
594
595	(ok, d) := sys->stat(file);
596	if (ok < 0)
597		return -1;
598	if (d.mode & Sys->DMDIR)
599		return 0;
600	bio := bufio->open(file, Bufio->OREAD);
601	if (bio == nil)
602		return -1;
603	buff := array[Sys->ATOMICIO] of byte;
604	m := len buff;
605	ds := keyring->md5(nil, 0, nil, nil);
606	r := 0;
607	while(1){
608		if(r){
609			if((n = bio.read(buff[1:], m-1)) <= 0)
610				break;
611			n++;
612		}
613		else{
614			if ((n = bio.read(buff, m)) <= 0)
615				break;
616		}
617		(n0, r) = remcr(buff, n);
618		if(r){
619			keyring->md5(buff, n0-1, nil, ds);
620			buff[0] = byte '\r';
621		}
622		else
623			keyring->md5(buff, n0, nil, ds);
624	}
625	if(r)
626		keyring->md5(buff, 1, nil, ds);
627	keyring->md5(nil, 0, digest, ds);
628	bio = nil;
629	return 0;
630}
631
632remcr(b: array of byte, n: int): (int, int)
633{
634	if(n == 0)
635		return (0, 0);
636	for(i := 0; i < n; ){
637		if(b[i] == byte '\r' && i+1 < n && b[i+1] == byte '\n')
638			b[i:] = b[i+1:n--];
639		else
640			i++;
641	}
642	return (n, b[n-1] == byte '\r');
643}
644
645TEN2EIGHT: con 100000000;
646
647now2string(n: int, flag: int): string
648{
649	if(flag == 0)
650		return sys->sprint("%ud", n);
651	if(n < 0)
652		return nil;
653	q := n/TEN2EIGHT;
654	s := "0" +  string (n-TEN2EIGHT*q);
655	while(len s < 9)
656		s = "0" + s;
657	if(q <= 9)
658		s[0] = '0' + q - 0;
659	else if(q <= 21)
660		s[0] = 'A' + q - 10;
661	else
662		return nil;
663	return s;
664}
665
666string2now(s: string, flag: int): int
667{
668	if(flag == 0 && s[0] != 'A')
669		return int s;
670	if(len s != 9)
671		return 0;
672	r := int s[1: ];
673	c := s[0];
674	if(c >= '0' && c <= '9')
675		q := c - '0' + 0;
676	else if(c >= 'A' && c <= 'L')
677		q = c - 'A' + 10;
678	else
679		return 0;
680	n := TEN2EIGHT*q + r;
681	if(n < 0)
682		return 0;
683	return n;
684}
685