]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/disk/mkfs.c
disk/format: removed 9fat magic VOLID value
[plan9front.git] / sys / src / cmd / disk / mkfs.c
1 #include <u.h>
2 #include <libc.h>
3 #include <disk.h>
4 #include <auth.h>
5 #include <bio.h>
6
7 enum{
8         LEN = 4096,
9
10         /*
11          * types of destination file sytems
12          */
13         Kfs = 0,
14         Fs,
15         Archive,
16 };
17
18 void    protowarn(char *msg, void *);
19 void    protoenum(char *new, char *old, Dir *d, void *);
20
21 void    arch(Dir*);
22 void    copy(Dir*);
23 void    error(char *, ...);
24 void    kfscmd(char *);
25 void    mkdir(Dir*);
26 void    mountkfs(char*);
27 int     uptodate(Dir*, char*);
28 void    usage(void);
29 void    warn(char *, ...);
30
31 Biobufhdr bout;                 /* stdout when writing archive */
32 uchar   boutbuf[2*LEN];
33 char    newfile[LEN];
34 char    oldfile[LEN];
35 char    *proto;
36 char    *cputype;
37 char    *users;
38 char    *oldroot;
39 char    *newroot;
40 char    *prog = "mkfs";
41 char    *buf;
42 char    *zbuf;
43 int     buflen = 1024-8;
44 int     verb;
45 int     modes;
46 int     ream;
47 int     debug;
48 int     xflag;
49 int     sfd;
50 int     fskind;                 /* Kfs, Fs, Archive */
51 int     setuid;                 /* on Fs: set uid and gid? */
52 char    *user;
53
54 void
55 main(int argc, char **argv)
56 {
57         char *name;
58         int i, errs;
59
60         quotefmtinstall();
61         user = getuser();
62         name = "";
63         oldroot = "";
64         newroot = "/n/kfs";
65         users = 0;
66         fskind = Kfs;
67         ARGBEGIN{
68         case 'a':
69                 if(fskind != Kfs) {
70                         fprint(2, "cannot use -a with -d\n");
71                         usage();
72                 }
73                 fskind = Archive;
74                 newroot = "";
75                 Binits(&bout, 1, OWRITE, boutbuf, sizeof boutbuf);
76                 break;
77         case 'd':
78                 if(fskind != Kfs) {
79                         fprint(2, "cannot use -d with -a\n");
80                         usage();
81                 }
82                 fskind = Fs;
83                 newroot = ARGF();
84                 break;
85         case 'D':
86                 debug = 1;
87                 break;
88         case 'n':
89                 name = EARGF(usage());
90                 break;
91         case 'p':
92                 modes = 1;
93                 break;
94         case 'r':
95                 ream = 1;
96                 break;
97         case 's':
98                 oldroot = ARGF();
99                 break;
100         case 'u':
101                 users = ARGF();
102                 break;
103         case 'U':
104                 setuid = 1;
105                 break;
106         case 'v':
107                 verb = 1;
108                 break;
109         case 'x':
110                 xflag = 1;
111                 break;
112         case 'z':
113                 buflen = atoi(ARGF())-8;
114                 break;
115         default:
116                 usage();
117         }ARGEND
118
119         if(!argc)       
120                 usage();
121
122         buf = malloc(buflen);
123         zbuf = malloc(buflen);
124         memset(zbuf, 0, buflen);
125
126         mountkfs(name);
127         kfscmd("allow");
128         cputype = getenv("cputype");
129         if(cputype == 0)
130                 cputype = "386";
131
132         errs = 0;
133         for(i = 0; i < argc; i++){
134                 proto = argv[i];
135                 fprint(2, "processing %q\n", proto);
136                 if(rdproto(proto, oldroot, protoenum, protowarn, nil) < 0){
137                         fprint(2, "%q: can't open %q: skipping\n", prog, proto);
138                         errs++;
139                         continue;
140                 }
141         }
142         fprint(2, "file system made\n");
143         kfscmd("disallow");
144         kfscmd("sync");
145         if(errs)
146                 exits("skipped protos");
147         if(fskind == Archive){
148                 Bprint(&bout, "end of archive\n");
149                 Bterm(&bout);
150         }
151         exits(0);
152 }
153
154 /*
155  * check if file to is up to date with
156  * respect to the file represented by df
157  */
158 int
159 uptodate(Dir *df, char *to)
160 {
161         int ret;
162         Dir *dt;
163
164         if(fskind == Archive || ream || (dt = dirstat(to)) == nil)
165                 return 0;
166         ret = dt->mtime >= df->mtime;
167         free(dt);
168         return ret;
169 }
170
171 void
172 copy(Dir *d)
173 {
174         char cptmp[LEN], *p;
175         int f, t, n, needwrite, nowarnyet = 1;
176         vlong tot, len;
177         Dir nd;
178
179         f = open(oldfile, OREAD);
180         if(f < 0){
181                 warn("can't open %q: %r", oldfile);
182                 return;
183         }
184         t = -1;
185         if(fskind == Archive)
186                 arch(d);
187         else{
188                 strcpy(cptmp, newfile);
189                 p = utfrrune(cptmp, L'/');
190                 if(!p)
191                         error("internal temporary file error");
192                 strcpy(p+1, "__mkfstmp");
193                 t = create(cptmp, OWRITE, 0666);
194                 if(t < 0){
195                         warn("can't create %q: %r", newfile);
196                         close(f);
197                         return;
198                 }
199         }
200
201         needwrite = 0;
202         for(tot = 0; tot < d->length; tot += n){
203                 len = d->length - tot;
204                 /* don't read beyond d->length */
205                 if (len > buflen)
206                         len = buflen;
207                 n = read(f, buf, len);
208                 if(n <= 0) {
209                         if(n < 0 && nowarnyet) {
210                                 warn("can't read %q: %r", oldfile);
211                                 nowarnyet = 0;
212                         }
213                         /*
214                          * don't quit: pad to d->length (in pieces) to agree
215                          * with the length in the header, already emitted.
216                          */
217                         memset(buf, 0, len);
218                         n = len;
219                 }
220                 if(fskind == Archive){
221                         if(Bwrite(&bout, buf, n) != n)
222                                 error("write error: %r");
223                 }else if(memcmp(buf, zbuf, n) == 0){
224                         if(seek(t, n, 1) < 0)
225                                 error("can't write zeros to %q: %r", newfile);
226                         needwrite = 1;
227                 }else{
228                         if(write(t, buf, n) < n)
229                                 error("can't write %q: %r", newfile);
230                         needwrite = 0;
231                 }
232         }
233         close(f);
234         if(needwrite){
235                 if(seek(t, -1, 1) < 0 || write(t, zbuf, 1) != 1)
236                         error("can't write zero at end of %q: %r", newfile);
237         }
238         if(tot != d->length){
239                 /* this should no longer happen */
240                 warn("wrong number of bytes written to %q (was %lld should be %lld)\n",
241                         newfile, tot, d->length);
242                 if(fskind == Archive){
243                         warn("seeking to proper position\n");
244                         /* does no good if stdout is a pipe */
245                         Bseek(&bout, d->length - tot, 1);
246                 }
247         }
248         if(fskind == Archive)
249                 return;
250         remove(newfile);
251         nulldir(&nd);
252         nd.mode = d->mode;
253         nd.gid = d->gid;
254         nd.mtime = d->mtime;
255         nd.name = d->name;
256         if(dirfwstat(t, &nd) < 0)
257                 error("can't move tmp file to %q: %r", newfile);
258         nulldir(&nd);
259         nd.uid = d->uid;
260         dirfwstat(t, &nd);
261         close(t);
262 }
263
264 void
265 mkdir(Dir *d)
266 {
267         Dir *d1;
268         Dir nd;
269         int fd;
270
271         if(fskind == Archive){
272                 arch(d);
273                 return;
274         }
275         fd = create(newfile, OREAD, d->mode);
276         nulldir(&nd);
277         nd.mode = d->mode;
278         nd.gid = d->gid;
279         nd.mtime = d->mtime;
280         if(fd < 0){
281                 if((d1 = dirstat(newfile)) == nil || !(d1->mode & DMDIR)){
282                         free(d1);
283                         error("can't create %q", newfile);
284                 }
285                 free(d1);
286                 if(dirwstat(newfile, &nd) < 0)
287                         warn("can't set modes for %q: %r", newfile);
288                 nulldir(&nd);
289                 nd.uid = d->uid;
290                 dirwstat(newfile, &nd);
291                 return;
292         }
293         if(dirfwstat(fd, &nd) < 0)
294                 warn("can't set modes for %q: %r", newfile);
295         nulldir(&nd);
296         nd.uid = d->uid;
297         dirfwstat(fd, &nd);
298         close(fd);
299 }
300
301 void
302 arch(Dir *d)
303 {
304         Bprint(&bout, "%q %luo %q %q %lud %lld\n",
305                 newfile, d->mode, d->uid, d->gid, d->mtime, d->length);
306 }
307
308 void
309 protowarn(char *msg, void *)
310 {
311         warn("%s", msg);
312 }
313
314 void
315 protoenum(char *new, char *old, Dir *d, void *)
316 {
317         Dir nd;
318
319         sprint(newfile, "%s%s", newroot, new);
320         sprint(oldfile, "%s", old);
321
322         if(xflag){
323                 Bprint(&bout, "%q\t%ld\t%lld\n", new, d->mtime, d->length);
324                 return;
325         }
326         if(verb && (fskind == Archive || ream))
327                 fprint(2, "%q\n", new);
328         if(fskind == Fs && !setuid){
329                 d->uid = "";
330                 d->gid = "";
331         }
332         if(!uptodate(d, newfile)){
333                 if(verb && (fskind != Archive && ream == 0))
334                         fprint(2, "%q\n", new);
335                 if(d->mode & DMDIR)
336                         mkdir(d);
337                 else
338                         copy(d);
339         }else if(modes){
340                 nulldir(&nd);
341                 nd.mode = d->mode;
342                 nd.gid = d->gid;
343                 nd.mtime = d->mtime;
344                 if(verb && (fskind != Archive && ream == 0))
345                         fprint(2, "%q\n", new);
346                 if(dirwstat(newfile, &nd) < 0)
347                         warn("can't set modes for %q: %r", new);
348                 nulldir(&nd);
349                 nd.uid = d->uid;
350                 dirwstat(newfile, &nd);
351         }
352 }
353
354 void
355 mountkfs(char *name)
356 {
357         char kname[64];
358
359         if(fskind != Kfs)
360                 return;
361         if(name[0])
362                 snprint(kname, sizeof kname, "/srv/kfs.%s", name);
363         else
364                 strcpy(kname, "/srv/kfs");
365         sfd = open(kname, ORDWR);
366         if(sfd < 0){
367                 fprint(2, "can't open %q\n", kname);
368                 exits("open /srv/kfs");
369         }
370         if(mount(sfd, -1, "/n/kfs", MREPL|MCREATE, "") < 0){
371                 fprint(2, "can't mount kfs on /n/kfs\n");
372                 exits("mount kfs");
373         }
374         close(sfd);
375         strcat(kname, ".cmd");
376         sfd = open(kname, ORDWR);
377         if(sfd < 0){
378                 fprint(2, "can't open %q\n", kname);
379                 exits("open /srv/kfs");
380         }
381 }
382
383 void
384 kfscmd(char *cmd)
385 {
386         char buf[4*1024];
387         int n;
388
389         if(fskind != Kfs)
390                 return;
391         if(write(sfd, cmd, strlen(cmd)) != strlen(cmd)){
392                 fprint(2, "%q: error writing %q: %r", prog, cmd);
393                 return;
394         }
395         for(;;){
396                 n = read(sfd, buf, sizeof buf - 1);
397                 if(n <= 0)
398                         return;
399                 buf[n] = '\0';
400                 if(strcmp(buf, "done") == 0 || strcmp(buf, "success") == 0)
401                         return;
402                 if(strcmp(buf, "unknown command") == 0){
403                         fprint(2, "%q: command %q not recognized\n", prog, cmd);
404                         return;
405                 }
406         }
407 }
408
409 void
410 error(char *fmt, ...)
411 {
412         char buf[1024];
413         va_list arg;
414
415         sprint(buf, "%q: %q: ", prog, proto);
416         va_start(arg, fmt);
417         vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
418         va_end(arg);
419         fprint(2, "%s\n", buf);
420         kfscmd("disallow");
421         kfscmd("sync");
422         exits(0);
423 }
424
425 void
426 warn(char *fmt, ...)
427 {
428         char buf[1024];
429         va_list arg;
430
431         sprint(buf, "%q: %q: ", prog, proto);
432         va_start(arg, fmt);
433         vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
434         va_end(arg);
435         fprint(2, "%s\n", buf);
436 }
437
438 void
439 usage(void)
440 {
441         fprint(2, "usage: %q [-adprvxUD] [-d root] [-n name] [-s source] [-u users] [-z n] proto ...\n", prog);
442         exits("usage");
443 }