xref: /plan9/sys/src/cmd/archfs.c (revision 14cc0f535177405a84c5b73603a98e5db6674719)
1 /*
2  * archfs - mount mkfs style archives
3  */
4 
5 #include <u.h>
6 #include <libc.h>
7 #include <bio.h>
8 #include <auth.h>
9 #include <fcall.h>
10 #include <thread.h>
11 #include <9p.h>
12 
13 Tree *archtree;
14 Biobuf *b;
15 int verbose;
16 
17 typedef struct Ahdr Ahdr;
18 struct Ahdr {
19 	char *name;
20 	Dir;
21 };
22 
23 typedef struct Arch Arch;
24 struct Arch {
25 	vlong off;
26 	vlong length;
27 };
28 
29 static void*
emalloc(long sz)30 emalloc(long sz)
31 {
32 	void *v;
33 
34 	v = malloc(sz);
35 	if(v == nil)
36 		sysfatal("malloc %lud fails", sz);
37 	memset(v, 0, sz);
38 	return v;
39 }
40 
41 static char*
estrdup(char * s)42 estrdup(char *s)
43 {
44 	s = strdup(s);
45 	if(s == nil)
46 		sysfatal("strdup (%.10s) fails", s);
47 	return s;
48 }
49 
50 static char*
Bgetline(Biobuf * b)51 Bgetline(Biobuf *b)
52 {
53 	char *p;
54 
55 	if(p = Brdline(b, '\n'))
56 		p[Blinelen(b)-1] = '\0';
57 	return p;
58 }
59 
60 Ahdr*
gethdr(Biobuf * b)61 gethdr(Biobuf *b)
62 {
63 	Ahdr *a;
64 	char *p, *f[10];
65 
66 	if((p = Bgetline(b)) == nil)
67 		return nil;
68 
69 	if(strcmp(p, "end of archive") == 0) {
70 		werrstr("");
71 		return nil;
72 	}
73 
74 	if(tokenize(p, f, nelem(f)) != 6) {
75 		werrstr("bad format");
76 		return nil;
77 	}
78 
79 	a = emalloc(sizeof(*a));
80 	a->name = estrdup(f[0]);
81 	a->mode = strtoul(f[1], 0, 8);
82 	a->uid = estrdup(f[2]);
83 	a->gid = estrdup(f[3]);
84 	a->mtime = strtoll(f[4], 0, 10);
85 	a->length = strtoll(f[5], 0, 10);
86 	return a;
87 }
88 
89 static Arch*
newarch(vlong off,vlong length)90 newarch(vlong off, vlong length)
91 {
92 	static Arch *abuf;
93 	static int nabuf;
94 
95 	if(nabuf == 0) {
96 		nabuf = 256;
97 		abuf = emalloc(sizeof(Arch)*nabuf);
98 	}
99 
100 	nabuf--;
101 	abuf->off = off;
102 	abuf->length = length;
103 	return abuf++;
104 }
105 
106 static File*
createpath(File * f,char * name,char * u,ulong m)107 createpath(File *f, char *name, char *u, ulong m)
108 {
109 	char *p;
110 	File *nf;
111 
112 	if(verbose)
113 		fprint(2, "createpath %s\n", name);
114 	incref(f);
115 	while(f && (p = strchr(name, '/'))) {
116 		*p = '\0';
117 		if(strcmp(name, "") != 0 && strcmp(name, ".") != 0){
118 			/* this would be a race if we were multithreaded */
119 			incref(f);	/* so walk doesn't kill it immediately on failure */
120 			if((nf = walkfile(f, name)) == nil)
121 				nf = createfile(f, name, u, DMDIR|0777, nil);
122 			decref(f);
123 			f = nf;
124 		}
125 		*p = '/';
126 		name = p+1;
127 	}
128 	if(f == nil)
129 		return nil;
130 
131 	incref(f);
132 	if((nf = walkfile(f, name)) == nil)
133 		nf = createfile(f, name, u, m, nil);
134 	decref(f);
135 	return nf;
136 }
137 
138 static void
archcreatefile(char * name,Arch * arch,Dir * d)139 archcreatefile(char *name, Arch *arch, Dir *d)
140 {
141 	File *f;
142 	f = createpath(archtree->root, name, d->uid, d->mode);
143 	if(f == nil)
144 		sysfatal("creating %s: %r", name);
145 	free(f->gid);
146 	f->gid = estrdup9p(d->gid);
147 	f->aux = arch;
148 	f->mtime = d->mtime;
149 	f->length = d->length;
150 	decref(f);
151 }
152 
153 static void
fsread(Req * r)154 fsread(Req *r)
155 {
156 	Arch *a;
157 	char err[ERRMAX];
158 	int n;
159 
160 	a = r->fid->file->aux;
161 	if(a->length <= r->ifcall.offset)
162 		r->ifcall.count = 0;
163 	else if(a->length <= r->ifcall.offset+r->ifcall.count)
164 		r->ifcall.count = a->length - r->ifcall.offset;
165 
166 	werrstr("unknown error");
167 	if(Bseek(b, a->off+r->ifcall.offset, 0) < 0
168 	|| (n = Bread(b, r->ofcall.data, r->ifcall.count)) < 0) {
169 		err[0] = '\0';
170 		errstr(err, sizeof err);
171 		respond(r, err);
172 	} else {
173 		r->ofcall.count = n;
174 		respond(r, nil);
175 	}
176 }
177 
178 Srv fs = {
179 	.read=	fsread,
180 };
181 
182 static void
usage(void)183 usage(void)
184 {
185 	fprint(2, "usage: archfs [-abcC] [-m mtpt] archfile\n");
186 	exits("usage");
187 }
188 
189 void
main(int argc,char ** argv)190 main(int argc, char **argv)
191 {
192 	Ahdr *a;
193 	ulong flag;
194 	char *mtpt;
195 	char err[ERRMAX];
196 
197 	flag = 0;
198 	mtpt = "/mnt/arch";
199 	ARGBEGIN{
200 	case 'D':
201 		chatty9p++;
202 		break;
203 	case 'a':
204 		flag |= MAFTER;
205 		break;
206 	case 'b':
207 		flag |= MBEFORE;
208 		break;
209 	case 'c':
210 		flag |= MCREATE;
211 		break;
212 	case 'C':
213 		flag |= MCACHE;
214 		break;
215 	case 'm':
216 		mtpt = EARGF(usage());
217 		break;
218 	default:
219 		usage();
220 		break;
221 	}ARGEND;
222 
223 	if(argc != 1)
224 		usage();
225 
226 	if((b = Bopen(argv[0], OREAD)) == nil)
227 		sysfatal("open '%s': %r", argv[0]);
228 
229 	archtree = fs.tree = alloctree("sys", "sys", DMDIR|0775, nil);
230 	while(a = gethdr(b)) {
231 		archcreatefile(a->name, newarch(Boffset(b), a->length), a);
232 		Bseek(b, a->length, 1);
233 	}
234 
235 	err[0] = '\0';
236 	errstr(err, sizeof err);
237 	if(err[0])
238 		sysfatal("reading archive: %s", err);
239 
240 	postmountsrv(&fs, nil, mtpt, flag);
241 	exits(0);
242 }
243