1 /* #includes */ /*{{{C}}}*//*{{{*/
3 #define _POSIX_SOURCE 1
5 #define _POSIX_C_SOURCE 2
23 /* #defines */ /*{{{*/
26 /* Number of _used_ bits per int */
28 #define INTBITS ((int)(sizeof(int)*8))
30 /* There are four reserved entries: ., .., [passwd] and [label] */
32 #define RESERVED_ENTRIES 4
34 /* CP/M does not support any kind of inodes, so they are simulated.
35 Inode 0-(maxdir-1) are used by files which lowest extent is stored in
36 the respective directory entry. Inode maxdir is the root directory
37 and inode maxdir+1 is the passwd file, if any. */
39 #define RESERVED_INODES 3
41 #define PASSWD_RECLEN 24
44 extern char **environ;
46 static mode_t s_ifdir=1;
47 static mode_t s_ifreg=1;
49 /* memcpy7 -- Copy string, leaving 8th bit alone */ /*{{{*/
50 static void memcpy7(char *dest, const char *src, int count)
54 *dest = ((*dest) & 0x80) | ((*src) & 0x7F);
61 /* file name conversions */
62 /* splitFilename -- split file name into name and extension */ /*{{{*/
63 static int splitFilename(const char *fullname, int type, char *name, char *ext, int *user)
67 assert(fullname!=(const char*)0);
68 assert(name!=(char*)0);
69 assert(ext!=(char*)0);
70 assert(user!=(int*)0);
73 if (!isdigit(fullname[0]) || !isdigit(fullname[1]))
75 boo="illegal CP/M filename";
78 *user=10*(fullname[0]-'0')+(fullname[1]-'0');
80 if ((fullname[0]=='\0') || (type==CPMFS_DR22 && *user>=16) || (type==CPMFS_P2DOS && *user>=32))
82 boo="illegal CP/M filename";
85 for (i=0; i<8 && fullname[i] && fullname[i]!='.'; ++i) if (!ISFILECHAR(i,fullname[i]))
87 boo="illegal CP/M filename";
90 else name[i]=toupper(fullname[i]);
94 for (j=0; j<3 && fullname[i]; ++i,++j) if (!ISFILECHAR(1,fullname[i]))
96 boo="illegal CP/M filename";
99 else ext[j]=toupper(fullname[i]);
102 boo="illegal CP/M filename";
109 /* isMatching -- do two file names match? */ /*{{{*/
110 static int isMatching(int user1, const char *name1, const char *ext1, int user2, const char *name2, const char *ext2)
114 assert(name1!=(const char*)0);
115 assert(ext1!=(const char*)0);
116 assert(name2!=(const char*)0);
117 assert(ext2!=(const char*)0);
118 if (user1!=user2) return 0;
119 for (i=0; i<8; ++i) if ((name1[i]&0x7f)!=(name2[i]&0x7f)) return 0;
120 for (i=0; i<3; ++i) if ((ext1[i]&0x7f)!=(ext2[i]&0x7f)) return 0;
125 /* allocation vector bitmap functions */
126 /* alvInit -- init allocation vector */ /*{{{*/
127 static void alvInit(const struct cpmSuperBlock *d)
129 int i,j,offset,block;
131 assert(d!=(const struct cpmSuperBlock*)0);
132 /* clean bitmap */ /*{{{*/
133 memset(d->alv,0,d->alvSize*sizeof(int));
135 /* mark directory blocks as used */ /*{{{*/
136 *d->alv=(1<<((d->maxdir*32+d->blksiz-1)/d->blksiz))-1;
138 for (i=0; i<d->maxdir; ++i) /* mark file blocks as used */ /*{{{*/
140 if (d->dir[i].status>=0 && d->dir[i].status<=(d->type==CPMFS_P2DOS ? 31 : 15))
143 fprintf(stderr,"alvInit: allocate extent %d\n",i);
147 block=(unsigned char)d->dir[i].pointers[j];
148 if (d->size>=256) block+=(((unsigned char)d->dir[i].pointers[++j])<<8);
152 fprintf(stderr,"alvInit: allocate block %d\n",block);
154 offset=block/INTBITS;
155 d->alv[offset]|=(1<<block%INTBITS);
163 /* allocBlock -- allocate a new disk block */ /*{{{*/
164 static int allocBlock(const struct cpmSuperBlock *drive)
168 assert(drive!=(const struct cpmSuperBlock*)0);
169 for (i=0; i<drive->alvSize; ++i)
171 for (j=0,bits=drive->alv[i]; j<INTBITS; ++j)
176 if (block>=drive->size)
181 drive->alv[i] |= (1<<j);
192 /* logical block I/O */
193 /* readBlock -- read a (partial) block */ /*{{{*/
194 static int readBlock(const struct cpmSuperBlock *d, int blockno, char *buffer, int start, int end)
196 int sect, track, counter;
199 assert(blockno<d->size);
200 assert(buffer!=(char*)0);
201 if (end<0) end=d->blksiz/d->secLength-1;
202 sect=(blockno*(d->blksiz/d->secLength)+ d->sectrk*d->boottrk)%d->sectrk;
203 track=(blockno*(d->blksiz/d->secLength)+ d->sectrk*d->boottrk)/d->sectrk;
204 for (counter=0; counter<=end; ++counter)
208 if (counter>=start && (err=Device_readSector(&d->dev,track,d->skewtab[sect],buffer+(d->secLength*counter))))
223 /* writeBlock -- write a (partial) block */ /*{{{*/
224 static int writeBlock(const struct cpmSuperBlock *d, int blockno, const char *buffer, int start, int end)
226 int sect, track, counter;
229 assert(blockno<d->size);
230 assert(buffer!=(const char*)0);
231 if (end < 0) end=d->blksiz/d->secLength-1;
232 sect = (blockno*(d->blksiz/d->secLength))%d->sectrk;
233 track = (blockno*(d->blksiz/d->secLength))/d->sectrk+d->boottrk;
234 for (counter = 0; counter<=end; ++counter)
238 if (counter>=start && (err=Device_writeSector(&d->dev,track,d->skewtab[sect],buffer+(d->secLength*counter))))
254 /* directory management */
255 /* readPhysDirectory -- read directory from drive */ /*{{{*/
256 static int readPhysDirectory(const struct cpmSuperBlock *drive)
260 blocks=(drive->maxdir*32+drive->blksiz-1)/drive->blksiz;
262 for (i=0; i<blocks; ++i)
264 if (readBlock(drive,i,(char*)(drive->dir+entry),0,-1)==-1) return -1;
265 entry+=(drive->blksiz/32);
270 /* writePhysDirectory -- write directory to drive */ /*{{{*/
271 static int writePhysDirectory(const struct cpmSuperBlock *drive)
275 blocks=(drive->maxdir*32+drive->blksiz-1)/drive->blksiz;
277 for (i=0; i<blocks; ++i)
279 if (writeBlock(drive,i,(char*)(drive->dir+entry),0,-1)==-1) return -1;
280 entry+=(drive->blksiz/32);
285 /* findFileExtent -- find first/next extent for a file */ /*{{{*/
286 static int findFileExtent(const struct cpmSuperBlock *sb, int user, const char *name, const char *ext, int start, int extno)
288 boo="file already exists";
289 for (; start<sb->maxdir; ++start)
293 ((unsigned char)sb->dir[start].status)<=(sb->type==CPMFS_P2DOS ? 31 : 15)
294 && (extno==-1 || (EXTENT(sb->dir[start].extnol,sb->dir[start].extnoh)/sb->extents)==(extno/sb->extents))
295 && isMatching(user,name,ext,sb->dir[start].status,sb->dir[start].name,sb->dir[start].ext)
298 boo="file not found";
302 /* findFreeExtent -- find first free extent */ /*{{{*/
303 static int findFreeExtent(const struct cpmSuperBlock *drive)
307 for (i=0; i<drive->maxdir; ++i) if (drive->dir[i].status==(char)0xe5) return (i);
308 boo="directory full";
312 /* updateTimeStamps -- convert time stamps to CP/M format */ /*{{{*/
313 static void updateTimeStamps(const struct cpmInode *ino, int extent)
315 struct PhysDirectoryEntry *date;
318 int ca_min,ca_hour,ca_days,u_min,u_hour,u_days;
320 if (!S_ISREG(ino->mode)) return;
322 fprintf(stderr,"CPMFS: updating time stamps for inode %d (%d)\n",extent,extent&3);
324 /* compute ctime/atime */ /*{{{*/
325 t=localtime(ino->sb->cnotatime ? &ino->ctime : &ino->atime);
326 ca_min=((t->tm_min/10)<<4)|(t->tm_min%10);
327 ca_hour=((t->tm_hour/10)<<4)|(t->tm_hour%10);
328 for (i=1978,ca_days=0; i < 1900 + t->tm_year; ++i)
331 if (i%4==0 && (i%100!=0 || i%400==0)) ++ca_days;
333 ca_days += t->tm_yday + 1;
335 /* compute mtime */ /*{{{*/
336 t=localtime(&ino->mtime);
337 u_min=((t->tm_min/10)<<4)|(t->tm_min%10);
338 u_hour=((t->tm_hour/10)<<4)|(t->tm_hour%10);
339 for (i=1978,u_days=0; i < 1900 + t->tm_year; ++i)
342 if (i%4==0 && (i%100!=0 || i%400==0)) ++u_days;
344 u_days += t->tm_yday + 1;
346 if ((ino->sb->type==CPMFS_P2DOS || ino->sb->type==CPMFS_DR3) && (date=ino->sb->dir+(extent|3))->status==0x21)
350 case 0: /* first entry */ /*{{{*/
352 date->name[0]=ca_days&0xff; date->name[1]=ca_days>>8;
353 date->name[2]=ca_hour;
354 date->name[3]=ca_min;
355 date->name[4]=u_days&0xff; date->name[5]=u_days>>8;
356 date->name[6]=u_hour;
361 case 1: /* second entry */ /*{{{*/
363 date->ext[2]=ca_days&0xff; date->extnol=ca_days>>8;
366 date->blkcnt=u_days&0xff; date->pointers[0]=u_days>>8;
367 date->pointers[1]=u_hour;
368 date->pointers[2]=u_min;
372 case 2: /* third entry */ /*{{{*/
374 date->pointers[5]=ca_days&0xff; date->pointers[6]=ca_days>>8;
375 date->pointers[7]=ca_hour;
376 date->pointers[8]=ca_min;
377 date->pointers[9]=u_days&0xff; date->pointers[10]=u_days>>8;
378 date->pointers[11]=u_hour;
379 date->pointers[12]=u_min;
388 /* diskdefReadSuper -- read super block from diskdefs file */ /*{{{*/
389 static int diskdefReadSuper(struct cpmSuperBlock *d, const char *format)
393 int insideDef=0,found=0;
395 if ((fp=fopen(DISKDEFS,"r"))==(FILE*)0 && (fp=fopen("diskdefs","r"))==(FILE*)0)
397 fprintf(stderr,"%s: Neither " DISKDEFS " nor diskdefs could be opened.\n",cmd);
400 while (fgets(line,sizeof(line),fp)!=(char*)0)
405 for (argc=0; argc<1 && (argv[argc]=strtok(argc ? (char*)0 : line," \t\n")); ++argc);
406 if ((argv[argc]=strtok((char*)0,"\n"))!=(char*)0) ++argc;
409 if (argc==1 && strcmp(argv[0],"end")==0)
412 d->size=(d->secLength*d->sectrk*(d->tracks-d->boottrk))/d->blksiz;
413 if (d->extents==0) d->extents=((d->size>=256 ? 8 : 16)*d->blksiz)/16384;
414 if (d->extents==0) d->extents=1;
419 if (strcmp(argv[0],"seclen")==0) d->secLength=strtol(argv[1],(char**)0,0);
420 else if (strcmp(argv[0],"tracks")==0) d->tracks=strtol(argv[1],(char**)0,0);
421 else if (strcmp(argv[0],"sectrk")==0) d->sectrk=strtol(argv[1],(char**)0,0);
422 else if (strcmp(argv[0],"blocksize")==0) d->blksiz=strtol(argv[1],(char**)0,0);
423 else if (strcmp(argv[0],"maxdir")==0) d->maxdir=strtol(argv[1],(char**)0,0);
424 else if (strcmp(argv[0],"skew")==0) d->skew=strtol(argv[1],(char**)0,0);
425 else if (strcmp(argv[0],"boottrk")==0) d->boottrk=strtol(argv[1],(char**)0,0);
426 else if (strcmp(argv[0],"logicalextents")==0) d->extents=strtol(argv[1],(char**)0,0);
427 else if (strcmp(argv[0],"os")==0)
429 if (strcmp(argv[1],"2.2")==0) d->type=CPMFS_DR22;
430 else if (strcmp(argv[1],"3")==0) d->type=CPMFS_DR3;
431 else if (strcmp(argv[1],"p2dos")==0) d->type=CPMFS_P2DOS;
435 else if (argc==2 && strcmp(argv[0],"diskdef")==0)
441 if (strcmp(argv[1],format)==0) found=1;
447 fprintf(stderr,"%s: unknown format %s\n",cmd,format);
453 /* amsReadSuper -- read super block from amstrad disk */ /*{{{*/
454 static int amsReadSuper(struct cpmSuperBlock *d, const char *format)
456 unsigned char boot_sector[512], *boot_spec;
459 Device_setGeometry(&d->dev,512,9,40);
460 if ((err=Device_readSector(&d->dev, 0, 0, (char *)boot_sector)))
462 fprintf(stderr,"%s: Failed to read Amstrad superblock (%s)\n",cmd,err);
465 boot_spec=(boot_sector[0] == 0 || boot_sector[0] == 3)?boot_sector:(unsigned char*)0;
466 /* Check for JCE's extension to allow Amstrad and MSDOS superblocks
467 * in the same sector (for the PCW16)
471 (boot_sector[0] == 0xE9 || boot_sector[0] == 0xEB)
472 && !memcmp(boot_sector + 0x2B, "CP/M", 4)
473 && !memcmp(boot_sector + 0x33, "DSK", 3)
474 && !memcmp(boot_sector + 0x7C, "CP/M", 4)
475 ) boot_spec = boot_sector + 128;
476 if (boot_spec==(unsigned char*)0)
478 fprintf(stderr,"%s: Amstrad superblock not present\n",cmd);
481 /* boot_spec[0] = format number: 0 for SS SD, 3 for DS DD
482 [1] = single/double sided and density flags
483 [2] = cylinders per side
484 [3] = sectors per cylinder
485 [4] = Physical sector shift, 2 => 512
486 [5] = Reserved track count
488 [7] = No. of directory blocks
490 d->type = CPMFS_DR3; /* Amstrads are CP/M 3 systems */
491 d->secLength = 128 << boot_spec[4];
492 d->tracks = boot_spec[2];
493 if (boot_spec[1] & 3) d->tracks *= 2;
494 d->sectrk = boot_spec[3];
495 d->blksiz = 128 << boot_spec[6];
496 d->maxdir = (d->blksiz / 32) * boot_spec[7];
497 d->skew = 1; /* Amstrads skew at the controller level */
498 d->boottrk = boot_spec[5];
499 d->size = (d->secLength*d->sectrk*(d->tracks-d->boottrk))/d->blksiz;
500 d->extents = ((d->size>=256 ? 8 : 16)*d->blksiz)/16384;
506 /* match -- match filename against a pattern */ /*{{{*/
507 static int recmatch(const char *a, const char *pattern)
517 if (*a=='.' && first) return 1;
519 while (*a) if (recmatch(a,pattern)) return 1; else ++a;
524 if (*a) { ++a; ++pattern; } else return 0;
527 default: if (*a==*pattern) { ++a; ++pattern; } else return 0;
531 return (*pattern=='\0' && *a=='\0');
534 int match(const char *a, const char *pattern)
539 assert(strlen(pattern)<255);
540 if (isdigit(*pattern) && *(pattern+1)==':') { user=(*pattern-'0'); pattern+=2; }
541 else if (isdigit(*pattern) && isdigit(*(pattern+1)) && *(pattern+2)==':') { user=(10*(*pattern-'0')+(*(pattern+1)-'0')); pattern+=3; }
543 if (user==-1) sprintf(pat,"??%s",pattern);
544 else sprintf(pat,"%02d%s",user,pattern);
545 return recmatch(a,pat);
549 /* cpmglob -- expand CP/M style wildcards */ /*{{{*/
550 void cpmglob(int optin, int argc, char * const argv[], struct cpmInode *root, int *gargc, char ***gargv)
553 int entries,dirsize=0;
554 struct cpmDirent *dirent=(struct cpmDirent*)0;
559 cpmOpendir(root,&dir);
561 dirent=realloc(dirent,sizeof(struct cpmDirent)*(dirsize=32));
562 while (cpmReaddir(&dir,dirent+entries))
565 if (entries==dirsize) dirent=realloc(dirent,sizeof(struct cpmDirent)*(dirsize*=2));
567 for (i=optin; i<argc; ++i)
569 for (j=0; j<entries; ++j) if (i<argc && match(dirent[j].name,argv[i]))
571 if (*gargc==gargcap) *gargv=realloc(*gargv,sizeof(char*)*(gargcap ? (gargcap*=2) : (gargcap=16)));
572 (*gargv)[*gargc]=strcpy(malloc(strlen(dirent[j].name)+1),dirent[j].name);
579 /* cpmReadSuper -- get DPB and init in-core data for drive */ /*{{{*/
580 int cpmReadSuper(struct cpmSuperBlock *d, struct cpmInode *root, const char *format)
582 while (s_ifdir && !S_ISDIR(s_ifdir)) s_ifdir<<=1;
584 while (s_ifreg && !S_ISREG(s_ifreg)) s_ifreg<<=1;
586 if (strcmp(format, "amstrad")==0) amsReadSuper(d,format);
587 else diskdefReadSuper(d,format);
588 Device_setGeometry(&d->dev,d->secLength,d->sectrk,d->tracks);
589 /* generate skew table */ /*{{{*/
590 if (( d->skewtab = malloc(d->sectrk*sizeof(int))) == (int*)0)
592 fprintf(stderr,"%s: can not allocate memory for skew sector table\n",cmd);
595 if (strcmp(format,"apple-do")==0)
597 static int skew[]={0,6,12,3,9,15,14,5,11,2,8,7,13,4,10,1};
598 memcpy(d->skewtab,skew,d->sectrk*sizeof(int));
600 else if (strcmp(format,"apple-po")==0)
602 static int skew[]={0,9,3,12,6,15,1,10,4,13,7,8,2,11,5,14};
603 memcpy(d->skewtab,skew,d->sectrk*sizeof(int));
609 for (i=j=0; i<d->sectrk; ++i,j=(j+d->skew)%d->sectrk)
613 for (k=0; k<i && d->skewtab[k]!=j; ++k);
614 if (k<i) j=(j+1)%d->sectrk;
621 /* initialise allocation vector bitmap */ /*{{{*/
623 d->alvSize=((d->secLength*d->sectrk*(d->tracks-d->boottrk))/d->blksiz+INTBITS-1)/INTBITS;
624 if ((d->alv=malloc(d->alvSize*sizeof(int)))==(int*)0)
631 /* allocate directory buffer */ /*{{{*/
632 if ((d->dir=malloc(d->maxdir*32))==(struct PhysDirectoryEntry*)0)
638 if (d->dev.opened==0) memset(d->dir,0xe5,d->maxdir*32);
639 else if (readPhysDirectory(d)==-1) return -1;
641 if (d->type==CPMFS_DR3) /* read additional superblock information */ /*{{{*/
645 /* passwords */ /*{{{*/
649 for (i=0; i<d->maxdir; ++i) if (d->dir[i].status>=16 && d->dir[i].status<=31) ++passwords;
651 fprintf(stderr,"getformat: found %d passwords\n",passwords);
653 if ((d->passwdLength=passwords*PASSWD_RECLEN))
655 if ((d->passwd=malloc(d->passwdLength))==(char*)0)
660 for (i=0,passwords=0; i<d->maxdir; ++i) if (d->dir[i].status>=16 && d->dir[i].status<=31)
663 char *p=d->passwd+(passwords++*PASSWD_RECLEN);
665 p[0]='0'+(d->dir[i].status-16)/10;
666 p[1]='0'+(d->dir[i].status-16)%10;
667 for (j=0; j<8; ++j) p[2+j]=d->dir[i].name[j]&0x7f;
668 p[10]=(d->dir[i].ext[0]&0x7f)==' ' ? ' ' : '.';
669 for (j=0; j<3; ++j) p[11+j]=d->dir[i].ext[j]&0x7f;
671 pb=(unsigned char)d->dir[i].lrc;
672 for (j=0; j<8; ++j) p[15+j]=((unsigned char)d->dir[i].pointers[7-j])^pb;
675 fprintf(stderr,"getformat: %s\n",p);
682 /* disc label */ /*{{{*/
683 for (i=0; i<d->maxdir; ++i) if (d->dir[i].status==(char)0x20)
687 d->cnotatime=d->dir[i].extnol&0x10;
688 if (d->dir[i].extnol&0x1)
691 if ((d->label=malloc(d->labelLength))==(char*)0)
696 for (j=0; j<8; ++j) d->label[j]=d->dir[i].name[j]&0x7f;
697 for (j=0; j<3; ++j) d->label[8+j]=d->dir[i].ext[j]&0x7f;
723 root->mode=(s_ifdir|0777);
725 root->atime=root->mtime=root->ctime=0;
729 /* cpmNamei -- map name to inode */ /*{{{*/
730 int cpmNamei(const struct cpmInode *dir, const char *filename, struct cpmInode *i)
732 /* variables */ /*{{{*/
734 char name[8],extension[3];
735 struct PhysDirectoryEntry *date;
737 static char gmt0[]="TZ=GMT0";
738 static char *gmt_env[]={ gmt0, (char*)0 };
739 int highestExtno,highestExt=-1,lowestExtno,lowestExt=-1;
743 if (!S_ISDIR(dir->mode))
748 if (strcmp(filename,".")==0 || strcmp(filename,"..")==0) /* root directory */ /*{{{*/
754 else if (strcmp(filename,"[passwd]")==0 && dir->sb->passwdLength) /* access passwords */ /*{{{*/
757 i->ino=dir->sb->maxdir+1;
758 i->mode=s_ifreg|0444;
760 i->atime=i->mtime=i->ctime=0;
761 i->size=i->sb->passwdLength;
765 else if (strcmp(filename,"[label]")==0 && dir->sb->labelLength) /* access label */ /*{{{*/
768 i->ino=dir->sb->maxdir+2;
769 i->mode=s_ifreg|0444;
771 i->atime=i->mtime=i->ctime=0;
772 i->size=i->sb->labelLength;
776 if (splitFilename(filename,dir->sb->type,name,extension,&user)==-1) return -1;
777 /* find highest and lowest extent */ /*{{{*/
785 while ((extent=findFileExtent(dir->sb,user,name,extension,extent+1,-1))!=-1)
787 int extno=EXTENT(dir->sb->dir[extent].extnol,dir->sb->dir[extent].extnoh);
789 if (extno>highestExtno)
794 if (extno<lowestExtno)
802 if (highestExtno==-1) return -1;
803 /* calculate size */ /*{{{*/
807 i->size=highestExtno*16384;
808 if (dir->sb->size<256) for (block=15; block>=0; --block)
810 if (dir->sb->dir[highestExt].pointers[block]) break;
812 else for (block=7; block>=0; --block)
814 if (dir->sb->dir[highestExt].pointers[2*block] || dir->sb->dir[highestExt].pointers[2*block+1]) break;
816 if (dir->sb->dir[highestExt].blkcnt) i->size+=((dir->sb->dir[highestExt].blkcnt&0xff)-1)*128;
817 i->size+=dir->sb->dir[highestExt].lrc ? (dir->sb->dir[highestExt].lrc&0xff) : 128;
819 fprintf(stderr,"cpmNamei: size=%ld\n",(long)i->size);
827 environ=gmt_env; /* for mktime() */
830 (dir->sb->type==CPMFS_P2DOS || dir->sb->type==CPMFS_DR3)
831 && (date=dir->sb->dir+(lowestExt|3))->status==0x21
833 /* set time stamps */ /*{{{*/
835 /* variables */ /*{{{*/
836 int u_days=0,u_hour=0,u_min=0;
837 int ca_days=0,ca_hour=0,ca_min=0;
843 case 0: /* first entry of the four */ /*{{{*/
845 ca_days=((unsigned char)date->name[0])+(((unsigned char)date->name[1])<<8);
846 ca_hour=(unsigned char)date->name[2];
847 ca_min=(unsigned char)date->name[3];
848 u_days=((unsigned char)date->name[4])+(((unsigned char)date->name[5])<<8);
849 u_hour=(unsigned char)date->name[6];
850 u_min=(unsigned char)date->name[7];
851 protectMode=(unsigned char)date->name[8];
855 case 1: /* second entry */ /*{{{*/
857 ca_days=((unsigned char)date->ext[2])+(((unsigned char)date->extnol)<<8);
858 ca_hour=(unsigned char)date->lrc;
859 ca_min=(unsigned char)date->extnoh;
860 u_days=((unsigned char)date->blkcnt)+(((unsigned char)date->pointers[0])<<8);
861 u_hour=(unsigned char)date->pointers[1];
862 u_min=(unsigned char)date->pointers[2];
863 protectMode=(unsigned char)date->pointers[3];
867 case 2: /* third one */ /*{{{*/
869 ca_days=((unsigned char)date->pointers[5])+(((unsigned char)date->pointers[6])<<8);
870 ca_hour=(unsigned char)date->pointers[7];
871 ca_min=(unsigned char)date->pointers[8];
872 u_days=((unsigned char)date->pointers[9])+(((unsigned char)date->pointers[10])<<8);
873 u_hour=(unsigned char)date->pointers[11];
874 u_min=(unsigned char)date->pointers[12];
875 protectMode=(unsigned char)date->pointers[13];
880 /* compute CP/M to UNIX time format */ /*{{{*/
882 tms.tm_min=((ca_min>>4)&0xf)*10+(ca_min&0xf);
883 tms.tm_hour=((ca_hour>>4)&0xf)*10+(ca_hour&0xf);
888 if (i->sb->cnotatime)
890 i->ctime=mktime(&tms)+(ca_days-1)*24*3600;
896 i->atime=mktime(&tms)+(ca_days-1)*24*3600;
898 tms.tm_min=((u_min>>4)&0xf)*10+(u_min&0xf);
899 tms.tm_hour=((u_hour>>4)&0xf)*10+(u_hour&0xf);
900 i->mtime=mktime(&tms)+(u_days-1)*24*3600;
904 else i->atime=i->mtime=i->ctime=0;
907 /* Determine the inode attributes */
909 if (dir->sb->dir[lowestExt].name[0]&0x80) i->attr |= CPM_ATTR_F1;
910 if (dir->sb->dir[lowestExt].name[1]&0x80) i->attr |= CPM_ATTR_F2;
911 if (dir->sb->dir[lowestExt].name[2]&0x80) i->attr |= CPM_ATTR_F3;
912 if (dir->sb->dir[lowestExt].name[3]&0x80) i->attr |= CPM_ATTR_F4;
913 if (dir->sb->dir[lowestExt].ext [0]&0x80) i->attr |= CPM_ATTR_RO;
914 if (dir->sb->dir[lowestExt].ext [1]&0x80) i->attr |= CPM_ATTR_SYS;
915 if (dir->sb->dir[lowestExt].ext [2]&0x80) i->attr |= CPM_ATTR_ARCV;
916 if (protectMode&0x20) i->attr |= CPM_ATTR_PWDEL;
917 if (protectMode&0x40) i->attr |= CPM_ATTR_PWWRITE;
918 if (protectMode&0x80) i->attr |= CPM_ATTR_PWREAD;
920 if (dir->sb->dir[lowestExt].ext[1]&0x80) i->mode|=01000;
922 if (!(dir->sb->dir[lowestExt].ext[0]&0x80)) i->mode|=0222;
923 if (extension[0]=='C' && extension[1]=='O' && extension[2]=='M') i->mode|=0111;
927 /* cpmStatFS -- statfs */ /*{{{*/
928 void cpmStatFS(const struct cpmInode *ino, struct cpmStatFS *buf)
931 struct cpmSuperBlock *d;
934 buf->f_bsize=d->blksiz;
935 buf->f_blocks=(d->tracks*d->sectrk*d->secLength)/d->blksiz;
937 buf->f_bused=-(d->maxdir*32+d->blksiz-1)/d->blksiz;
938 for (i=0; i<d->alvSize; ++i)
943 for (j=0; j<INTBITS; ++j)
945 if (i*INTBITS+j < d->size)
950 fprintf(stderr,"cpmStatFS: block %d allocated\n",(i*INTBITS+j));
959 buf->f_bavail=buf->f_bfree;
960 buf->f_files=d->maxdir;
962 for (i=0; i<d->maxdir; ++i)
964 if (d->dir[i].status==(char)0xe5) ++buf->f_ffree;
969 /* cpmUnlink -- unlink */ /*{{{*/
970 int cpmUnlink(const struct cpmInode *dir, const char *fname)
973 char name[8],extension[3];
975 struct cpmSuperBlock *drive;
977 if (!S_ISDIR(dir->mode))
983 if (splitFilename(fname,dir->sb->type,name,extension,&user)==-1) return -1;
984 if ((extent=findFileExtent(drive,user,name,extension,0,-1))==-1) return -1;
985 drive->dir[extent].status=(char)0xe5;
988 drive->dir[extent].status=(char)0xe5;
989 } while ((extent=findFileExtent(drive,user,name,extension,extent+1,-1))>=0);
990 if (writePhysDirectory(drive)==-1) return -1;
995 /* cpmRename -- rename */ /*{{{*/
996 int cpmRename(const struct cpmInode *dir, const char *old, const char *new)
998 struct cpmSuperBlock *drive;
1001 char oldname[8], oldext[3];
1003 char newname[8], newext[3];
1005 if (!S_ISDIR(dir->mode))
1011 if (splitFilename(old,dir->sb->type, oldname, oldext,&olduser)==-1) return -1;
1012 if (splitFilename(new,dir->sb->type, newname, newext,&newuser)==-1) return -1;
1013 if ((extent=findFileExtent(drive,olduser,oldname,oldext,0,-1))==-1) return -1;
1014 if (findFileExtent(drive,newuser,newname, newext,0,-1)!=-1)
1016 boo="file already exists";
1021 drive->dir[extent].status=newuser;
1022 memcpy7(drive->dir[extent].name, newname, 8);
1023 memcpy7(drive->dir[extent].ext, newext, 3);
1024 } while ((extent=findFileExtent(drive,olduser,oldname,oldext,extent+1,-1))!=-1);
1025 if (writePhysDirectory(drive)==-1) return -1;
1029 /* cpmOpendir -- opendir */ /*{{{*/
1030 int cpmOpendir(struct cpmInode *dir, struct cpmFile *dirp)
1032 if (!S_ISDIR(dir->mode))
1039 dirp->mode=O_RDONLY;
1043 /* cpmReaddir -- readdir */ /*{{{*/
1044 int cpmReaddir(struct cpmFile *dir, struct cpmDirent *ent)
1046 /* variables */ /*{{{*/
1047 struct PhysDirectoryEntry *cur=(struct PhysDirectoryEntry*)0;
1054 if (!(S_ISDIR(dir->ino->mode))) /* error: not a directory */ /*{{{*/
1056 boo="not a directory";
1062 if (dir->pos==0) /* first entry is . */ /*{{{*/
1064 ent->ino=dir->ino->sb->maxdir;
1066 strcpy(ent->name,".");
1072 else if (dir->pos==1) /* next entry is .. */ /*{{{*/
1074 ent->ino=dir->ino->sb->maxdir;
1076 strcpy(ent->name,"..");
1082 else if (dir->pos==2)
1084 if (dir->ino->sb->passwdLength) /* next entry is [passwd] */ /*{{{*/
1086 ent->ino=dir->ino->sb->maxdir+1;
1088 strcpy(ent->name,"[passwd]");
1095 else if (dir->pos==3)
1097 if (dir->ino->sb->labelLength) /* next entry is [label] */ /*{{{*/
1099 ent->ino=dir->ino->sb->maxdir+2;
1101 strcpy(ent->name,"[label]");
1108 else if (dir->pos>=RESERVED_ENTRIES && dir->pos<dir->ino->sb->maxdir+RESERVED_ENTRIES)
1110 int first=dir->pos-RESERVED_ENTRIES;
1112 if ((cur=dir->ino->sb->dir+(dir->pos-RESERVED_ENTRIES))->status>=0 && cur->status<=(dir->ino->sb->type==CPMFS_P2DOS ? 31 : 15))
1114 /* determine first extent for the current file */ /*{{{*/
1115 for (i=0; i<dir->ino->sb->maxdir; ++i) if (i!=(dir->pos-RESERVED_ENTRIES))
1117 if (isMatching(cur->status,cur->name,cur->ext,dir->ino->sb->dir[i].status,dir->ino->sb->dir[i].name,dir->ino->sb->dir[i].ext) && EXTENT(cur->extnol,cur->extnoh)>EXTENT(dir->ino->sb->dir[i].extnol,dir->ino->sb->dir[i].extnoh)) first=i;
1120 if (first==(dir->pos-RESERVED_ENTRIES))
1122 ent->ino=dir->pos-RESERVED_INODES;
1123 /* convert file name to UNIX style */ /*{{{*/
1124 buf[0]='0'+cur->status/10;
1125 buf[1]='0'+cur->status%10;
1126 for (bufp=buf+2,i=0; i<8 && (cur->name[i]&0x7f)!=' '; ++i) *bufp++=tolower(cur->name[i]&0x7f);
1127 for (hasext=0,i=0; i<3 && (cur->ext[i]&0x7f)!=' '; ++i)
1129 if (!hasext) { *bufp++='.'; hasext=1; }
1130 *bufp++=tolower(cur->ext[i]&0x7f);
1134 ent->reclen=strlen(buf);
1135 strcpy(ent->name,buf);
1147 /* cpmStat -- stat */ /*{{{*/
1148 void cpmStat(const struct cpmInode *ino, struct cpmStat *buf)
1151 buf->mode=ino->mode;
1152 buf->size=ino->size;
1153 buf->atime=ino->atime;
1154 buf->mtime=ino->mtime;
1155 buf->ctime=ino->ctime;
1158 /* cpmOpen -- open */ /*{{{*/
1159 int cpmOpen(struct cpmInode *ino, struct cpmFile *file, mode_t mode)
1161 if (S_ISREG(ino->mode))
1163 if ((mode&O_WRONLY) && (ino->mode&0222)==0)
1165 boo="permission denied";
1175 boo="not a regular file";
1180 /* cpmRead -- read */ /*{{{*/
1181 int cpmRead(struct cpmFile *file, char *buf, int count)
1183 int findext=1,findblock=1,extent=-1,block=-1,extentno=-1,got=0,nextblockpos=-1,nextextpos=-1;
1184 int blocksize=file->ino->sb->blksiz;
1187 extcap=(file->ino->sb->size<256 ? 16 : 8)*blocksize;
1188 if (extcap>16384) extcap=16384*file->ino->sb->extents;
1189 if (file->ino->ino==file->ino->sb->maxdir+1) /* [passwd] */ /*{{{*/
1191 if ((file->pos+count)>file->ino->size) count=file->ino->size-file->pos;
1192 if (count) memcpy(buf,file->ino->sb->passwd+file->pos,count);
1195 fprintf(stderr,"cpmRead passwd: read %d bytes, now at position %ld\n",count,(long)file->pos);
1200 else if (file->ino->ino==file->ino->sb->maxdir+2) /* [label] */ /*{{{*/
1202 if ((file->pos+count)>file->ino->size) count=file->ino->size-file->pos;
1203 if (count) memcpy(buf,file->ino->sb->label+file->pos,count);
1206 fprintf(stderr,"cpmRead label: read %d bytes, now at position %ld\n",count,(long)file->pos);
1211 else while (count>0 && file->pos<file->ino->size)
1217 extentno=file->pos/16384;
1218 extent=findFileExtent(file->ino->sb,file->ino->sb->dir[file->ino->ino].status,file->ino->sb->dir[file->ino->ino].name,file->ino->sb->dir[file->ino->ino].ext,0,extentno);
1219 nextextpos=(file->pos/extcap)*extcap+extcap;
1229 ptr=(file->pos%extcap)/blocksize;
1230 if (file->ino->sb->size>=256) ptr*=2;
1231 block=(unsigned char)file->ino->sb->dir[extent].pointers[ptr];
1232 if (file->ino->sb->size>=256) block+=((unsigned char)file->ino->sb->dir[extent].pointers[ptr+1])<<8;
1235 memset(buffer,0,blocksize);
1239 start=(file->pos%blocksize)/file->ino->sb->secLength;
1240 end=((file->pos%blocksize+count)>blocksize ? blocksize-1 : (file->pos%blocksize+count-1))/file->ino->sb->secLength;
1241 readBlock(file->ino->sb,block,buffer,start,end);
1244 nextblockpos=(file->pos/blocksize)*blocksize+blocksize;
1247 if (file->pos<nextblockpos)
1249 if (extent==-1) *buf++='\0'; else *buf++=buffer[file->pos%blocksize];
1254 else if (file->pos==nextextpos) findext=1; else findblock=1;
1257 fprintf(stderr,"cpmRead: read %d bytes, now at position %ld\n",got,(long)file->pos);
1262 /* cpmWrite -- write */ /*{{{*/
1263 int cpmWrite(struct cpmFile *file, const char *buf, int count)
1265 int findext=1,findblock=1,extent=-1,extentno=-1,got=0,nextblockpos=-1,nextextpos=-1;
1266 int blocksize=file->ino->sb->blksiz;
1267 int extcap=(file->ino->sb->size<256 ? 16 : 8)*blocksize;
1268 int block=-1,start=-1,end=-1,ptr=-1;
1275 extentno=file->pos/16384;
1276 extent=findFileExtent(file->ino->sb,file->ino->sb->dir[file->ino->ino].status,file->ino->sb->dir[file->ino->ino].name,file->ino->sb->dir[file->ino->ino].ext,0,extentno);
1277 nextextpos=(file->pos/extcap)*extcap+extcap;
1280 updateTimeStamps(file->ino,extent);
1288 last=writeBlock(file->ino->sb,block,buffer,start,end);
1289 if (file->ino->sb->size<256) for (last=15; last>=ptr; --last)
1291 if (file->ino->sb->dir[extent].pointers[last]) break;
1293 else for (last=14; last>=ptr; last-=2)
1295 if (file->ino->sb->dir[extent].pointers[last] || file->ino->sb->dir[extent].pointers[last+1]) break;
1297 if (last==ptr) /* we wrote the last used block of this extent */
1299 file->ino->sb->dir[extent].extnol=EXTENTL((file->pos-1)/16384);
1300 file->ino->sb->dir[extent].extnoh=EXTENTH((file->pos-1)/16384);
1301 file->ino->sb->dir[extent].blkcnt=((file->pos-1)%16384)/128+1;
1302 file->ino->sb->dir[extent].lrc=file->pos%128;
1307 if ((extent=findFreeExtent(file->ino->sb))==-1) return (got==0 ? -1 : got);
1308 file->ino->sb->dir[extent]=file->ino->sb->dir[file->ino->ino];
1309 memset(file->ino->sb->dir[extent].pointers,0,16);
1310 file->ino->sb->dir[extent].extnol=EXTENTL(extentno);
1311 file->ino->sb->dir[extent].extnoh=EXTENTH(extentno);
1312 file->ino->sb->dir[extent].blkcnt=0;
1313 file->ino->sb->dir[extent].lrc=0;
1315 ptr=(file->pos%extcap)/blocksize;
1316 if (file->ino->sb->size>=256) ptr*=2;
1317 block=(unsigned char)file->ino->sb->dir[extent].pointers[ptr];
1318 if (file->ino->sb->size>=256) block+=((unsigned char)file->ino->sb->dir[extent].pointers[ptr+1])<<8;
1321 if ((block=allocBlock(file->ino->sb))==-1) return (got==0 ? -1 : got);
1322 file->ino->sb->dir[extent].pointers[ptr]=block&0xff;
1323 if (file->ino->sb->size>=256) file->ino->sb->dir[extent].pointers[ptr+1]=(block>>8)&0xff;
1325 end=(blocksize-1)/file->ino->sb->secLength;
1326 memset(buffer,0,blocksize);
1330 start=(file->pos%blocksize)/file->ino->sb->secLength;
1331 end=((file->pos%blocksize+count)>blocksize ? blocksize-1 : (file->pos%blocksize+count-1))/file->ino->sb->secLength;
1332 if (file->pos%file->ino->sb->secLength) readBlock(file->ino->sb,block,buffer,start,start);
1333 if (end!=start && (file->pos+count-1)<blocksize) readBlock(file->ino->sb,block,buffer+end*file->ino->sb->secLength,end,end);
1335 nextblockpos=(file->pos/blocksize)*blocksize+blocksize;
1338 buffer[file->pos%blocksize]=*buf++;
1340 if (file->ino->size<file->pos) file->ino->size=file->pos;
1342 if (file->pos==nextblockpos) { if (file->pos==nextextpos) findext=1; else findblock=1; }
1349 last=writeBlock(file->ino->sb,block,buffer,start,end);
1350 if (file->ino->sb->size<256) for (last=15; last>=ptr; --last)
1352 if (file->ino->sb->dir[extent].pointers[last]) break;
1354 else for (last=14; last>=ptr; last-=2)
1356 if (file->ino->sb->dir[extent].pointers[last] || file->ino->sb->dir[extent].pointers[last+1]) break;
1358 if (last==ptr) /* we wrote the last used block of this extent */
1360 file->ino->sb->dir[extent].extnol=EXTENTL((file->pos-1)/16384);
1361 file->ino->sb->dir[extent].extnoh=EXTENTH((file->pos-1)/16384);
1362 file->ino->sb->dir[extent].blkcnt=((file->pos-1)%16384)/128+1;
1363 file->ino->sb->dir[extent].lrc=file->pos%128;
1364 writePhysDirectory(file->ino->sb);
1370 /* cpmClose -- close */ /*{{{*/
1371 int cpmClose(struct cpmFile *file)
1373 if (file->mode&O_WRONLY) return (writePhysDirectory(file->ino->sb));
1377 /* cpmCreat -- creat */ /*{{{*/
1378 int cpmCreat(struct cpmInode *dir, const char *fname, struct cpmInode *ino, mode_t mode)
1381 char name[8],extension[3];
1383 struct cpmSuperBlock *drive;
1384 struct PhysDirectoryEntry *ent;
1386 if (!S_ISDIR(dir->mode))
1388 boo="No such file or directory";
1391 if (splitFilename(fname,dir->sb->type,name,extension,&user)==-1) return -1;
1393 fprintf(stderr,"cpmCreat: %s -> %d:%-.8s.%-.3s\n",fname,user,name,extension);
1395 if (findFileExtent(dir->sb,user,name,extension,0,-1)!=-1) return -1;
1397 if ((extent=findFreeExtent(dir->sb))==-1) return -1;
1398 ent=dir->sb->dir+extent;
1401 memcpy(ent->name,name,8);
1402 memcpy(ent->ext,extension,3);
1404 ino->mode=s_ifreg|mode;
1410 updateTimeStamps(ino,extent);
1411 writePhysDirectory(dir->sb);
1415 /* cpmAttrGet -- get CP/M attributes */ /*{{{*/
1416 int cpmAttrGet(struct cpmInode *ino, cpm_attr_t *attrib)
1418 *attrib = ino->attr;
1422 /* cpmAttrSet -- set CP/M attributes */ /*{{{*/
1423 int cpmAttrSet(struct cpmInode *ino, cpm_attr_t attrib)
1425 struct cpmSuperBlock *drive;
1428 char name[8], extension[3];
1430 memset(name, 0, sizeof(name));
1431 memset(extension, 0, sizeof(extension));
1435 /* Strip off existing attribute bits */
1436 memcpy7(name, drive->dir[extent].name, 8);
1437 memcpy7(extension, drive->dir[extent].ext, 3);
1438 user = drive->dir[extent].status;
1440 /* And set new ones */
1441 if (attrib & CPM_ATTR_F1) name[0] |= 0x80;
1442 if (attrib & CPM_ATTR_F2) name[1] |= 0x80;
1443 if (attrib & CPM_ATTR_F3) name[2] |= 0x80;
1444 if (attrib & CPM_ATTR_F4) name[3] |= 0x80;
1445 if (attrib & CPM_ATTR_RO) extension[0] |= 0x80;
1446 if (attrib & CPM_ATTR_SYS) extension[1] |= 0x80;
1447 if (attrib & CPM_ATTR_ARCV) extension[2] |= 0x80;
1451 memcpy(drive->dir[extent].name, name, 8);
1452 memcpy(drive->dir[extent].ext, extension, 3);
1453 } while ((extent=findFileExtent(drive, user,name,extension,extent+1,-1))!=-1);
1454 if (writePhysDirectory(drive)==-1) return -1;
1456 /* Update the stored (inode) copies of the file attributes and mode */
1458 if (attrib&CPM_ATTR_RO) ino->mode&=~(S_IWUSR|S_IWGRP|S_IWOTH);
1459 else ino->mode|=(S_IWUSR|S_IWGRP|S_IWOTH);
1464 /* cpmChmod -- set CP/M r/o & sys */ /*{{{*/
1465 int cpmChmod(struct cpmInode *ino, mode_t mode)
1467 /* Convert the chmod() into a chattr() call that affects RO */
1468 int newatt = ino->attr & ~CPM_ATTR_RO;
1470 if ((mode & (S_IWUSR|S_IWGRP|S_IWOTH))) newatt |= CPM_ATTR_RO;
1471 return cpmAttrSet(ino, newatt);
1474 /* cpmSync -- write directory back */ /*{{{*/
1475 int cpmSync(struct cpmSuperBlock *sb)
1477 return (writePhysDirectory(sb));
1480 /* cpmUmount -- free super block */ /*{{{*/
1481 void cpmUmount(struct cpmSuperBlock *sb)
1486 if (sb->passwdLength) free(sb->passwd);