1 /* #includes */ /*{{{C}}}*//*{{{*/
16 /* #defines */ /*{{{*/
19 /* Number of _used_ bits per int */
21 #define INTBITS ((int)(sizeof(int)*8))
23 /* Convert BCD datestamp digits to binary */
25 #define BCD2BIN(x) ((((x)>>4)&0xf)*10 + ((x)&0xf))
27 #define BIN2BCD(x) (((((x)/10)&0xf)<<4) + (((x)%10)&0xf))
29 /* There are four reserved directory entries: ., .., [passwd] and [label].
30 The first two of them refer to the same inode. */
32 #define RESERVED_ENTRIES 4
34 /* CP/M does not support any kind of inodes, so they are simulated.
35 Inode 0-(maxdir-1) correlate to the lowest extent number (not the first
36 extent of the file in the directory) of a file. Inode maxdir is the
37 root directory, inode maxdir+1 is the optional passwd file and inode
38 maxdir+2 the optional disk label. */
40 #define RESERVED_INODES 3
42 #define PASSWD_RECLEN 24
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, char const *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(char const *fullname, int type, char *name, char *ext, int *user)
67 assert(fullname!=(char const *)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') || *user>=((type&CPMFS_HI_USER) ? 32 : 16))
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, char const *name1, char const *ext1, int user2, char const *name2, char const *ext2)
114 assert(name1!=(char const *)0);
115 assert(ext1!=(char const *)0);
116 assert(name2!=(char const *)0);
117 assert(ext2!=(char const *)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 /* time conversions */
126 /* cpm2unix_time -- convert CP/M time to UTC */ /*{{{*/
127 static time_t cpm2unix_time(int days, int hour, int min)
129 /* CP/M stores timestamps in local time. We don't know which */
130 /* timezone was used and if DST was in effect. Assuming it was */
131 /* the current offset from UTC is most sensible, but not perfect. */
133 int year,days_per_year;
134 static int days_per_month[]={31,0,31,30,31,30,31,31,30,31,30,31};
136 static char gmt0[]="TZ=GMT0";
137 static char *gmt_env[]={ gmt0, (char*)0 };
150 tms.tm_min=((min>>4)&0xf)*10+(min&0xf);
151 tms.tm_hour=((hour>>4)&0xf)*10+(hour&0xf);
158 year=tms.tm_year+1900;
159 days_per_year=((year%4)==0 && ((year%100) || (year%400)==0)) ? 366 : 365;
160 if (days>days_per_year)
169 days_per_month[1]=(days_per_year==366) ? 29 : 28;
170 if (days>days_per_month[tms.tm_mon])
172 days-=days_per_month[tms.tm_mon];
177 t=mktime(&tms)+(days-1)*24*3600;
183 /* unix2cpm_time -- convert UTC to CP/M time */ /*{{{*/
184 static void unix2cpm_time(time_t now, int *days, int *hour, int *min)
190 *min=((tms->tm_min/10)<<4)|(tms->tm_min%10);
191 *hour=((tms->tm_hour/10)<<4)|(tms->tm_hour%10);
192 for (i=1978,*days=0; i<1900+tms->tm_year; ++i)
195 if (i%4==0 && (i%100!=0 || i%400==0)) ++*days;
197 *days += tms->tm_yday+1;
200 /* ds2unix_time -- convert DS to Unix time */ /*{{{*/
201 static time_t ds2unix_time(const struct dsEntry *entry)
206 if (entry->minute==0 &&
210 entry->year==0) return 0;
214 tms.tm_min = BCD2BIN( entry->minute );
215 tms.tm_hour = BCD2BIN( entry->hour );
216 tms.tm_mday = BCD2BIN( entry->day );
217 tms.tm_mon = BCD2BIN( entry->month ) - 1;
219 yr = BCD2BIN(entry->year);
226 /* unix2ds_time -- convert Unix to DS time */ /*{{{*/
227 static void unix2ds_time(time_t now, struct dsEntry *entry)
234 entry->minute=entry->hour=entry->day=entry->month=entry->year = 0;
239 entry->minute = BIN2BCD( tms->tm_min );
240 entry->hour = BIN2BCD( tms->tm_hour );
241 entry->day = BIN2BCD( tms->tm_mday );
242 entry->month = BIN2BCD( tms->tm_mon + 1 );
245 if ( yr>100 ) yr -= 100;
246 entry->year = BIN2BCD( yr );
251 /* allocation vector bitmap functions */
252 /* alvInit -- init allocation vector */ /*{{{*/
253 static void alvInit(const struct cpmSuperBlock *d)
255 int i,j,offset,block;
257 assert(d!=(const struct cpmSuperBlock*)0);
258 /* clean bitmap */ /*{{{*/
259 memset(d->alv,0,d->alvSize*sizeof(int));
261 /* mark directory blocks as used */ /*{{{*/
262 /* A directory may cover more blocks than an int may hold bits,
263 * so a loop is needed.
265 for (block=0; block<d->dirblks; ++block)
267 offset = block/INTBITS;
268 d->alv[offset] |= (1<<(block%INTBITS));
270 fprintf(stderr,"alvInit: allocate directory block %d\n",block);
274 for (i=0; i<d->maxdir; ++i) /* mark file blocks as used */ /*{{{*/
276 if (d->dir[i].status>=0 && d->dir[i].status<=(d->type&CPMFS_HI_USER ? 31 : 15))
279 fprintf(stderr,"alvInit: allocate extent %d\n",i);
283 block=(unsigned char)d->dir[i].pointers[j];
284 if (d->size>256) block+=(((unsigned char)d->dir[i].pointers[++j])<<8);
285 if (block && block<d->size)
288 fprintf(stderr,"alvInit: allocate block %d\n",block);
290 offset=block/INTBITS;
291 d->alv[offset]|=(1<<block%INTBITS);
299 /* allocBlock -- allocate a new disk block */ /*{{{*/
300 static int allocBlock(const struct cpmSuperBlock *drive)
304 assert(drive!=(const struct cpmSuperBlock*)0);
305 for (i=0; i<drive->alvSize; ++i)
307 for (j=0,bits=drive->alv[i]; j<INTBITS; ++j)
312 if (block>=drive->size)
318 fprintf(stderr,"allocBlock: allocate data block %d\n",block);
320 drive->alv[i] |= (1<<j);
331 /* bootOffset -- find the logical sector number of the CP/M directory */ /*{{{*/
332 static int bootOffset(const struct cpmSuperBlock *d)
335 if (d->bootsec >= 0) return d->bootsec;
336 return d->boottrk * d->sectrk;
340 /* logical block I/O */
341 /* readBlock -- read a (partial) block */ /*{{{*/
342 static int readBlock(const struct cpmSuperBlock *d, int blockno, char *buffer, int start, int end)
344 int sect, track, counter;
351 fprintf(stderr,"readBlock: read block %d %d-%d\n",blockno,start,end);
353 if (blockno>=d->size)
355 boo="Attempting to access block beyond end of disk";
358 if (end<0) end=d->blksiz/d->secLength-1;
359 sect =(blockno*(d->blksiz/d->secLength)+ bootOffset(d))%d->sectrk;
360 track=(blockno*(d->blksiz/d->secLength)+ bootOffset(d))/d->sectrk;
361 for (counter=0; counter<=end; ++counter)
365 assert(d->skewtab[sect]>=0);
366 assert(d->skewtab[sect]<d->sectrk);
370 fprintf(stderr,"readBlock: read sector %d/%d\n",d->skewtab[sect],track);
372 if ((err=Device_readSector(&d->dev,track,d->skewtab[sect],buffer+(d->secLength*counter))))
388 /* writeBlock -- write a (partial) block */ /*{{{*/
389 static int writeBlock(const struct cpmSuperBlock *d, int blockno, char const *buffer, int start, int end)
391 int sect, track, counter;
394 assert(blockno<d->size);
395 assert(buffer!=(char const *)0);
398 fprintf(stderr,"writeBlock: write block %d %d-%d\n",blockno,start,end);
400 if (end < 0) end=d->blksiz/d->secLength-1;
401 sect = (blockno*(d->blksiz/d->secLength) + bootOffset(d)) % d->sectrk;
402 track = (blockno*(d->blksiz/d->secLength) + bootOffset(d)) / d->sectrk;
403 for (counter = 0; counter<=end; ++counter)
407 if (counter>=start && (err=Device_writeSector(&d->dev,track,d->skewtab[sect],buffer+(d->secLength*counter))))
423 /* directory management */
424 /* findFileExtent -- find first/next extent for a file */ /*{{{*/
425 static int findFileExtent(const struct cpmSuperBlock *sb, int user, char const *name, char const *ext, int start, int extno)
427 boo="file already exists";
428 for (; start<sb->maxdir; ++start)
432 ((unsigned char)sb->dir[start].status)<=(sb->type&CPMFS_HI_USER ? 31 : 15)
433 && (extno==-1 || (EXTENT(sb->dir[start].extnol,sb->dir[start].extnoh)/sb->extents)==(extno/sb->extents))
434 && isMatching(user,name,ext,sb->dir[start].status,sb->dir[start].name,sb->dir[start].ext)
437 boo="file not found";
441 /* findFreeExtent -- find first free extent */ /*{{{*/
442 static int findFreeExtent(const struct cpmSuperBlock *drive)
446 for (i=0; i<drive->maxdir; ++i) if (drive->dir[i].status==(char)0xe5) return (i);
447 boo="directory full";
451 /* updateTimeStamps -- convert time stamps to CP/M format */ /*{{{*/
452 static void updateTimeStamps(const struct cpmInode *ino, int extent)
454 struct PhysDirectoryEntry *date;
455 int ca_min,ca_hour,ca_days,u_min,u_hour,u_days;
457 if (!S_ISREG(ino->mode)) return;
459 fprintf(stderr,"CPMFS: updating time stamps for inode %d (%d)\n",extent,extent&3);
461 unix2cpm_time(ino->sb->cnotatime ? ino->ctime : ino->atime,&ca_days,&ca_hour,&ca_min);
462 unix2cpm_time(ino->mtime,&u_days,&u_hour,&u_min);
463 if ((ino->sb->type&CPMFS_CPM3_DATES) && (date=ino->sb->dir+(extent|3))->status==0x21)
465 ino->sb->dirtyDirectory=1;
468 case 0: /* first entry */ /*{{{*/
470 date->name[0]=ca_days&0xff; date->name[1]=ca_days>>8;
471 date->name[2]=ca_hour;
472 date->name[3]=ca_min;
473 date->name[4]=u_days&0xff; date->name[5]=u_days>>8;
474 date->name[6]=u_hour;
479 case 1: /* second entry */ /*{{{*/
481 date->ext[2]=ca_days&0xff; date->extnol=ca_days>>8;
484 date->blkcnt=u_days&0xff; date->pointers[0]=u_days>>8;
485 date->pointers[1]=u_hour;
486 date->pointers[2]=u_min;
490 case 2: /* third entry */ /*{{{*/
492 date->pointers[5]=ca_days&0xff; date->pointers[6]=ca_days>>8;
493 date->pointers[7]=ca_hour;
494 date->pointers[8]=ca_min;
495 date->pointers[9]=u_days&0xff; date->pointers[10]=u_days>>8;
496 date->pointers[11]=u_hour;
497 date->pointers[12]=u_min;
505 /* updateDsStamps -- set time in datestamper file */ /*{{{*/
506 static void updateDsStamps(const struct cpmInode *ino, int extent)
508 struct dsDate *stamp;
510 if (!S_ISREG(ino->mode)) return;
511 if ( !(ino->sb->type&CPMFS_DS_DATES) ) return;
514 fprintf(stderr,"CPMFS: updating ds stamps for inode %d (%d)\n",extent,extent&3);
517 /* Get datestamp struct */
518 stamp = ino->sb->ds+extent;
520 unix2ds_time( ino->mtime, &stamp->modify );
521 unix2ds_time( ino->ctime, &stamp->create );
522 unix2ds_time( ino->atime, &stamp->access );
524 ino->sb->dirtyDs = 1;
527 /* readTimeStamps -- read CP/M time stamp */ /*{{{*/
528 static int readTimeStamps(struct cpmInode *i, int lowestExt)
530 /* variables */ /*{{{*/
531 struct PhysDirectoryEntry *date;
532 int u_days=0,u_hour=0,u_min=0;
533 int ca_days=0,ca_hour=0,ca_min=0;
537 if ( (i->sb->type&CPMFS_CPM3_DATES) && (date=i->sb->dir+(lowestExt|3))->status==0x21 )
541 case 0: /* first entry of the four */ /*{{{*/
543 ca_days=((unsigned char)date->name[0])+(((unsigned char)date->name[1])<<8);
544 ca_hour=(unsigned char)date->name[2];
545 ca_min=(unsigned char)date->name[3];
546 u_days=((unsigned char)date->name[4])+(((unsigned char)date->name[5])<<8);
547 u_hour=(unsigned char)date->name[6];
548 u_min=(unsigned char)date->name[7];
549 protectMode=(unsigned char)date->ext[0];
553 case 1: /* second entry */ /*{{{*/
555 ca_days=((unsigned char)date->ext[2])+(((unsigned char)date->extnol)<<8);
556 ca_hour=(unsigned char)date->lrc;
557 ca_min=(unsigned char)date->extnoh;
558 u_days=((unsigned char)date->blkcnt)+(((unsigned char)date->pointers[0])<<8);
559 u_hour=(unsigned char)date->pointers[1];
560 u_min=(unsigned char)date->pointers[2];
561 protectMode=(unsigned char)date->pointers[3];
565 case 2: /* third one */ /*{{{*/
567 ca_days=((unsigned char)date->pointers[5])+(((unsigned char)date->pointers[6])<<8);
568 ca_hour=(unsigned char)date->pointers[7];
569 ca_min=(unsigned char)date->pointers[8];
570 u_days=((unsigned char)date->pointers[9])+(((unsigned char)date->pointers[10])<<8);
571 u_hour=(unsigned char)date->pointers[11];
572 u_min=(unsigned char)date->pointers[12];
573 protectMode=(unsigned char)date->pointers[13];
578 if (i->sb->cnotatime)
580 i->ctime=cpm2unix_time(ca_days,ca_hour,ca_min);
586 i->atime=cpm2unix_time(ca_days,ca_hour,ca_min);
588 i->mtime=cpm2unix_time(u_days,u_hour,u_min);
592 i->atime=i->mtime=i->ctime=0;
599 /* readDsStamps -- read datestamper time stamp */ /*{{{*/
600 static void readDsStamps(struct cpmInode *i, int lowestExt)
602 struct dsDate *stamp;
604 if ( !(i->sb->type&CPMFS_DS_DATES) ) return;
607 stamp = i->sb->ds+lowestExt;
609 i->mtime = ds2unix_time(&stamp->modify);
610 i->ctime = ds2unix_time(&stamp->create);
611 i->atime = ds2unix_time(&stamp->access);
615 /* match -- match filename against a pattern */ /*{{{*/
616 static int recmatch(char const *a, char const *pattern)
628 if (*a=='.' && first) return 1;
630 while (*a) if (recmatch(a,pattern)) return 1; else ++a;
635 if (*a) { ++a; ++pattern; } else return 0;
638 default: if (tolower(*a)==tolower(*pattern)) { ++a; ++pattern; } else return 0;
642 return (*pattern=='\0' && *a=='\0');
645 int match(char const *a, char const *pattern)
652 assert(strlen(pattern)<255);
653 if (isdigit(*pattern) && *(pattern+1)==':') { user=(*pattern-'0'); pattern+=2; }
654 else if (isdigit(*pattern) && isdigit(*(pattern+1)) && *(pattern+2)==':') { user=(10*(*pattern-'0')+(*(pattern+1)-'0')); pattern+=3; }
656 if (user==-1) sprintf(pat,"??%s",pattern);
657 else sprintf(pat,"%02d%s",user,pattern);
658 return recmatch(a,pat);
662 /* cpmglob -- expand CP/M style wildcards */ /*{{{*/
663 void cpmglob(int optin, int argc, char * const argv[], struct cpmInode *root, int *gargc, char ***gargv)
666 int entries,dirsize=0;
667 struct cpmDirent *dirent=(struct cpmDirent*)0;
672 cpmOpendir(root,&dir);
675 dirent=malloc(sizeof(struct cpmDirent)*dirsize);
676 while (cpmReaddir(&dir,&dirent[entries]))
679 if (entries==dirsize) dirent=realloc(dirent,sizeof(struct cpmDirent)*(dirsize*=2));
681 for (i=optin; i<argc; ++i)
685 for (j=0,found=0; j<entries; ++j)
687 if (match(dirent[j].name,argv[i]))
689 if (*gargc==gargcap) *gargv=realloc(*gargv,sizeof(char*)*(gargcap ? (gargcap*=2) : (gargcap=16)));
690 (*gargv)[*gargc]=strcpy(malloc(strlen(dirent[j].name)+1),dirent[j].name);
699 /* cpmglobfree -- free expanded wildcards */ /*{{{*/
700 void cpmglobfree(char **dirent, int entries)
706 for (d=0; d<entries; ++d) free(dirent[d]);
711 /* superblock management */
712 /* diskdefReadSuper -- read super block from diskdefs file */ /*{{{*/
713 static int diskdefReadSuper(struct cpmSuperBlock *d, char const *format)
718 int insideDef=0,found=0;
720 d->libdskGeometry[0] = '\0';
722 if ((fp=fopen("diskdefs","r"))==(FILE*)0 && (fp=fopen(DISKDEFS,"r"))==(FILE*)0)
724 fprintf(stderr,"%s: Neither `diskdefs' nor `" DISKDEFS "' could be opened.\n",cmd);
728 while (fgets(line,sizeof(line),fp)!=(char*)0)
734 /* Allow inline comments preceded by ; or # */
735 s = strchr(line, '#');
736 if (s) strcpy(s, "\n");
737 s = strchr(line, ';');
738 if (s) strcpy(s, "\n");
740 for (argc=0; argc<1 && (argv[argc]=strtok(argc ? (char*)0 : line," \t\n")); ++argc);
741 if ((argv[argc]=strtok((char*)0,"\n"))!=(char*)0) ++argc;
744 if (argc==1 && strcmp(argv[0],"end")==0)
747 d->size=(d->sectrk*d->tracks-bootOffset(d)) * d->secLength / d->blksiz;
748 if (d->extents==0) d->extents=((d->size>256 ? 8 : 16)*d->blksiz)/16384;
749 if (d->extents==0) d->extents=1;
754 if (strcmp(argv[0],"seclen")==0) d->secLength=strtol(argv[1],(char**)0,0);
755 else if (strcmp(argv[0],"tracks")==0) d->tracks=strtol(argv[1],(char**)0,0);
756 else if (strcmp(argv[0],"sectrk")==0) d->sectrk=strtol(argv[1],(char**)0,0);
757 else if (strcmp(argv[0],"blocksize")==0)
759 d->blksiz=strtol(argv[1],(char**)0,0);
762 fprintf(stderr,"%s: invalid blocksize `%s' in line %d\n",cmd,argv[1],ln);
766 else if (strcmp(argv[0],"maxdir")==0) d->maxdir=strtol(argv[1],(char**)0,0);
767 else if (strcmp(argv[0],"dirblks")==0) d->dirblks=strtol(argv[1],(char**)0,0);
768 else if (strcmp(argv[0],"skew")==0) d->skew=strtol(argv[1],(char**)0,0);
769 else if (strcmp(argv[0],"skewtab")==0)
773 for (pass=0; pass<2; ++pass)
776 for (s=argv[1]; *s; )
781 phys=strtol(s,&end,10);
782 if (pass==1) d->skewtab[sectors]=phys;
785 fprintf(stderr,"%s: invalid skewtab `%s' at `%s' in line %d\n",cmd,argv[1],s,ln);
792 if (pass==0) d->skewtab=malloc(sizeof(int)*sectors);
795 else if (strcmp(argv[0],"boottrk")==0) d->boottrk=strtol(argv[1],(char**)0,0);
796 else if (strcmp(argv[0],"bootsec")==0) d->bootsec=strtol(argv[1],(char**)0,0);
797 else if (strcmp(argv[0],"offset")==0)
800 unsigned int multiplier;
805 val = strtol(argv[1],&endptr,10);
806 if ((errno==ERANGE && val==LONG_MAX)||(errno!=0 && val<=0))
808 fprintf(stderr,"%s: invalid offset value `%s' (%s) in line %d\n",cmd,argv[1],strerror(errno),ln);
813 fprintf(stderr,"%s: offset value `%s' is not a number in line %d\n",cmd,argv[1],ln);
818 /* Have a unit specifier */
819 switch (toupper(*endptr))
825 multiplier=1024*1024;
828 if (d->sectrk<0||d->tracks<0||d->secLength<0)
830 fprintf(stderr,"%s: offset must be specified after sectrk, tracks and secLength in line %d\n",cmd,ln);
833 multiplier=d->sectrk*d->secLength;
836 if (d->sectrk<0||d->tracks<0||d->secLength<0)
838 fprintf(stderr,"%s: offset must be specified after sectrk, tracks and secLength in line %d\n",cmd,ln);
841 multiplier=d->secLength;
844 fprintf(stderr,"%s: unknown unit specifier `%c' in line %d\n",cmd,*endptr,ln);
848 if (val*multiplier>INT_MAX)
850 fprintf(stderr,"%s: effective offset is out of range in line %d\n",cmd,ln);
853 d->offset=val*multiplier;
855 else if (strcmp(argv[0],"logicalextents")==0) d->extents=strtol(argv[1],(char**)0,0);
856 else if (strcmp(argv[0],"os")==0)
858 if (strcmp(argv[1],"2.2" )==0) d->type|=CPMFS_DR22;
859 else if (strcmp(argv[1],"3" )==0) d->type|=CPMFS_DR3;
860 else if (strcmp(argv[1],"isx" )==0) d->type|=CPMFS_ISX;
861 else if (strcmp(argv[1],"p2dos")==0) d->type|=CPMFS_P2DOS;
862 else if (strcmp(argv[1],"zsys" )==0) d->type|=CPMFS_ZSYS;
865 fprintf(stderr, "%s: invalid OS type `%s' in line %d\n",cmd,argv[1],ln);
869 else if (strcmp(argv[0], "libdsk:format")==0)
871 strncpy(d->libdskGeometry, argv[1], sizeof(d->libdskGeometry) - 1);
872 d->libdskGeometry[sizeof(d->libdskGeometry) - 1] = 0;
875 else if (argc>0 && argv[0][0]!='#' && argv[0][0]!=';')
877 fprintf(stderr,"%s: invalid keyword `%s' in line %d\n",cmd,argv[0],ln);
881 else if (argc==2 && strcmp(argv[0],"diskdef")==0)
889 d->blksiz=d->boottrk=d->bootsec=d->secLength=d->sectrk=d->tracks=d->maxdir=-1;
891 d->libdskGeometry[0] = 0;
892 if (strcmp(argv[1],format)==0) found=1;
899 fprintf(stderr,"%s: unknown format %s\n",cmd,format);
902 if (d->boottrk<0 && d->bootsec<0)
904 fprintf(stderr, "%s: boottrk / bootsec parameter invalid or missing from diskdef\n",cmd);
909 fprintf(stderr, "%s: secLength parameter invalid or missing from diskdef\n",cmd);
914 fprintf(stderr, "%s: sectrk parameter invalid or missing from diskdef\n",cmd);
919 fprintf(stderr, "%s: tracks parameter invalid or missing from diskdef\n",cmd);
924 fprintf(stderr, "%s: blocksize parameter invalid or missing from diskdef\n",cmd);
929 fprintf(stderr, "%s: maxdir parameter invalid or missing from diskdef\n",cmd);
935 /* amsReadSuper -- read super block from amstrad disk */ /*{{{*/
936 static int amsReadSuper(struct cpmSuperBlock *d, char const *format)
938 unsigned char boot_sector[512], *boot_spec;
941 Device_setGeometry(&d->dev,512,9,40,0,"pcw180");
942 if ((err=Device_readSector(&d->dev, 0, 0, (char *)boot_sector)))
944 fprintf(stderr,"%s: Failed to read Amstrad superblock (%s)\n",cmd,err);
947 boot_spec=(boot_sector[0] == 0 || boot_sector[0] == 3)?boot_sector:(unsigned char*)0;
948 /* Check for JCE's extension to allow Amstrad and MSDOS superblocks
949 * in the same sector (for the PCW16)
953 (boot_sector[0] == 0xE9 || boot_sector[0] == 0xEB)
954 && !memcmp(boot_sector + 0x2B, "CP/M", 4)
955 && !memcmp(boot_sector + 0x33, "DSK", 3)
956 && !memcmp(boot_sector + 0x7C, "CP/M", 4)
957 ) boot_spec = boot_sector + 128;
958 if (boot_spec==(unsigned char*)0)
960 fprintf(stderr,"%s: Amstrad superblock not present\n",cmd);
963 /* boot_spec[0] = format number: 0 for SS SD, 3 for DS DD
964 [1] = single/double sided and density flags
965 [2] = cylinders per side
966 [3] = sectors per cylinder
967 [4] = Physical sector shift, 2 => 512
968 [5] = Reserved track count
970 [7] = No. of directory blocks
973 d->type |= CPMFS_DR3; /* Amstrads are CP/M 3 systems */
974 d->secLength = 128 << boot_spec[4];
975 d->tracks = boot_spec[2];
976 if (boot_spec[1] & 3) d->tracks *= 2;
977 d->sectrk = boot_spec[3];
978 d->blksiz = 128 << boot_spec[6];
979 d->maxdir = (d->blksiz / 32) * boot_spec[7];
981 d->skew = 1; /* Amstrads skew at the controller level */
982 d->skewtab = (int*)0;
983 d->boottrk = boot_spec[5];
985 d->size = (d->secLength*d->sectrk*(d->tracks-d->boottrk))/d->blksiz;
986 d->extents = ((d->size>256 ? 8 : 16)*d->blksiz)/16384;
987 d->libdskGeometry[0] = 0; /* LibDsk can recognise an Amstrad superblock
993 /* cpmCheckDs -- read all datestamper timestamps */ /*{{{*/
994 int cpmCheckDs(struct cpmSuperBlock *sb)
996 int dsoffset, dsblks, dsrecs, off, i;
999 if (!isMatching(0,"!!!TIME&","DAT",sb->dir->status,sb->dir->name,sb->dir->ext)) return -1;
1001 /* Offset to ds file in alloc blocks */
1002 dsoffset=(sb->maxdir*32+(sb->blksiz-1))/sb->blksiz;
1004 dsrecs=(sb->maxdir+7)/8;
1005 dsblks=(dsrecs*128+(sb->blksiz-1))/sb->blksiz;
1007 /* Allocate buffer */
1008 sb->ds=malloc(dsblks*sb->blksiz);
1010 /* Read ds file in its entirety */
1012 for (i=dsoffset; i<dsoffset+dsblks; i++)
1014 if (readBlock(sb,i,((char*)sb->ds)+off,0,-1)==-1) return -1;
1018 /* Verify checksums */
1019 buf = (unsigned char *)sb->ds;
1020 for (i=0; i<dsrecs; i++)
1024 for (j=0; j<127; j++) cksum += buf[j];
1025 if (buf[j]!=(cksum&0xff))
1028 fprintf( stderr, "!!!TIME&.DAT file failed cksum at record %i\n", i );
1031 sb->ds = (struct dsDate *)0;
1039 /* cpmReadSuper -- get DPB and init in-core data for drive */ /*{{{*/
1040 int cpmReadSuper(struct cpmSuperBlock *d, struct cpmInode *root, char const *format, int uppercase)
1042 while (s_ifdir && !S_ISDIR(s_ifdir)) s_ifdir<<=1;
1044 while (s_ifreg && !S_ISREG(s_ifreg)) s_ifreg<<=1;
1047 if (strcmp(format,"amstrad")==0) amsReadSuper(d,format);
1048 else diskdefReadSuper(d,format);
1050 d->uppercase=uppercase;
1052 assert(d->boottrk>=0);
1053 assert(d->secLength>0);
1054 assert(d->sectrk>0);
1055 assert(d->tracks>0);
1056 assert(d->blksiz>0);
1057 assert(d->maxdir>0);
1058 assert(d->dirblks>=0);
1062 /* optional field, compute based on directory size */
1063 d->dirblks=(d->maxdir*32+(d->blksiz-1))/d->blksiz;
1066 boo = Device_setGeometry(&d->dev,d->secLength,d->sectrk,d->tracks,d->offset,d->libdskGeometry);
1069 if (d->skewtab==(int*)0) /* generate skew table */ /*{{{*/
1073 if (( d->skewtab = malloc(d->sectrk*sizeof(int))) == (int*)0)
1075 boo=strerror(errno);
1078 memset(d->skewtab,0,d->sectrk*sizeof(int));
1079 for (i=j=0; i<d->sectrk; ++i,j=(j+d->skew)%d->sectrk)
1083 assert(i<d->sectrk);
1084 assert(j<d->sectrk);
1085 for (k=0; k<i && d->skewtab[k]!=j; ++k);
1086 if (k<i) j=(j+1)%d->sectrk;
1093 /* initialise allocation vector bitmap */ /*{{{*/
1095 d->alvSize= ((((d->sectrk * d->tracks)-bootOffset(d)) * d->secLength)/d->blksiz+INTBITS-1)/INTBITS;
1096 if ((d->alv=malloc(d->alvSize*sizeof(int)))==(int*)0)
1098 boo=strerror(errno);
1103 /* allocate directory buffer */ /*{{{*/
1104 assert(sizeof(struct PhysDirectoryEntry)==32);
1105 if ((d->dir=malloc(((d->maxdir*32+d->blksiz-1)/d->blksiz)*d->blksiz))==(struct PhysDirectoryEntry*)0)
1107 boo=strerror(errno);
1111 if (d->dev.opened==0) /* create empty directory in core */ /*{{{*/
1113 memset(d->dir,0xe5,d->maxdir*32);
1116 else /* read directory in core */ /*{{{*/
1120 blocks=(d->maxdir*32+d->blksiz-1)/d->blksiz;
1122 for (i=0; i<blocks; ++i)
1124 if (readBlock(d,i,(char*)(d->dir+entry),0,-1)==-1) return -1;
1125 entry+=(d->blksiz/32);
1130 if (d->type&CPMFS_CPM3_OTHER) /* read additional superblock information */ /*{{{*/
1134 /* passwords */ /*{{{*/
1138 for (i=0; i<d->maxdir; ++i) if (d->dir[i].status>=16 && d->dir[i].status<=31) ++passwords;
1140 fprintf(stderr,"getformat: found %d passwords\n",passwords);
1142 if ((d->passwdLength=passwords*PASSWD_RECLEN))
1144 if ((d->passwd=malloc(d->passwdLength))==(char*)0)
1146 boo="out of memory";
1149 for (i=0,passwords=0; i<d->maxdir; ++i) if (d->dir[i].status>=16 && d->dir[i].status<=31)
1152 char *p=d->passwd+(passwords++*PASSWD_RECLEN);
1154 p[0]='0'+(d->dir[i].status-16)/10;
1155 p[1]='0'+(d->dir[i].status-16)%10;
1156 for (j=0; j<8; ++j) p[2+j]=d->dir[i].name[j]&0x7f;
1157 p[10]=(d->dir[i].ext[0]&0x7f)==' ' ? ' ' : '.';
1158 for (j=0; j<3; ++j) p[11+j]=d->dir[i].ext[j]&0x7f;
1160 pb=(unsigned char)d->dir[i].lrc;
1161 for (j=0; j<8; ++j) p[15+j]=((unsigned char)d->dir[i].pointers[7-j])^pb;
1164 fprintf(stderr,"getformat: %s\n",p);
1171 /* disc label */ /*{{{*/
1172 for (i=0; i<d->maxdir; ++i) if (d->dir[i].status==(char)0x20)
1176 d->cnotatime=d->dir[i].extnol&0x10;
1177 if (d->dir[i].extnol&0x1)
1180 if ((d->label=malloc(d->labelLength))==(char*)0)
1182 boo="out of memory";
1185 for (j=0; j<8; ++j) d->label[j]=d->dir[i].name[j]&0x7f;
1186 for (j=0; j<3; ++j) d->label[8+j]=d->dir[i].ext[j]&0x7f;
1210 d->dirtyDirectory = 0;
1211 root->ino=d->maxdir;
1213 root->mode=(s_ifdir|0777);
1215 root->atime=root->mtime=root->ctime=0;
1218 if (cpmCheckDs(d)==0) d->type|=CPMFS_DS_DATES;
1219 else d->ds=(struct dsDate*)0;
1224 /* syncDs -- write all datestamper timestamps */ /*{{{*/
1225 static int syncDs(const struct cpmSuperBlock *sb)
1229 int dsoffset, dsblks, dsrecs, off, i;
1232 dsrecs=(sb->maxdir+7)/8;
1234 /* Re-calculate checksums */
1235 buf = (unsigned char *)sb->ds;
1236 for ( i=0; i<dsrecs; i++ )
1240 for ( j=0; j<127; j++ ) cksum += buf[j];
1241 buf[j] = cksum & 0xff;
1244 dsoffset=(sb->maxdir*32+(sb->blksiz-1))/sb->blksiz;
1245 dsblks=(dsrecs*128+(sb->blksiz-1))/sb->blksiz;
1248 for (i=dsoffset; i<dsoffset+dsblks; i++)
1250 if (writeBlock(sb,i,((char*)(sb->ds))+off,0,-1)==-1) return -1;
1257 /* cpmSync -- write directory back */ /*{{{*/
1258 int cpmSync(struct cpmSuperBlock *sb)
1260 if (sb->dirtyDirectory)
1264 blocks=(sb->maxdir*32+sb->blksiz-1)/sb->blksiz;
1266 for (i=0; i<blocks; ++i)
1268 if (writeBlock(sb,i,(char*)(sb->dir+entry),0,-1)==-1) return -1;
1269 entry+=(sb->blksiz/32);
1271 sb->dirtyDirectory=0;
1273 if (sb->type&CPMFS_DS_DATES) syncDs(sb);
1277 /* cpmUmount -- free super block */ /*{{{*/
1278 int cpmUmount(struct cpmSuperBlock *sb)
1281 char const *err_close;
1283 err_sync=cpmSync(sb);
1284 err_close=Device_close(&sb->dev);
1285 if (sb->type&CPMFS_DS_DATES) free(sb->ds);
1289 if (sb->passwdLength) free(sb->passwd);
1290 if (err_sync==-1) return err_sync;
1300 /* cpmNamei -- map name to inode */ /*{{{*/
1301 int cpmNamei(const struct cpmInode *dir, char const *filename, struct cpmInode *i)
1303 /* variables */ /*{{{*/
1305 char name[8],extension[3];
1306 int highestExtno,highestExt=-1,lowestExtno,lowestExt=-1;
1311 fprintf(stderr,"cpmNamei: map %s\n", filename);
1313 if (!S_ISDIR(dir->mode))
1318 if (strcmp(filename,".")==0 || strcmp(filename,"..")==0) /* root directory */ /*{{{*/
1324 else if (strcmp(filename,"[passwd]")==0 && dir->sb->passwdLength) /* access passwords */ /*{{{*/
1327 i->ino=dir->sb->maxdir+1;
1328 i->mode=s_ifreg|0444;
1330 i->atime=i->mtime=i->ctime=0;
1331 i->size=i->sb->passwdLength;
1335 else if (strcmp(filename,"[label]")==0 && dir->sb->labelLength) /* access label */ /*{{{*/
1338 i->ino=dir->sb->maxdir+2;
1339 i->mode=s_ifreg|0444;
1341 i->atime=i->mtime=i->ctime=0;
1342 i->size=i->sb->labelLength;
1346 if (splitFilename(filename,dir->sb->type,name,extension,&user)==-1) return -1;
1347 /* find highest and lowest extent */ /*{{{*/
1355 while ((extent=findFileExtent(dir->sb,user,name,extension,extent+1,-1))!=-1)
1357 int extno=EXTENT(dir->sb->dir[extent].extnol,dir->sb->dir[extent].extnoh);
1359 if (extno>highestExtno)
1364 if (extno<lowestExtno)
1372 if (highestExtno==-1) return -1;
1373 /* calculate size */ /*{{{*/
1377 i->size=highestExtno*16384;
1378 if (dir->sb->size<=256) for (block=15; block>=0; --block)
1380 if (dir->sb->dir[highestExt].pointers[block]) break;
1382 else for (block=7; block>=0; --block)
1384 if (dir->sb->dir[highestExt].pointers[2*block] || dir->sb->dir[highestExt].pointers[2*block+1]) break;
1386 if (dir->sb->dir[highestExt].blkcnt)
1388 i->size+=((dir->sb->dir[highestExt].blkcnt&0xff)-1)*128;
1389 if (dir->sb->type & CPMFS_ISX)
1391 i->size += (128 - dir->sb->dir[highestExt].lrc);
1395 i->size+=dir->sb->dir[highestExt].lrc ? (dir->sb->dir[highestExt].lrc&0xff) : 128;
1399 fprintf(stderr,"cpmNamei: size=%ld\n",(long)i->size);
1407 /* read timestamps */ /*{{{*/
1408 protectMode = readTimeStamps(i,lowestExt);
1411 /* Determine the inode attributes */
1413 if (dir->sb->dir[lowestExt].name[0]&0x80) i->attr |= CPM_ATTR_F1;
1414 if (dir->sb->dir[lowestExt].name[1]&0x80) i->attr |= CPM_ATTR_F2;
1415 if (dir->sb->dir[lowestExt].name[2]&0x80) i->attr |= CPM_ATTR_F3;
1416 if (dir->sb->dir[lowestExt].name[3]&0x80) i->attr |= CPM_ATTR_F4;
1417 if (dir->sb->dir[lowestExt].ext [0]&0x80) i->attr |= CPM_ATTR_RO;
1418 if (dir->sb->dir[lowestExt].ext [1]&0x80) i->attr |= CPM_ATTR_SYS;
1419 if (dir->sb->dir[lowestExt].ext [2]&0x80) i->attr |= CPM_ATTR_ARCV;
1420 if (protectMode&0x20) i->attr |= CPM_ATTR_PWDEL;
1421 if (protectMode&0x40) i->attr |= CPM_ATTR_PWWRITE;
1422 if (protectMode&0x80) i->attr |= CPM_ATTR_PWREAD;
1424 if (dir->sb->dir[lowestExt].ext[1]&0x80) i->mode|=01000;
1426 if (!(dir->sb->dir[lowestExt].ext[0]&0x80)) i->mode|=0222;
1427 if (extension[0]=='C' && extension[1]=='O' && extension[2]=='M') i->mode|=0111;
1429 readDsStamps(i,lowestExt);
1434 /* cpmStatFS -- statfs */ /*{{{*/
1435 void cpmStatFS(const struct cpmInode *ino, struct cpmStatFS *buf)
1438 struct cpmSuperBlock *d;
1441 buf->f_bsize=d->blksiz;
1442 buf->f_blocks=d->size;
1444 buf->f_bused=-(d->dirblks);
1445 for (i=0; i<d->alvSize; ++i)
1450 for (j=0; j<INTBITS; ++j)
1452 if (i*INTBITS+j < d->size)
1457 fprintf(stderr,"cpmStatFS: block %d allocated\n",(i*INTBITS+j));
1461 else ++buf->f_bfree;
1466 buf->f_bavail=buf->f_bfree;
1467 buf->f_files=d->maxdir;
1469 for (i=0; i<d->maxdir; ++i)
1471 if (d->dir[i].status==(char)0xe5) ++buf->f_ffree;
1476 /* cpmUnlink -- unlink */ /*{{{*/
1477 int cpmUnlink(const struct cpmInode *dir, char const *fname)
1480 char name[8],extension[3];
1482 struct cpmSuperBlock *drive;
1484 if (!S_ISDIR(dir->mode))
1490 if (splitFilename(fname,dir->sb->type,name,extension,&user)==-1) return -1;
1491 if ((extent=findFileExtent(drive,user,name,extension,0,-1))==-1) return -1;
1492 drive->dirtyDirectory=1;
1493 drive->dir[extent].status=(char)0xe5;
1496 drive->dir[extent].status=(char)0xe5;
1497 } while ((extent=findFileExtent(drive,user,name,extension,extent+1,-1))>=0);
1502 /* cpmRename -- rename */ /*{{{*/
1503 int cpmRename(const struct cpmInode *dir, char const *old, char const *new)
1505 struct cpmSuperBlock *drive;
1508 char oldname[8], oldext[3];
1510 char newname[8], newext[3];
1512 if (!S_ISDIR(dir->mode))
1518 if (splitFilename(old,dir->sb->type, oldname, oldext,&olduser)==-1) return -1;
1519 if (splitFilename(new,dir->sb->type, newname, newext,&newuser)==-1) return -1;
1520 if ((extent=findFileExtent(drive,olduser,oldname,oldext,0,-1))==-1) return -1;
1521 if (findFileExtent(drive,newuser,newname, newext,0,-1)!=-1)
1523 boo="file already exists";
1528 drive->dirtyDirectory=1;
1529 drive->dir[extent].status=newuser;
1530 memcpy7(drive->dir[extent].name, newname, 8);
1531 memcpy7(drive->dir[extent].ext, newext, 3);
1532 } while ((extent=findFileExtent(drive,olduser,oldname,oldext,extent+1,-1))!=-1);
1536 /* cpmOpendir -- opendir */ /*{{{*/
1537 int cpmOpendir(struct cpmInode *dir, struct cpmFile *dirp)
1539 if (!S_ISDIR(dir->mode))
1546 dirp->mode=O_RDONLY;
1550 /* cpmReaddir -- readdir */ /*{{{*/
1551 int cpmReaddir(struct cpmFile *dir, struct cpmDirent *ent)
1553 /* variables */ /*{{{*/
1554 struct PhysDirectoryEntry *cur=(struct PhysDirectoryEntry*)0;
1555 char buf[2+8+1+3+1]; /* 00foobarxy.zzy\0 */
1560 if (!(S_ISDIR(dir->ino->mode))) /* error: not a directory */ /*{{{*/
1562 boo="not a directory";
1570 if (dir->pos==0) /* first entry is . */ /*{{{*/
1572 ent->ino=dir->ino->sb->maxdir;
1574 strcpy(ent->name,".");
1580 else if (dir->pos==1) /* next entry is .. */ /*{{{*/
1582 ent->ino=dir->ino->sb->maxdir;
1584 strcpy(ent->name,"..");
1590 else if (dir->pos==2)
1592 if (dir->ino->sb->passwdLength) /* next entry is [passwd] */ /*{{{*/
1594 ent->ino=dir->ino->sb->maxdir+1;
1596 strcpy(ent->name,"[passwd]");
1603 else if (dir->pos==3)
1605 if (dir->ino->sb->labelLength) /* next entry is [label] */ /*{{{*/
1607 ent->ino=dir->ino->sb->maxdir+2;
1609 strcpy(ent->name,"[label]");
1616 else if (dir->pos>=RESERVED_ENTRIES && dir->pos<(int)dir->ino->sb->maxdir+RESERVED_ENTRIES)
1618 int first=dir->pos-RESERVED_ENTRIES;
1620 if ((cur=dir->ino->sb->dir+(dir->pos-RESERVED_ENTRIES))->status>=0 && cur->status<=(dir->ino->sb->type&CPMFS_HI_USER ? 31 : 15))
1622 /* determine first extent for the current file */ /*{{{*/
1623 for (i=0; i<dir->ino->sb->maxdir; ++i) if (i!=(dir->pos-RESERVED_ENTRIES))
1625 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;
1628 if (first==(dir->pos-RESERVED_ENTRIES))
1630 ent->ino=dir->pos-RESERVED_INODES;
1631 /* convert file name to UNIX style */ /*{{{*/
1632 buf[0]='0'+cur->status/10;
1633 buf[1]='0'+cur->status%10;
1634 for (bufp=buf+2,i=0; i<8 && (cur->name[i]&0x7f)!=' '; ++i)
1636 *bufp++=dir->ino->sb->uppercase ? cur->name[i]&0x7f : tolower(cur->name[i]&0x7f);
1638 for (hasext=0,i=0; i<3 && (cur->ext[i]&0x7f)!=' '; ++i)
1640 if (!hasext) { *bufp++='.'; hasext=1; }
1641 *bufp++=dir->ino->sb->uppercase ? cur->ext[i]&0x7f : tolower(cur->ext[i]&0x7f);
1645 assert(bufp<=buf+sizeof(buf));
1646 ent->reclen=bufp-buf;
1647 strcpy(ent->name,buf);
1659 /* cpmStat -- stat */ /*{{{*/
1660 void cpmStat(const struct cpmInode *ino, struct cpmStat *buf)
1663 buf->mode=ino->mode;
1664 buf->size=ino->size;
1665 buf->atime=ino->atime;
1666 buf->mtime=ino->mtime;
1667 buf->ctime=ino->ctime;
1670 /* cpmOpen -- open */ /*{{{*/
1671 int cpmOpen(struct cpmInode *ino, struct cpmFile *file, mode_t mode)
1673 if (S_ISREG(ino->mode))
1675 if ((mode&O_WRONLY) && (ino->mode&0222)==0)
1677 boo="permission denied";
1687 boo="not a regular file";
1692 /* cpmRead -- read */ /*{{{*/
1693 ssize_t cpmRead(struct cpmFile *file, char *buf, size_t count)
1695 int findext=1,findblock=1,extent=-1,block=-1,extentno=-1,got=0,nextblockpos=-1,nextextpos=-1;
1696 struct cpmSuperBlock const *sb=file->ino->sb;
1697 int blocksize=sb->blksiz;
1700 extcap=(sb->size<=256 ? 16 : 8)*blocksize;
1701 if (extcap>16384) extcap=16384*sb->extents;
1702 if (file->ino->ino==(ino_t)sb->maxdir+1) /* [passwd] */ /*{{{*/
1704 if ((file->pos+(off_t)count)>file->ino->size) count=file->ino->size-file->pos;
1705 if (count) memcpy(buf,sb->passwd+file->pos,count);
1708 fprintf(stderr,"cpmRead passwd: read %d bytes, now at position %ld\n",count,(long)file->pos);
1713 else if (file->ino->ino==(ino_t)sb->maxdir+2) /* [label] */ /*{{{*/
1715 if ((file->pos+(off_t)count)>file->ino->size) count=file->ino->size-file->pos;
1716 if (count) memcpy(buf,sb->label+file->pos,count);
1719 fprintf(stderr,"cpmRead label: read %d bytes, now at position %ld\n",count,(long)file->pos);
1724 else while (count>0 && file->pos<file->ino->size)
1730 extentno=file->pos/16384;
1731 extent=findFileExtent(sb,sb->dir[file->ino->ino].status,sb->dir[file->ino->ino].name,sb->dir[file->ino->ino].ext,0,extentno);
1732 nextextpos=(file->pos/extcap)*extcap+extcap;
1742 ptr=(file->pos%extcap)/blocksize;
1743 if (sb->size>256) ptr*=2;
1744 block=(unsigned char)sb->dir[extent].pointers[ptr];
1745 if (sb->size>256) block+=((unsigned char)sb->dir[extent].pointers[ptr+1])<<8;
1748 memset(buffer,0,blocksize);
1754 start=(file->pos%blocksize)
1756 end=((file->pos%blocksize+(off_t)count)>blocksize ? blocksize-1 : (int)(file->pos%blocksize+count-1))
1758 if (block<sb->dirblks)
1760 boo="Attempting to access block before beginning of data";
1764 if (readBlock(sb,block,buffer,start,end)==-1)
1771 nextblockpos=(file->pos/blocksize)*blocksize+blocksize;
1774 if (file->pos<nextblockpos)
1776 if (extent==-1) *buf++='\0'; else *buf++=buffer[file->pos%blocksize];
1781 else if (file->pos==nextextpos) findext=1; else findblock=1;
1784 fprintf(stderr,"cpmRead: read %d bytes, now at position %ld\n",got,(long)file->pos);
1789 /* cpmWrite -- write */ /*{{{*/
1790 ssize_t cpmWrite(struct cpmFile *file, char const *buf, size_t count)
1792 int findext=1,findblock=-1,extent=-1,extentno=-1,got=0,nextblockpos=-1,nextextpos=-1;
1793 int blocksize=file->ino->sb->blksiz;
1795 int block=-1,start=-1,end=-1,ptr=-1,last=-1;
1798 extcap=(file->ino->sb->size<=256 ? 16 : 8)*blocksize;
1799 if (extcap>16384) extcap=16384*file->ino->sb->extents;
1802 if (findext) /*{{{*/
1804 extentno=file->pos/16384;
1805 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);
1806 nextextpos=(file->pos/extcap)*extcap+extcap;
1809 if ((extent=findFreeExtent(file->ino->sb))==-1) return (got==0 ? -1 : got);
1810 file->ino->sb->dir[extent]=file->ino->sb->dir[file->ino->ino];
1811 memset(file->ino->sb->dir[extent].pointers,0,16);
1812 file->ino->sb->dir[extent].extnol=EXTENTL(extentno);
1813 file->ino->sb->dir[extent].extnoh=EXTENTH(extentno);
1814 file->ino->sb->dir[extent].blkcnt=0;
1815 file->ino->sb->dir[extent].lrc=0;
1816 time(&file->ino->ctime);
1817 updateTimeStamps(file->ino,extent);
1818 updateDsStamps(file->ino,extent);
1824 if (findblock) /*{{{*/
1826 ptr=(file->pos%extcap)/blocksize;
1827 if (file->ino->sb->size>256) ptr*=2;
1828 block=(unsigned char)file->ino->sb->dir[extent].pointers[ptr];
1829 if (file->ino->sb->size>256) block+=((unsigned char)file->ino->sb->dir[extent].pointers[ptr+1])<<8;
1830 if (block==0) /* allocate new block, set start/end to cover it */ /*{{{*/
1832 if ((block=allocBlock(file->ino->sb))==-1) return (got==0 ? -1 : got);
1833 file->ino->sb->dir[extent].pointers[ptr]=block&0xff;
1834 if (file->ino->sb->size>256) file->ino->sb->dir[extent].pointers[ptr+1]=(block>>8)&0xff;
1836 /* By setting end to the end of the block and not the end
1837 * of the currently written data, the whole block gets
1838 * wiped from the disk, which is slow, but convenient in
1839 * case of sparse files.
1841 end=(blocksize-1)/file->ino->sb->secLength;
1842 memset(buffer,0,blocksize);
1843 time(&file->ino->ctime);
1844 updateTimeStamps(file->ino,extent);
1845 updateDsStamps(file->ino,extent);
1848 else /* read existing block and set start/end to cover modified parts */ /*{{{*/
1850 start=(file->pos%blocksize)/file->ino->sb->secLength;
1851 end=((int)(file->pos%blocksize+count)>=blocksize ? blocksize-1 : (int)(file->pos%blocksize+count-1))/file->ino->sb->secLength;
1852 if (file->pos%file->ino->sb->secLength)
1854 if (readBlock(file->ino->sb,block,buffer,start,start)==-1)
1860 if (end!=start && (int)(file->pos%blocksize+count)<blocksize && (file->pos+count)%file->ino->sb->secLength)
1862 if (readBlock(file->ino->sb,block,buffer,end,end)==-1)
1870 nextblockpos=(file->pos/blocksize)*blocksize+blocksize;
1874 /* fill block and write it */ /*{{{*/
1875 file->ino->sb->dirtyDirectory=1;
1876 while (file->pos!=nextblockpos && count)
1878 buffer[file->pos%blocksize]=*buf++;
1880 if (file->ino->size<file->pos) file->ino->size=file->pos;
1884 /* In case the data only fills part of a sector, the rest is
1885 * still initialized: A new block was cleared and the boundaries
1886 * of an existing block were read.
1889 (void)writeBlock(file->ino->sb,block,buffer,start,end);
1890 time(&file->ino->mtime);
1891 if (file->ino->sb->size<=256) for (last=15; last>=0; --last)
1893 if (file->ino->sb->dir[extent].pointers[last])
1898 else for (last=14; last>0; last-=2)
1900 if (file->ino->sb->dir[extent].pointers[last] || file->ino->sb->dir[extent].pointers[last+1])
1906 if (last>0) extentno+=(last*blocksize)/extcap;
1907 file->ino->sb->dir[extent].extnol=EXTENTL(extentno);
1908 file->ino->sb->dir[extent].extnoh=EXTENTH(extentno);
1909 file->ino->sb->dir[extent].blkcnt=((file->pos-1)%16384)/128+1;
1910 if (file->ino->sb->type & CPMFS_EXACT_SIZE)
1912 file->ino->sb->dir[extent].lrc = (128 - (file->pos%128)) & 0x7F;
1916 file->ino->sb->dir[extent].lrc=file->pos%128;
1918 updateTimeStamps(file->ino,extent);
1919 updateDsStamps(file->ino,extent);
1921 if (file->pos==nextextpos) findext=1;
1922 else if (file->pos==nextblockpos) findblock=1;
1927 /* cpmClose -- close */ /*{{{*/
1928 int cpmClose(struct cpmFile *file)
1933 /* cpmCreat -- creat */ /*{{{*/
1934 int cpmCreat(struct cpmInode *dir, char const *fname, struct cpmInode *ino, mode_t mode)
1937 char name[8],extension[3];
1939 struct cpmSuperBlock *drive;
1940 struct PhysDirectoryEntry *ent;
1942 if (!S_ISDIR(dir->mode))
1944 boo="No such file or directory";
1947 if (splitFilename(fname,dir->sb->type,name,extension,&user)==-1) return -1;
1949 fprintf(stderr,"cpmCreat: %s -> %d:%-.8s.%-.3s\n",fname,user,name,extension);
1951 if (findFileExtent(dir->sb,user,name,extension,0,-1)!=-1) return -1;
1953 if ((extent=findFreeExtent(dir->sb))==-1) return -1;
1954 ent=dir->sb->dir+extent;
1955 drive->dirtyDirectory=1;
1958 memcpy(ent->name,name,8);
1959 memcpy(ent->ext,extension,3);
1961 ino->mode=s_ifreg|mode;
1968 updateTimeStamps(ino,extent);
1969 updateDsStamps(ino,extent);
1974 /* cpmAttrGet -- get CP/M attributes */ /*{{{*/
1975 int cpmAttrGet(struct cpmInode *ino, cpm_attr_t *attrib)
1977 *attrib = ino->attr;
1981 /* cpmAttrSet -- set CP/M attributes */ /*{{{*/
1982 int cpmAttrSet(struct cpmInode *ino, cpm_attr_t attrib)
1984 struct cpmSuperBlock *drive;
1987 char name[8], extension[3];
1989 memset(name, 0, sizeof(name));
1990 memset(extension, 0, sizeof(extension));
1994 drive->dirtyDirectory=1;
1995 /* Strip off existing attribute bits */
1996 memcpy7(name, drive->dir[extent].name, 8);
1997 memcpy7(extension, drive->dir[extent].ext, 3);
1998 user = drive->dir[extent].status;
2000 /* And set new ones */
2001 if (attrib & CPM_ATTR_F1) name[0] |= 0x80;
2002 if (attrib & CPM_ATTR_F2) name[1] |= 0x80;
2003 if (attrib & CPM_ATTR_F3) name[2] |= 0x80;
2004 if (attrib & CPM_ATTR_F4) name[3] |= 0x80;
2005 if (attrib & CPM_ATTR_RO) extension[0] |= 0x80;
2006 if (attrib & CPM_ATTR_SYS) extension[1] |= 0x80;
2007 if (attrib & CPM_ATTR_ARCV) extension[2] |= 0x80;
2011 memcpy(drive->dir[extent].name, name, 8);
2012 memcpy(drive->dir[extent].ext, extension, 3);
2013 } while ((extent=findFileExtent(drive, user,name,extension,extent+1,-1))!=-1);
2015 /* Update the stored (inode) copies of the file attributes and mode */
2017 if (attrib&CPM_ATTR_RO) ino->mode&=~(S_IWUSR|S_IWGRP|S_IWOTH);
2018 else ino->mode|=(S_IWUSR|S_IWGRP|S_IWOTH);
2023 /* cpmChmod -- set CP/M r/o & sys */ /*{{{*/
2024 int cpmChmod(struct cpmInode *ino, mode_t mode)
2026 /* Convert the chmod() into a chattr() call that affects RO */
2027 int newatt = ino->attr & ~CPM_ATTR_RO;
2029 if (!(mode & (S_IWUSR|S_IWGRP|S_IWOTH))) newatt |= CPM_ATTR_RO;
2030 return cpmAttrSet(ino, newatt);
2033 /* cpmUtime -- set timestamps */ /*{{{*/
2034 void cpmUtime(struct cpmInode *ino, struct utimbuf *times)
2036 ino->atime = times->actime;
2037 ino->mtime = times->modtime;
2039 updateTimeStamps(ino,ino->ino);
2040 updateDsStamps(ino,ino->ino);