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