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