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