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