]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/disk/format.c
devproc: can't wait for ourselfs to stop (thanks Shamar)
[plan9front.git] / sys / src / cmd / disk / format.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <disk.h>
5
6 /*
7  *  disk types (all MFM encoding)
8  */
9 typedef struct Type     Type;
10 struct Type
11 {
12         char    *name;
13         int     bytes;          /* bytes/sector */
14         int     sectors;        /* sectors/track */
15         int     heads;          /* number of heads */
16         int     tracks;         /* tracks/disk */
17         int     media;          /* media descriptor byte */
18         int     cluster;        /* default cluster size */
19 };
20 Type floppytype[] =
21 {
22  { "3½HD",     512, 18,  2, 80, 0xf0, 1, },
23  { "3½DD",     512,  9,  2, 80, 0xf9, 2, },
24  { "3½QD",     512, 36, 2, 80, 0xf9, 2, },     /* invented */
25  { "5¼HD",     512, 15,  2, 80, 0xf9, 1, },
26  { "5¼DD",     512,  9,  2, 40, 0xfd, 2, },
27  { "hard",      512,  0,  0, 0, 0xf8, 4, },
28 };
29
30 #define NTYPES (sizeof(floppytype)/sizeof(Type))
31
32 typedef struct Dosboot  Dosboot;
33 struct Dosboot{
34         uchar   magic[3];       /* really an x86 JMP instruction */
35         uchar   version[8];
36         uchar   sectsize[2];
37         uchar   clustsize;
38         uchar   nresrv[2];
39         uchar   nfats;
40         uchar   rootsize[2];
41         uchar   volsize[2];
42         uchar   mediadesc;
43         uchar   fatsize[2];
44         uchar   trksize[2];
45         uchar   nheads[2];
46         uchar   nhidden[4];
47         uchar   bigvolsize[4];
48         uchar   driveno;
49         uchar   reserved0;
50         uchar   bootsig;
51         uchar   volid[4];
52         uchar   label[11];
53         uchar   type[8];
54 };
55
56 typedef struct Dosboot32 Dosboot32;
57 struct Dosboot32
58 {
59         uchar   common[36];
60         uchar   fatsize[4];
61         uchar   flags[2];
62         uchar   ver[2];
63         uchar   rootclust[4];
64         uchar   fsinfo[2];
65         uchar   bootbak[2];
66         uchar   reserved0[12];
67         uchar   driveno;
68         uchar   reserved1;
69         uchar   bootsig;
70         uchar   volid[4];
71         uchar   label[11];
72         uchar   type[8];
73 };
74
75 enum
76 {
77         FATINFOSIG1     = 0x41615252UL,
78         FATINFOSIG      = 0x61417272UL,
79 };
80
81 typedef struct Fatinfo Fatinfo;
82 struct Fatinfo
83 {
84         uchar   sig1[4];
85         uchar   pad[480];
86         uchar   sig[4];
87         uchar   freeclust[4];   /* num frre clusters; -1 is unknown */
88         uchar   nextfree[4];    /* most recently allocated cluster */
89         uchar   resrv[4*3];
90 };
91
92
93 #define PUTSHORT(p, v) { (p)[1] = (v)>>8; (p)[0] = (v); }
94 #define PUTLONG(p, v) { PUTSHORT((p), (v)); PUTSHORT((p)+2, (v)>>16); }
95 #define GETSHORT(p)     (((p)[1]<<8)|(p)[0])
96 #define GETLONG(p)      (((ulong)GETSHORT(p+2)<<16)|(ulong)GETSHORT(p))
97
98 typedef struct Dosdir   Dosdir;
99 struct Dosdir
100 {
101         uchar   name[8];
102         uchar   ext[3];
103         uchar   attr;
104         uchar   reserved[10];
105         uchar   time[2];
106         uchar   date[2];
107         uchar   start[2];
108         uchar   length[4];
109 };
110
111 #define DRONLY  0x01
112 #define DHIDDEN 0x02
113 #define DSYSTEM 0x04
114 #define DVLABEL 0x08
115 #define DDIR    0x10
116 #define DARCH   0x20
117
118 /*
119  *  the boot program for the boot sector.
120  */
121 int nbootprog = 188;    /* no. of bytes of boot program, including the first 0x3E */
122 uchar bootprog[512] =
123 {
124 [0x000] 0xEB, 0x3C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
125         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 [0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0,
127         0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19,
128         0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00,
129         0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12,
130         0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF,
131         0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4,
132         0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2,
133         0xC3,  'N',  'o',  't',  ' ',  'a',  ' ',  'b',
134          'o',  'o',  't',  'a',  'b',  'l',  'e',  ' ',
135          'd',  'i',  's',  'c',  ' ',  'o',  'r',  ' ',
136          'd',  'i',  's',  'c',  ' ',  'e',  'r',  'r',
137          'o',  'r', '\r', '\n',  'P',  'r',  'e',  's',
138          's',  ' ',  'a',  'l',  'm',  'o',  's',  't',
139          ' ',  'a',  'n',  'y',  ' ',  'k',  'e',  'y',
140          ' ',  't',  'o',  ' ',  'r',  'e',  'b',  'o',
141          'o',  't',  '.',  '.',  '.', 0x00, 0x00, 0x00,
142 [0x1F0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA,
144 };
145
146 char *dev;
147 int clustersize;
148 uchar *fat;     /* the fat */
149 int fatbits;
150 int fatsecs;
151 int fatlast;    /* last cluster allocated */
152 int clusters;
153 int fatsecs;
154 vlong volsecs;
155 uchar *root;    /* first block of root */
156 int rootsecs;
157 int rootfiles;
158 int rootnext;
159 int nresrv = 1;
160 int chatty;
161 vlong length;
162 Type *t;
163 int fflag;
164 int hflag;
165 int xflag;
166 char *file;
167 char *pbs;
168 char *type;
169 char *bootfile;
170 int dos;
171
172 enum
173 {
174         Sof = 1,        /* start of file */
175         Eof = 2,        /* end of file */
176 };
177
178 void    dosfs(int, int, Disk*, char*, int, char*[], int);
179 ulong   clustalloc(int);
180 void    addrname(uchar*, Dir*, char*, ulong);
181 void    sanitycheck(Disk*);
182
183 void
184 usage(void)
185 {
186         fprint(2, "usage: disk/format [-df] [-b bootblock] [-c csize] "
187                 "[-l label] [-r nresrv] [-t type] disk [files ...]\n");
188         exits("usage");
189 }
190
191 void
192 fatal(char *fmt, ...)
193 {
194         char err[128];
195         va_list arg;
196
197         va_start(arg, fmt);
198         vsnprint(err, sizeof(err), fmt, arg);
199         va_end(arg);
200         fprint(2, "format: %s\n", err);
201         if(fflag && file)
202                 remove(file);
203         exits(err);
204 }
205
206 void
207 main(int argc, char **argv)
208 {
209         int fd, n, writepbs;
210         char buf[512], label[11];
211         char *a;
212         Disk *disk;
213
214         dos = 0;
215         type = nil;
216         clustersize = 0;
217         writepbs = 0;
218         memmove(label, "CYLINDRICAL", sizeof(label));
219         ARGBEGIN {
220         case 'b':
221                 pbs = EARGF(usage());
222                 writepbs = 1;
223                 break;
224         case 'c':
225                 clustersize = atoi(EARGF(usage()));
226                 break;
227         case 'd':
228                 dos = 1;
229                 writepbs = 1;
230                 break;
231         case 'f':
232                 fflag = 1;
233                 break;
234         case 'l':
235                 a = EARGF(usage());
236                 n = strlen(a);
237                 if(n > sizeof(label))
238                         n = sizeof(label);
239                 memmove(label, a, n);
240                 while(n < sizeof(label))
241                         label[n++] = ' ';
242                 break;
243         case 'r':
244                 nresrv = atoi(EARGF(usage()));
245                 break;
246         case 't':
247                 type = EARGF(usage());
248                 break;
249         case 'v':
250                 chatty++;
251                 break;
252         case 'x':
253                 xflag = 1;
254                 break;
255         default:
256                 usage();
257         } ARGEND
258
259         if(argc < 1)
260                 usage();
261
262         disk = opendisk(argv[0], 0, 0);
263         if(disk == nil) {
264                 if(fflag) {
265                         if((fd = create(argv[0], ORDWR, 0666)) >= 0) {
266                                 file = argv[0];
267                                 close(fd);
268                                 disk = opendisk(argv[0], 0, 0);
269                         }
270                 }
271         }
272         if(disk == nil)
273                 fatal("opendisk: %r");
274
275         if(disk->type == Tfile)
276                 fflag = 1;
277
278         if(type == nil) {
279                 switch(disk->type){
280                 case Tfile:
281                         type = "3½HD";
282                         break;
283                 case Tfloppy:
284                         seek(disk->ctlfd, 0, 0);
285                         n = read(disk->ctlfd, buf, 10);
286                         if(n <= 0 || n >= 10)
287                                 fatal("reading floppy type");
288                         buf[n] = 0;
289                         type = strdup(buf);
290                         if(type == nil)
291                                 fatal("out of memory");
292                         break;
293                 case Tsd:
294                         type = "hard";
295                         break;
296                 default:
297                         type = "unknown";
298                         break;
299                 }
300         }
301
302         if(!fflag && disk->type == Tfloppy)
303                 if(fprint(disk->ctlfd, "format %s", type) < 0)
304                         fatal("formatting floppy as %s: %r", type);
305
306         if(disk->type != Tfloppy)
307                 sanitycheck(disk);
308
309         /* check that everything will succeed */
310         dosfs(dos, writepbs, disk, label, argc-1, argv+1, 0);
311
312         /* commit */
313         dosfs(dos, writepbs, disk, label, argc-1, argv+1, 1);
314
315         print("used %lld bytes\n", fatlast*clustersize*disk->secsize);
316         exits(0);
317 }
318
319 /*
320  * Look for a partition table on sector 1, as would be the
321  * case if we were erroneously formatting 9fat without -r 2.
322  * If it's there and nresrv is not big enough, complain and exit.
323  * I've blown away my partition table too many times.
324  */
325 void
326 sanitycheck(Disk *disk)
327 {
328         char buf[512];
329         int bad;
330
331         if(xflag)
332                 return;
333
334         bad = 0;
335         if(dos && nresrv < 2 && seek(disk->fd, disk->secsize, 0) == disk->secsize
336         && read(disk->fd, buf, sizeof(buf)) >= 5 && strncmp(buf, "part ", 5) == 0) {
337                 fprint(2, 
338                         "there's a plan9 partition on the disk\n"
339                         "and you didn't specify -r 2 (or greater).\n"
340                         "either specify -r 2 or -x to disable this check.\n");
341                 bad = 1;
342         }
343
344         if(disk->type == Tsd && disk->offset == 0LL) {
345                 fprint(2,
346                         "you're attempting to format your disk (/dev/sdXX/data)\n"
347                         "rather than a partition like /dev/sdXX/9fat;\n"
348                         "this is likely a mistake.  specify -x to disable this check.\n");
349                 bad = 1;
350         }
351
352         if(bad)
353                 exits("failed disk sanity check");
354 }
355
356 /*
357  * Return the BIOS drive number for the disk.
358  * 0x80 is the first fixed disk, 0x81 the next, etc.
359  * We map sdC0=0x80, sdC1=0x81, sdD0=0x82, sdD1=0x83
360  */
361 int
362 getdriveno(Disk *disk)
363 {
364         char buf[64], *p;
365
366         if(disk->type != Tsd)
367                 return 0x80;    /* first hard disk */
368
369         if(fd2path(disk->fd, buf, sizeof(buf)) < 0)
370                 return 0x80;
371
372         /*
373          * The name is of the format #SsdC0/foo 
374          * or /dev/sdC0/foo.
375          * So that we can just look for /sdC0, turn 
376          * #SsdC0/foo into #/sdC0/foo.
377          */
378         if(buf[0] == '#' && buf[1] == 'S')
379                 buf[1] = '/';
380
381         for(p=buf; *p; p++)
382                 if(p[0] == 's' && p[1] == 'd' && (p[2]=='C' || p[2]=='D') &&
383                     (p[3]=='0' || p[3]=='1'))
384                         return 0x80 + (p[2]-'C')*2 + (p[3]-'0');
385                 
386         return 0x80;
387 }
388
389 long
390 writen(int fd, void *buf, long n)
391 {
392         long m, tot;
393
394         /* write 8k at a time, to be nice to the disk subsystem */
395         for(tot=0; tot<n; tot+=m){
396                 m = n - tot;
397                 if(m > 8192)
398                         m = 8192;
399                 if(write(fd, (uchar*)buf+tot, m) != m)
400                         break;
401         }
402         return tot;
403 }
404
405 int
406 defcluster(vlong n)
407 {
408         int i;
409
410         i = n / 32768;
411         if(i <= 1)
412                 return 1;
413         if(i >= 128)
414                 return 128;
415         i--;
416         i |= i >> 1;
417         i |= i >> 2;
418         i |= i >> 4;
419         return i+1;
420 }
421
422 void
423 dosfs(int dofat, int dopbs, Disk *disk, char *label, int argc, char *argv[], int commit)
424 {
425         char r[16];
426         Dosboot *b;
427         uchar *buf, *pbsbuf, *p;
428         Dir *d;
429         int i, data, newclusters, npbs, n, sysfd;
430         ulong x;
431         vlong length, secsize;
432
433         if(dofat == 0 && dopbs == 0)
434                 return;
435
436         for(t = floppytype; t < &floppytype[NTYPES]; t++)
437                 if(strcmp(type, t->name) == 0)
438                         break;
439         if(t == &floppytype[NTYPES])
440                 fatal("unknown floppy type %s", type);
441
442         if(t->sectors == 0 && strcmp(type, "hard") == 0) {
443                 t->sectors = disk->s;
444                 t->heads = disk->h;
445                 t->tracks = disk->c;
446                 t->cluster = defcluster(disk->secs);
447         }
448
449         if(t->sectors == 0 && dofat)
450                 fatal("cannot format fat with type %s: geometry unknown\n", type);
451
452         if(fflag){
453                 disk->size = t->bytes*t->sectors*t->heads*t->tracks;
454                 disk->secsize = t->bytes;
455                 disk->secs = disk->size / disk->secsize;
456         }
457
458         secsize = disk->secsize;
459         length = disk->size;
460
461         buf = malloc(secsize);
462         if(buf == 0)
463                 fatal("out of memory");
464
465         /*
466          * Make disk full size if a file.
467          */
468         if(fflag && disk->type == Tfile){
469                 if((d = dirfstat(disk->wfd)) == nil)
470                         fatal("fstat disk: %r");
471                 if(commit && d->length < disk->size) {
472                         if(seek(disk->wfd, disk->size-1, 0) < 0)
473                                 fatal("seek to 9: %r");
474                         if(write(disk->wfd, "9", 1) < 0)
475                                 fatal("writing 9: @%lld %r", seek(disk->wfd, 0LL, 1));
476                 }
477                 free(d);
478         }
479
480         /*
481          * Start with initial sector from disk
482          */
483         if(seek(disk->fd, 0, 0) < 0)
484                 fatal("seek to boot sector: %r\n");
485         if(commit && read(disk->fd, buf, secsize) != secsize)
486                 fatal("reading boot sector: %r");
487
488         if(dofat)
489                 memset(buf, 0, sizeof(Dosboot));
490
491         /*
492          * Jump instruction and OEM name.
493          */
494         b = (Dosboot*)buf;
495         b->magic[0] = 0xEB;
496         b->magic[1] = 0x3C;
497         b->magic[2] = 0x90;
498         memmove(b->version, "Plan9.00", sizeof(b->version));
499         
500         /*
501          * Add bootstrapping code; offset is
502          * determined from short jump (0xEB 0x??)
503          * instruction.
504          */
505         if(dopbs) {
506                 pbsbuf = malloc(secsize);
507                 if(pbsbuf == 0)
508                         fatal("out of memory");
509
510                 if(pbs){
511                         if((sysfd = open(pbs, OREAD)) < 0)
512                                 fatal("open %s: %r", pbs);
513                         if((npbs = read(sysfd, pbsbuf, secsize)) < 0)
514                                 fatal("read %s: %r", pbs);
515
516                         if(npbs > secsize-2)
517                                 fatal("boot block too large");
518
519                         close(sysfd);
520                 }
521                 else {
522                         memmove(pbsbuf, bootprog, sizeof(bootprog));
523                         npbs = nbootprog;
524                 }
525                 n = buf[1] + 2;
526                 if(npbs <= 0x3 || npbs < n)
527                         fprint(2, "warning: pbs too small\n");
528                 else if(buf[0] != 0xEB)
529                         fprint(2, "warning: pbs doesn't start with short jump\n");
530                 else{
531                         memmove(buf, pbsbuf, 3);
532                         memmove(buf+n, pbsbuf+n, npbs-n);
533                 }
534                 free(pbsbuf);
535         }
536
537         /*
538          * Add FAT BIOS parameter block.
539          */
540         if(dofat) {
541                 if(commit) {
542                         print("Initializing FAT file system\n");
543                         print("type %s, %d tracks, %d heads, %d sectors/track, %lld bytes/sec\n",
544                                 t->name, t->tracks, t->heads, t->sectors, secsize);
545                 }
546
547                 if(clustersize == 0)
548                         clustersize = t->cluster;
549 if(chatty) print("clustersize %d\n", clustersize);
550
551                 /*
552                  * the number of fat bits depends on how much disk is left
553                  * over after you subtract out the space taken up by the fat tables. 
554                  * try both.  what a crock.
555                  */
556                 fatbits = 12;
557 Tryagain:
558                 if(fatbits == 32)
559                         nresrv++;       /* for FatInfo */
560                 volsecs = length/secsize;
561                 /*
562                  * here's a crock inside a crock.  even having fixed fatbits,
563                  * the number of fat sectors depends on the number of clusters,
564                  * but of course we don't know yet.  maybe iterating will get us there.
565                  * or maybe it will cycle.
566                  */
567                 clusters = 0;
568                 for(i=0;; i++){
569                         fatsecs = (fatbits*clusters + 8*secsize - 1)/(8*secsize);
570                         rootsecs = volsecs/200;
571                         rootfiles = rootsecs * (secsize/sizeof(Dosdir));
572                         if(rootfiles > 512){
573                                 rootfiles = 512;
574                                 rootsecs = rootfiles/(secsize/sizeof(Dosdir));
575                         }
576                         if(fatbits == 32){
577                                 rootsecs -= (rootsecs % clustersize);
578                                 if(rootsecs <= 0)
579                                         rootsecs = clustersize;
580                                 rootfiles = rootsecs * (secsize/sizeof(Dosdir));
581                         }
582                         data = nresrv + 2*fatsecs + (rootfiles*sizeof(Dosdir) + secsize-1)/secsize;
583                         newclusters = 2 + (volsecs - data)/clustersize;
584                         if(newclusters == clusters)
585                                 break;
586                         clusters = newclusters;
587                         if(i > 10)
588                                 fatal("can't decide how many clusters to use (%d? %d?)",
589                                         clusters, newclusters);
590 if(chatty) print("clusters %d\n", clusters);
591                 }
592                                 
593 if(chatty) print("try %d fatbits => %d clusters of %d\n", fatbits, clusters, clustersize);
594                 switch(fatbits){
595                 case 12:
596                         if(clusters >= 0xff7){
597                                 fatbits = 16;
598                                 goto Tryagain;
599                         }
600                         break;
601                 case 16:
602                         if(clusters >= 0xfff7){
603                                 fatbits = 32;
604                                 goto Tryagain;
605                         }
606                         break;
607                 case 32:
608                         if(clusters >= 0xffffff7)
609                                 fatal("filesystem too big");
610                         break;
611                 }
612                 PUTSHORT(b->sectsize, secsize);
613                 b->clustsize = clustersize;
614                 PUTSHORT(b->nresrv, nresrv);
615                 b->nfats = 2;
616                 PUTSHORT(b->rootsize, fatbits == 32 ? 0 : rootfiles);
617                 PUTSHORT(b->volsize, volsecs >= (1<<16) ? 0 : volsecs);
618                 b->mediadesc = t->media;
619                 PUTSHORT(b->fatsize, fatbits == 32 ? 0 : fatsecs);
620                 PUTSHORT(b->trksize, t->sectors);
621                 PUTSHORT(b->nheads, t->heads);
622                 PUTLONG(b->nhidden, disk->offset);
623                 PUTLONG(b->bigvolsize, volsecs);
624         
625                 sprint(r, "FAT%d    ", fatbits);
626                 if(fatbits == 32){
627                         Dosboot32 *bb;
628
629                         bb = (Dosboot32*)buf;
630                         PUTLONG(bb->fatsize, fatsecs);
631                         PUTLONG(bb->rootclust, 2);
632                         PUTSHORT(bb->fsinfo, nresrv-1);
633                         PUTSHORT(bb->bootbak, 0);
634                         bb->bootsig = 0x29;
635                         bb->driveno = (t->media == 0xF8) ? getdriveno(disk) : 0;
636                         memmove(bb->label, label, sizeof(bb->label));
637                         memmove(bb->type, r, sizeof(bb->type));
638                 } else {
639                         b->bootsig = 0x29;
640                         b->driveno = (t->media == 0xF8) ? getdriveno(disk) : 0;
641                         memmove(b->label, label, sizeof(b->label));
642                         memmove(b->type, r, sizeof(b->type));
643                 }
644         }
645
646         buf[secsize-2] = 0x55;
647         buf[secsize-1] = 0xAA;
648
649         if(commit) {
650                 if(seek(disk->wfd, 0, 0) < 0)
651                         fatal("seek to boot sector: %r\n");
652                 if(write(disk->wfd, buf, secsize) != secsize)
653                         fatal("writing boot sector: %r");
654         }
655
656         free(buf);
657
658         /*
659          * If we were only called to write the PBS, leave now.
660          */
661         if(dofat == 0)
662                 return;
663
664         /*
665          *  allocate an in memory fat
666          */
667         if(seek(disk->wfd, nresrv*secsize, 0) < 0)
668                 fatal("seek to fat: %r\n");
669 if(chatty) print("fat @%lluX\n", seek(disk->wfd, 0, 1));
670         fat = malloc(fatsecs*secsize);
671         if(fat == 0)
672                 fatal("out of memory");
673         memset(fat, 0, fatsecs*secsize);
674         fat[0] = t->media;
675         fat[1] = 0xff;
676         fat[2] = 0xff;
677         if(fatbits >= 16)
678                 fat[3] = 0xff;
679         if(fatbits == 32){
680                 fat[4] = 0xff;
681                 fat[5] = 0xff;
682                 fat[6] = 0xff;
683                 fat[7] = 0xff;
684         }
685         fatlast = 1;
686         if(seek(disk->wfd, 2*fatsecs*secsize, 1) < 0)   /* 2 fats */
687                 fatal("seek to root: %r");
688 if(chatty) print("root @%lluX\n", seek(disk->wfd, 0LL, 1));
689
690         if(fatbits == 32){
691                 /*
692                  * allocate clusters for root directory
693                  */
694                 if(rootsecs % clustersize)
695                         abort();
696                 length = rootsecs / clustersize;
697                 if(clustalloc(Sof) != 2)
698                         abort();
699                 for(n = 0; n < length-1; n++)
700                         clustalloc(0);
701                 clustalloc(Eof);
702         }
703
704         /*
705          *  allocate an in memory root
706          */
707         root = malloc(rootsecs*secsize);
708         if(root == 0)
709                 fatal("out of memory");
710         memset(root, 0, rootsecs*secsize);
711         if(seek(disk->wfd, rootsecs*secsize, 1) < 0)    /* rootsecs */
712                 fatal("seek to files: %r");
713 if(chatty) print("files @%lluX\n", seek(disk->wfd, 0LL, 1));
714
715         /*
716          * Now positioned at the Files Area.
717          * If we have any arguments, process 
718          * them and write out.
719          */
720         for(p = root; argc > 0; argc--, argv++, p += sizeof(Dosdir)){
721                 if(p >= (root+(rootsecs*secsize)))
722                         fatal("too many files in root");
723                 /*
724                  * Open the file and get its length.
725                  */
726                 if((sysfd = open(*argv, OREAD)) < 0)
727                         fatal("open %s: %r", *argv);
728                 if((d = dirfstat(sysfd)) == nil)
729                         fatal("stat %s: %r", *argv);
730                 if(d->length > 0xFFFFFFFFU)
731                         fatal("file %s too big\n", *argv, d->length);
732                 if(commit)
733                         print("Adding file %s, length %lld\n", *argv, d->length);
734
735                 length = d->length;
736                 if(length){
737                         /*
738                          * Allocate a buffer to read the entire file into.
739                          * This must be rounded up to a cluster boundary.
740                          *
741                          * Read the file and write it out to the Files Area.
742                          */
743                         length += secsize*clustersize - 1;
744                         length /= secsize*clustersize;
745                         length *= secsize*clustersize;
746                         if((buf = malloc(length)) == 0)
747                                 fatal("out of memory");
748         
749                         if(readn(sysfd, buf, d->length) != d->length)
750                                 fatal("read %s: %r", *argv);
751                         memset(buf+d->length, 0, length-d->length);
752 if(chatty) print("%s @%lluX\n", d->name, seek(disk->wfd, 0LL, 1));
753                         if(commit && writen(disk->wfd, buf, length) != length)
754                                 fatal("write %s: %r", *argv);
755                         free(buf);
756
757                         close(sysfd);
758         
759                         /*
760                          * Allocate the FAT clusters.
761                          * We're assuming here that where we
762                          * wrote the file is in sync with
763                          * the cluster allocation.
764                          * Save the starting cluster.
765                          */
766                         length /= secsize*clustersize;
767                         x = clustalloc(Sof);
768                         for(n = 0; n < length-1; n++)
769                                 clustalloc(0);
770                         clustalloc(Eof);
771                 }
772                 else
773                         x = 0;
774
775                 /*
776                  * Add the filename to the root.
777                  */
778 fprint(2, "add %s at clust %lux\n", d->name, x);
779                 addrname(p, d, *argv, x);
780                 free(d);
781         }
782
783         /*
784          *  write the fats and root
785          */
786         if(commit) {
787                 if(fatbits == 32){
788                         Fatinfo *fi;
789
790                         fi = malloc(secsize);
791                         if(fi == nil)
792                                 fatal("out of memory");
793
794                         memset(fi, 0, secsize);
795                         PUTLONG(fi->sig1, FATINFOSIG1);
796                         PUTLONG(fi->sig, FATINFOSIG);
797                         PUTLONG(fi->freeclust, clusters - fatlast);
798                         PUTLONG(fi->nextfree, fatlast);
799         
800                         if(seek(disk->wfd, (nresrv-1)*secsize, 0) < 0)
801                                 fatal("seek to fatinfo: %r\n");
802                         if(write(disk->wfd, fi, secsize) < 0)
803                                 fatal("writing fat #1: %r");
804                         free(fi);
805                 }
806                 if(seek(disk->wfd, nresrv*secsize, 0) < 0)
807                         fatal("seek to fat #1: %r");
808                 if(write(disk->wfd, fat, fatsecs*secsize) < 0)
809                         fatal("writing fat #1: %r");
810                 if(write(disk->wfd, fat, fatsecs*secsize) < 0)
811                         fatal("writing fat #2: %r");
812                 if(write(disk->wfd, root, rootsecs*secsize) < 0)
813                         fatal("writing root: %r");
814         }
815
816         free(fat);
817         free(root);
818 }
819
820 /*
821  *  allocate a cluster
822  */
823 ulong
824 clustalloc(int flag)
825 {
826         ulong o, x;
827
828         if(flag != Sof){
829                 x = (flag == Eof) ? ~0 : (fatlast+1);
830                 if(fatbits == 12){
831                         x &= 0xfff;
832                         o = (3*fatlast)/2;
833                         if(fatlast & 1){
834                                 fat[o] = (fat[o]&0x0f) | (x<<4);
835                                 fat[o+1] = (x>>4);
836                         } else {
837                                 fat[o] = x;
838                                 fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F);
839                         }
840                 }
841                 else if(fatbits == 16){
842                         x &= 0xffff;
843                         o = 2*fatlast;
844                         fat[o] = x;
845                         fat[o+1] = x>>8;
846                 }
847                 else if(fatbits == 32){
848                         x &= 0xfffffff;
849                         o = 4*fatlast;
850                         fat[o] = x;
851                         fat[o+1] = x>>8;
852                         fat[o+2] = x>>16;
853                         fat[o+3] = x>>24;
854                 }
855         }
856                 
857         if(flag == Eof)
858                 return 0;
859         else{
860                 ++fatlast;
861                 if(fatlast >= clusters)
862                         sysfatal("data does not fit on disk (%d %d)", fatlast, clusters);
863                 return fatlast;
864         }
865 }
866
867 void
868 putname(char *p, Dosdir *d)
869 {
870         int i;
871
872         memset(d->name, ' ', sizeof d->name+sizeof d->ext);
873         for(i = 0; i< sizeof(d->name); i++){
874                 if(*p == 0 || *p == '.')
875                         break;
876                 d->name[i] = toupper(*p++);
877         }
878         p = strrchr(p, '.');
879         if(p){
880                 for(i = 0; i < sizeof d->ext; i++){
881                         if(*++p == 0)
882                                 break;
883                         d->ext[i] = toupper(*p);
884                 }
885         }
886 }
887
888 void
889 puttime(Dosdir *d)
890 {
891         Tm *t = localtime(time(0));
892         ushort x;
893
894         x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
895         d->time[0] = x;
896         d->time[1] = x>>8;
897         x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
898         d->date[0] = x;
899         d->date[1] = x>>8;
900 }
901
902 void
903 addrname(uchar *entry, Dir *dir, char *name, ulong start)
904 {
905         char *s;
906         Dosdir *d;
907
908         s = strrchr(name, '/');
909         if(s)
910                 s++;
911         else
912                 s = name;
913
914         d = (Dosdir*)entry;
915         putname(s, d);
916         if(cistrcmp(s, "9load") == 0 || cistrncmp(s, "9boot", 5) == 0)
917                 d->attr = DSYSTEM;
918         else
919                 d->attr = 0;
920         puttime(d);
921         d->start[0] = start;
922         d->start[1] = start>>8;
923         d->length[0] = dir->length;
924         d->length[1] = dir->length>>8;
925         d->length[2] = dir->length>>16;
926         d->length[3] = dir->length>>24;
927 }