X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=cpmfs.c;h=760c9ab3a1a9d27f46bce5b2742bb1e8de1724c6;hb=edf600144998d1a1e09898548938cc11b95c10bc;hp=4fef18368ee718ac45544fa66a25154232f5ca74;hpb=669a947da532682fcdb01a4fcbb0d9418206bcd0;p=debian%2Fcpmtools diff --git a/cpmfs.c b/cpmfs.c index 4fef183..760c9ab 100644 --- a/cpmfs.c +++ b/cpmfs.c @@ -24,14 +24,22 @@ #define INTBITS ((int)(sizeof(int)*8)) -/* There are four reserved entries: ., .., [passwd] and [label] */ +/* Convert BCD datestamp digits to binary */ + +#define BCD2BIN(x) ((((x)>>4)&0xf)*10 + ((x)&0xf)) + +#define BIN2BCD(x) (((((x)/10)&0xf)<<4) + (((x)%10)&0xf)) + +/* There are four reserved directory entries: ., .., [passwd] and [label]. +The first two of them refer to the same inode. */ #define RESERVED_ENTRIES 4 /* CP/M does not support any kind of inodes, so they are simulated. -Inode 0-(maxdir-1) are used by files which lowest extent is stored in -the respective directory entry. Inode maxdir is the root directory -and inode maxdir+1 is the passwd file, if any. */ +Inode 0-(maxdir-1) correlate to the lowest extent number (not the first +extent of the file in the directory) of a file. Inode maxdir is the +root directory, inode maxdir+1 is the optional passwd file and inode +maxdir+2 the optional disk label. */ #define RESERVED_INODES 3 @@ -74,7 +82,7 @@ static int splitFilename(const char *fullname, int type, char *name, char *ext, } *user=10*(fullname[0]-'0')+(fullname[1]-'0'); fullname+=2; - if ((fullname[0]=='\0') || (type==CPMFS_DR22 && *user>=16) || (type==CPMFS_P2DOS && *user>=32)) + if ((fullname[0]=='\0') || *user>=((type&CPMFS_HI_USER) ? 32 : 16)) { boo="illegal CP/M filename"; return -1; @@ -193,6 +201,56 @@ static void unix2cpm_time(time_t now, int *days, int *hour, int *min) *days += tms->tm_yday+1; } /*}}}*/ +/* ds2unix_time -- convert DS to Unix time */ /*{{{*/ +static time_t ds2unix_time(const struct dsEntry *entry) +{ + struct tm tms; + int yr; + + if (entry->minute==0 && + entry->hour==0 && + entry->day==0 && + entry->month==0 && + entry->year==0) return 0; + + tms.tm_isdst = -1; + tms.tm_sec = 0; + tms.tm_min = BCD2BIN( entry->minute ); + tms.tm_hour = BCD2BIN( entry->hour ); + tms.tm_mday = BCD2BIN( entry->day ); + tms.tm_mon = BCD2BIN( entry->month ) - 1; + + yr = BCD2BIN(entry->year); + if (yr<70) yr+=100; + tms.tm_year = yr; + + return mktime(&tms); +} +/*}}}*/ +/* unix2ds_time -- convert Unix to DS time */ /*{{{*/ +static void unix2ds_time(time_t now, struct dsEntry *entry) +{ + struct tm *tms; + int yr; + + if ( now==0 ) + { + entry->minute=entry->hour=entry->day=entry->month=entry->year = 0; + } + else + { + tms=localtime(&now); + entry->minute = BIN2BCD( tms->tm_min ); + entry->hour = BIN2BCD( tms->tm_hour ); + entry->day = BIN2BCD( tms->tm_mday ); + entry->month = BIN2BCD( tms->tm_mon + 1 ); + + yr = tms->tm_year; + if ( yr>100 ) yr -= 100; + entry->year = BIN2BCD( yr ); + } +} +/*}}}*/ /* allocation vector bitmap functions */ /* alvInit -- init allocation vector */ /*{{{*/ @@ -209,7 +267,7 @@ static void alvInit(const struct cpmSuperBlock *d) /*}}}*/ for (i=0; imaxdir; ++i) /* mark file blocks as used */ /*{{{*/ { - if (d->dir[i].status>=0 && d->dir[i].status<=(d->type==CPMFS_P2DOS ? 31 : 15)) + if (d->dir[i].status>=0 && d->dir[i].status<=(d->type&CPMFS_HI_USER ? 31 : 15)) { #ifdef CPMFS_DEBUG fprintf(stderr,"alvInit: allocate extent %d\n",i); @@ -324,36 +382,6 @@ static int writeBlock(const struct cpmSuperBlock *d, int blockno, const char *bu /*}}}*/ /* directory management */ -/* readPhysDirectory -- read directory from drive */ /*{{{*/ -static int readPhysDirectory(const struct cpmSuperBlock *drive) -{ - int i,blocks,entry; - - blocks=(drive->maxdir*32+drive->blksiz-1)/drive->blksiz; - entry=0; - for (i=0; idir+entry),0,-1)==-1) return -1; - entry+=(drive->blksiz/32); - } - return 0; -} -/*}}}*/ -/* writePhysDirectory -- write directory to drive */ /*{{{*/ -static int writePhysDirectory(const struct cpmSuperBlock *drive) -{ - int i,blocks,entry; - - blocks=(drive->maxdir*32+drive->blksiz-1)/drive->blksiz; - entry=0; - for (i=0; idir+entry),0,-1)==-1) return -1; - entry+=(drive->blksiz/32); - } - return 0; -} -/*}}}*/ /* findFileExtent -- find first/next extent for a file */ /*{{{*/ static int findFileExtent(const struct cpmSuperBlock *sb, int user, const char *name, const char *ext, int start, int extno) { @@ -362,7 +390,7 @@ static int findFileExtent(const struct cpmSuperBlock *sb, int user, const char * { if ( - ((unsigned char)sb->dir[start].status)<=(sb->type==CPMFS_P2DOS ? 31 : 15) + ((unsigned char)sb->dir[start].status)<=(sb->type&CPMFS_HI_USER ? 31 : 15) && (extno==-1 || (EXTENT(sb->dir[start].extnol,sb->dir[start].extnoh)/sb->extents)==(extno/sb->extents)) && isMatching(user,name,ext,sb->dir[start].status,sb->dir[start].name,sb->dir[start].ext) ) return start; @@ -394,8 +422,9 @@ static void updateTimeStamps(const struct cpmInode *ino, int extent) #endif unix2cpm_time(ino->sb->cnotatime ? ino->ctime : ino->atime,&ca_days,&ca_hour,&ca_min); unix2cpm_time(ino->mtime,&u_days,&u_hour,&u_min); - if ((ino->sb->type==CPMFS_P2DOS || ino->sb->type==CPMFS_DR3) && (date=ino->sb->dir+(extent|3))->status==0x21) + if ((ino->sb->type&CPMFS_CPM3_DATES) && (date=ino->sb->dir+(extent|3))->status==0x21) { + ino->sb->dirtyDirectory=1; switch (extent&3) { case 0: /* first entry */ /*{{{*/ @@ -435,7 +464,200 @@ static void updateTimeStamps(const struct cpmInode *ino, int extent) } } /*}}}*/ +/* updateDsStamps -- set time in datestamper file */ /*{{{*/ +static void updateDsStamps(const struct cpmInode *ino, int extent) +{ + int yr; + struct tm *cpm_time; + struct dsDate *stamp; + + if (!S_ISREG(ino->mode)) return; + if ( !(ino->sb->type&CPMFS_DS_DATES) ) return; +#ifdef CPMFS_DEBUG + fprintf(stderr,"CPMFS: updating ds stamps for inode %d (%d)\n",extent,extent&3); +#endif + + /* Get datestamp struct */ + stamp = ino->sb->ds+extent; + + unix2ds_time( ino->mtime, &stamp->modify ); + unix2ds_time( ino->ctime, &stamp->create ); + unix2ds_time( ino->atime, &stamp->access ); + + ino->sb->dirtyDs = 1; +} +/*}}}*/ +/* readTimeStamps -- read CP/M time stamp */ /*{{{*/ +static int readTimeStamps(struct cpmInode *i, int lowestExt) +{ + /* variables */ /*{{{*/ + struct PhysDirectoryEntry *date; + int u_days=0,u_hour=0,u_min=0; + int ca_days=0,ca_hour=0,ca_min=0; + int protectMode=0; + /*}}}*/ + + if ( (i->sb->type&CPMFS_CPM3_DATES) && (date=i->sb->dir+(lowestExt|3))->status==0x21 ) + { + switch (lowestExt&3) + { + case 0: /* first entry of the four */ /*{{{*/ + { + ca_days=((unsigned char)date->name[0])+(((unsigned char)date->name[1])<<8); + ca_hour=(unsigned char)date->name[2]; + ca_min=(unsigned char)date->name[3]; + u_days=((unsigned char)date->name[4])+(((unsigned char)date->name[5])<<8); + u_hour=(unsigned char)date->name[6]; + u_min=(unsigned char)date->name[7]; + protectMode=(unsigned char)date->ext[0]; + break; + } + /*}}}*/ + case 1: /* second entry */ /*{{{*/ + { + ca_days=((unsigned char)date->ext[2])+(((unsigned char)date->extnol)<<8); + ca_hour=(unsigned char)date->lrc; + ca_min=(unsigned char)date->extnoh; + u_days=((unsigned char)date->blkcnt)+(((unsigned char)date->pointers[0])<<8); + u_hour=(unsigned char)date->pointers[1]; + u_min=(unsigned char)date->pointers[2]; + protectMode=(unsigned char)date->pointers[3]; + break; + } + /*}}}*/ + case 2: /* third one */ /*{{{*/ + { + ca_days=((unsigned char)date->pointers[5])+(((unsigned char)date->pointers[6])<<8); + ca_hour=(unsigned char)date->pointers[7]; + ca_min=(unsigned char)date->pointers[8]; + u_days=((unsigned char)date->pointers[9])+(((unsigned char)date->pointers[10])<<8); + u_hour=(unsigned char)date->pointers[11]; + u_min=(unsigned char)date->pointers[12]; + protectMode=(unsigned char)date->pointers[13]; + break; + } + /*}}}*/ + } + if (i->sb->cnotatime) + { + i->ctime=cpm2unix_time(ca_days,ca_hour,ca_min); + i->atime=0; + } + else + { + i->ctime=0; + i->atime=cpm2unix_time(ca_days,ca_hour,ca_min); + } + i->mtime=cpm2unix_time(u_days,u_hour,u_min); + } + else + { + i->atime=i->mtime=i->ctime=0; + protectMode=0; + } + + return protectMode; +} +/*}}}*/ +/* readDsStamps -- read datestamper time stamp */ /*{{{*/ +static void readDsStamps(struct cpmInode *i, int lowestExt) +{ + struct dsDate *stamp; + + if ( !(i->sb->type&CPMFS_DS_DATES) ) return; + + /* Get datestamp */ + stamp = i->sb->ds+lowestExt; + + i->mtime = ds2unix_time(&stamp->modify); + i->ctime = ds2unix_time(&stamp->create); + i->atime = ds2unix_time(&stamp->access); +} +/*}}}*/ + +/* match -- match filename against a pattern */ /*{{{*/ +static int recmatch(const char *a, const char *pattern) +{ + int first=1; + + while (*pattern) + { + switch (*pattern) + { + case '*': + { + if (*a=='.' && first) return 1; + ++pattern; + while (*a) if (recmatch(a,pattern)) return 1; else ++a; + break; + } + case '?': + { + if (*a) { ++a; ++pattern; } else return 0; + break; + } + default: if (tolower(*a)==tolower(*pattern)) { ++a; ++pattern; } else return 0; + } + first=0; + } + return (*pattern=='\0' && *a=='\0'); +} + +int match(const char *a, const char *pattern) +{ + int user; + char pat[255]; + + assert(strlen(pattern)<255); + if (isdigit(*pattern) && *(pattern+1)==':') { user=(*pattern-'0'); pattern+=2; } + else if (isdigit(*pattern) && isdigit(*(pattern+1)) && *(pattern+2)==':') { user=(10*(*pattern-'0')+(*(pattern+1)-'0')); pattern+=3; } + else user=-1; + if (user==-1) sprintf(pat,"??%s",pattern); + else sprintf(pat,"%02d%s",user,pattern); + return recmatch(a,pat); +} + +/*}}}*/ +/* cpmglob -- expand CP/M style wildcards */ /*{{{*/ +void cpmglob(int optin, int argc, char * const argv[], struct cpmInode *root, int *gargc, char ***gargv) +{ + struct cpmFile dir; + int entries,dirsize=0; + struct cpmDirent *dirent=(struct cpmDirent*)0; + int gargcap=0,i,j; + + *gargv=(char**)0; + *gargc=0; + cpmOpendir(root,&dir); + entries=0; + dirsize=8; + dirent=malloc(sizeof(struct cpmDirent)*dirsize); + while (cpmReaddir(&dir,&dirent[entries])) + { + ++entries; + if (entries==dirsize) dirent=realloc(dirent,sizeof(struct cpmDirent)*(dirsize*=2)); + } + for (i=optin; itype=0; + if ((fp=fopen("diskdefs","r"))==(FILE*)0 && (fp=fopen(DISKDEFS,"r"))==(FILE*)0) { - fprintf(stderr,"%s: Neither " DISKDEFS " nor diskdefs could be opened.\n",cmd); + fprintf(stderr,"%s: Neither `diskdefs' nor `" DISKDEFS "' could be opened.\n",cmd); exit(1); } while (fgets(line,sizeof(line),fp)!=(char*)0) @@ -502,12 +725,77 @@ static int diskdefReadSuper(struct cpmSuperBlock *d, const char *format) } } else if (strcmp(argv[0],"boottrk")==0) d->boottrk=strtol(argv[1],(char**)0,0); + else if (strcmp(argv[0],"offset")==0) + { + off_t val; + unsigned int multiplier; + char *endptr; + + errno=0; + multiplier=1; + val = strtoul(argv[1],&endptr,10); + if ((errno==ERANGE && val==ULONG_MAX)||(errno!=0 && val==0)) + { + fprintf(stderr,"%s: invalid offset value \"%s\" - %s\n",cmd,argv[1],strerror(errno)); + exit(1); + } + if (endptr==argv[1]) + { + fprintf(stderr,"%s: offset value \"%s\" is not a number\n",cmd,argv[1]); + exit(1); + } + if (*endptr!='\0') + { + /* Have a unit specifier */ + switch (toupper(*endptr)) + { + case 'K': + multiplier=1024; + break; + case 'M': + multiplier=1024*1024; + break; + case 'T': + if (d->sectrk<0||d->tracks<0||d->secLength<0) + { + fprintf(stderr,"%s: offset must be specified after sectrk, tracks and secLength\n",cmd); + exit(1); + } + multiplier=d->sectrk*d->secLength; + break; + case 'S': + if (d->sectrk<0||d->tracks<0||d->secLength<0) + { + fprintf(stderr,"%s: offset must be specified after sectrk, tracks and secLength\n",cmd); + exit(1); + } + multiplier=d->secLength; + break; + default: + fprintf(stderr,"%s: unknown unit specifier \"%c\"\n",cmd,*endptr); + exit(1); + } + } + if (val*multiplier>INT_MAX) + { + fprintf(stderr,"%s: effective offset is out of range\n",cmd); + exit(1); + } + d->offset=val*multiplier; + } else if (strcmp(argv[0],"logicalextents")==0) d->extents=strtol(argv[1],(char**)0,0); else if (strcmp(argv[0],"os")==0) { - if (strcmp(argv[1],"2.2")==0) d->type=CPMFS_DR22; - else if (strcmp(argv[1],"3")==0) d->type=CPMFS_DR3; - else if (strcmp(argv[1],"p2dos")==0) d->type=CPMFS_P2DOS; + if (strcmp(argv[1],"2.2" )==0) d->type|=CPMFS_DR22; + else if (strcmp(argv[1],"3" )==0) d->type|=CPMFS_DR3; + else if (strcmp(argv[1],"isx" )==0) d->type|=CPMFS_ISX; + else if (strcmp(argv[1],"p2dos")==0) d->type|=CPMFS_P2DOS; + else if (strcmp(argv[1],"zsys" )==0) d->type|=CPMFS_ZSYS; + else + { + fprintf(stderr, "%s: invalid OS type `%s'\n", cmd, argv[1]); + exit(1); + } } } else if (argc>0 && argv[0][0]!='#') @@ -521,8 +809,10 @@ static int diskdefReadSuper(struct cpmSuperBlock *d, const char *format) insideDef=1; d->skew=1; d->extents=0; - d->type=CPMFS_DR3; + d->type|=CPMFS_DR3; d->skewtab=(int*)0; + d->offset=0; + d->boottrk=d->secLength=d->sectrk=d->tracks=-1; if (strcmp(argv[1],format)==0) found=1; } } @@ -532,6 +822,26 @@ static int diskdefReadSuper(struct cpmSuperBlock *d, const char *format) fprintf(stderr,"%s: unknown format %s\n",cmd,format); exit(1); } + if (d->boottrk<0) + { + fprintf(stderr, "%s: boottrk parameter invalid or missing from diskdef\n",cmd); + exit(1); + } + if (d->secLength<0) + { + fprintf(stderr, "%s: secLength parameter invalid or missing from diskdef\n",cmd); + exit(1); + } + if (d->sectrk<0) + { + fprintf(stderr, "%s: sectrk parameter invalid or missing from diskdef\n",cmd); + exit(1); + } + if (d->tracks<0) + { + fprintf(stderr, "%s: tracks parameter invalid or missing from diskdef\n",cmd); + exit(1); + } return 0; } /*}}}*/ @@ -541,7 +851,7 @@ static int amsReadSuper(struct cpmSuperBlock *d, const char *format) unsigned char boot_sector[512], *boot_spec; const char *err; - Device_setGeometry(&d->dev,512,9,40); + Device_setGeometry(&d->dev,512,9,40,0); if ((err=Device_readSector(&d->dev, 0, 0, (char *)boot_sector))) { fprintf(stderr,"%s: Failed to read Amstrad superblock (%s)\n",cmd,err); @@ -572,7 +882,8 @@ static int amsReadSuper(struct cpmSuperBlock *d, const char *format) [6] = Block shift [7] = No. of directory blocks */ - d->type = CPMFS_DR3; /* Amstrads are CP/M 3 systems */ + d->type = 0; + d->type |= CPMFS_DR3; /* Amstrads are CP/M 3 systems */ d->secLength = 128 << boot_spec[4]; d->tracks = boot_spec[2]; if (boot_spec[1] & 3) d->tracks *= 2; @@ -582,110 +893,59 @@ static int amsReadSuper(struct cpmSuperBlock *d, const char *format) d->skew = 1; /* Amstrads skew at the controller level */ d->skewtab = (int*)0; d->boottrk = boot_spec[5]; + d->offset = 0; d->size = (d->secLength*d->sectrk*(d->tracks-d->boottrk))/d->blksiz; d->extents = ((d->size>=256 ? 8 : 16)*d->blksiz)/16384; return 0; } /*}}}*/ - -/* match -- match filename against a pattern */ /*{{{*/ -static int recmatch(const char *a, const char *pattern) +/* cpmCheckDs -- read all datestamper timestamps */ /*{{{*/ +int cpmCheckDs(struct cpmSuperBlock *sb) { - int first=1; + int dsoffset, dsblks, dsrecs, off, i; + unsigned char *buf; - while (*pattern) - { - switch (*pattern) - { - case '*': - { - if (*a=='.' && first) return 1; - ++pattern; - while (*a) if (recmatch(a,pattern)) return 1; else ++a; - break; - } - case '?': - { - if (*a) { ++a; ++pattern; } else return 0; - break; - } - default: if (tolower(*a)==tolower(*pattern)) { ++a; ++pattern; } else return 0; - } - first=0; - } - return (*pattern=='\0' && *a=='\0'); -} + if (!isMatching(0,"!!!TIME&","DAT",sb->dir->status,sb->dir->name,sb->dir->ext)) return -1; -int match(const char *a, const char *pattern) -{ - int user; - char pat[255]; + /* Offset to ds file in alloc blocks */ + dsoffset=(sb->maxdir*32+(sb->blksiz-1))/sb->blksiz; - assert(strlen(pattern)<255); - if (isdigit(*pattern) && *(pattern+1)==':') { user=(*pattern-'0'); pattern+=2; } - else if (isdigit(*pattern) && isdigit(*(pattern+1)) && *(pattern+2)==':') { user=(10*(*pattern-'0')+(*(pattern+1)-'0')); pattern+=3; } - else user=-1; - if (user==-1) sprintf(pat,"??%s",pattern); - else sprintf(pat,"%02d%s",user,pattern); - return recmatch(a,pat); -} + dsrecs=(sb->maxdir+7)/8; + dsblks=(dsrecs*128+(sb->blksiz-1))/sb->blksiz; -/*}}}*/ -/* cpmglob -- expand CP/M style wildcards */ /*{{{*/ -void cpmglob(int optin, int argc, char * const argv[], struct cpmInode *root, int *gargc, char ***gargv) -{ - struct cpmFile dir; - int entries,dirsize=0; - struct cpmDirent *dirent=(struct cpmDirent*)0; - int gargcap=0,i,j; + /* Allocate buffer */ + sb->ds=malloc(dsblks*sb->blksiz); - *gargv=(char**)0; - *gargc=0; - cpmOpendir(root,&dir); - entries=0; - dirsize=8; - dirent=malloc(sizeof(struct cpmDirent)*dirsize); - while (cpmReaddir(&dir,&dirent[entries])) + /* Read ds file in its entirety */ + off=0; + for (i=dsoffset; ids)+off,0,-1)==-1) return -1; + off+=sb->blksiz; } - for (i=optin; ids; + for (i=0; ids); + sb->ds = (struct dsDate *)0; + return -1; } + buf += 128; } - free(dirent); + return 0; } /*}}}*/ - /* cpmReadSuper -- get DPB and init in-core data for drive */ /*{{{*/ int cpmReadSuper(struct cpmSuperBlock *d, struct cpmInode *root, const char *format) { @@ -695,7 +955,7 @@ int cpmReadSuper(struct cpmSuperBlock *d, struct cpmInode *root, const char *for assert(s_ifreg); if (strcmp(format, "amstrad")==0) amsReadSuper(d,format); else diskdefReadSuper(d,format); - Device_setGeometry(&d->dev,d->secLength,d->sectrk,d->tracks); + Device_setGeometry(&d->dev,d->secLength,d->sectrk,d->tracks,d->offset); if (d->skewtab==(int*)0) /* generate skew table */ /*{{{*/ { int i,j,k; @@ -735,10 +995,26 @@ int cpmReadSuper(struct cpmSuperBlock *d, struct cpmInode *root, const char *for return -1; } /*}}}*/ - if (d->dev.opened==0) memset(d->dir,0xe5,d->maxdir*32); - else if (readPhysDirectory(d)==-1) return -1; + if (d->dev.opened==0) /* create empty directory in core */ /*{{{*/ + { + memset(d->dir,0xe5,d->maxdir*32); + } + /*}}}*/ + else /* read directory in core */ /*{{{*/ + { + int i,blocks,entry; + + blocks=(d->maxdir*32+d->blksiz-1)/d->blksiz; + entry=0; + for (i=0; idir+entry),0,-1)==-1) return -1; + entry+=(d->blksiz/32); + } + } + /*}}}*/ alvInit(d); - if (d->type==CPMFS_DR3) /* read additional superblock information */ /*{{{*/ + if (d->type&CPMFS_CPM3_OTHER) /* read additional superblock information */ /*{{{*/ { int i; @@ -818,21 +1094,91 @@ int cpmReadSuper(struct cpmSuperBlock *d, struct cpmInode *root, const char *for d->labelLength=0; } d->root=root; + d->dirtyDirectory = 0; root->ino=d->maxdir; root->sb=d; root->mode=(s_ifdir|0777); root->size=0; root->atime=root->mtime=root->ctime=0; + + d->dirtyDs=0; + if (cpmCheckDs(d)==0) d->type|=CPMFS_DS_DATES; + else d->ds=(struct dsDate*)0; + + return 0; +} +/*}}}*/ +/* syncDs -- write all datestamper timestamps */ /*{{{*/ +static int syncDs(const struct cpmSuperBlock *sb) +{ + if (sb->dirtyDs) + { + int dsoffset, dsblks, dsrecs, off, i; + unsigned char *buf; + + dsrecs=(sb->maxdir+7)/8; + + /* Re-calculate checksums */ + buf = (unsigned char *)sb->ds; + for ( i=0; imaxdir*32+(sb->blksiz-1))/sb->blksiz; + dsblks=(dsrecs*128+(sb->blksiz-1))/sb->blksiz; + + off=0; + for (i=dsoffset; ids))+off,0,-1)==-1) return -1; + off+=sb->blksiz; + } + } return 0; } /*}}}*/ +/* cpmSync -- write directory back */ /*{{{*/ +int cpmSync(struct cpmSuperBlock *sb) +{ + if (sb->dirtyDirectory) + { + int i,blocks,entry; + + blocks=(sb->maxdir*32+sb->blksiz-1)/sb->blksiz; + entry=0; + for (i=0; idir+entry),0,-1)==-1) return -1; + entry+=(sb->blksiz/32); + } + sb->dirtyDirectory=0; + } + if (sb->type&CPMFS_DS_DATES) syncDs(sb); + return 0; +} +/*}}}*/ +/* cpmUmount -- free super block */ /*{{{*/ +void cpmUmount(struct cpmSuperBlock *sb) +{ + cpmSync(sb); + if (sb->type&CPMFS_DS_DATES) free(sb->ds); + free(sb->alv); + free(sb->skewtab); + free(sb->dir); + if (sb->passwdLength) free(sb->passwd); +} +/*}}}*/ + /* cpmNamei -- map name to inode */ /*{{{*/ int cpmNamei(const struct cpmInode *dir, const char *filename, struct cpmInode *i) { /* variables */ /*{{{*/ int user; char name[8],extension[3]; - struct PhysDirectoryEntry *date; int highestExtno,highestExt=-1,lowestExtno,lowestExt=-1; int protectMode=0; /*}}}*/ @@ -911,7 +1257,14 @@ int cpmNamei(const struct cpmInode *dir, const char *filename, struct cpmInode * if (dir->sb->dir[highestExt].pointers[2*block] || dir->sb->dir[highestExt].pointers[2*block+1]) break; } if (dir->sb->dir[highestExt].blkcnt) i->size+=((dir->sb->dir[highestExt].blkcnt&0xff)-1)*128; - i->size+=dir->sb->dir[highestExt].lrc ? (dir->sb->dir[highestExt].lrc&0xff) : 128; + if (dir->sb->type & CPMFS_ISX) + { + i->size += (128 - dir->sb->dir[highestExt].lrc); + } + else + { + i->size+=dir->sb->dir[highestExt].lrc ? (dir->sb->dir[highestExt].lrc&0xff) : 128; + } #ifdef CPMFS_DEBUG fprintf(stderr,"cpmNamei: size=%ld\n",(long)i->size); #endif @@ -920,70 +1273,9 @@ int cpmNamei(const struct cpmInode *dir, const char *filename, struct cpmInode * i->ino=lowestExt; i->mode=s_ifreg; i->sb=dir->sb; - /* set timestamps */ /*{{{*/ - if - ( - (dir->sb->type==CPMFS_P2DOS || dir->sb->type==CPMFS_DR3) - && (date=dir->sb->dir+(lowestExt|3))->status==0x21 - ) - { - /* variables */ /*{{{*/ - int u_days=0,u_hour=0,u_min=0; - int ca_days=0,ca_hour=0,ca_min=0; - /*}}}*/ - switch (lowestExt&3) - { - case 0: /* first entry of the four */ /*{{{*/ - { - ca_days=((unsigned char)date->name[0])+(((unsigned char)date->name[1])<<8); - ca_hour=(unsigned char)date->name[2]; - ca_min=(unsigned char)date->name[3]; - u_days=((unsigned char)date->name[4])+(((unsigned char)date->name[5])<<8); - u_hour=(unsigned char)date->name[6]; - u_min=(unsigned char)date->name[7]; - protectMode=(unsigned char)date->ext[0]; - break; - } - /*}}}*/ - case 1: /* second entry */ /*{{{*/ - { - ca_days=((unsigned char)date->ext[2])+(((unsigned char)date->extnol)<<8); - ca_hour=(unsigned char)date->lrc; - ca_min=(unsigned char)date->extnoh; - u_days=((unsigned char)date->blkcnt)+(((unsigned char)date->pointers[0])<<8); - u_hour=(unsigned char)date->pointers[1]; - u_min=(unsigned char)date->pointers[2]; - protectMode=(unsigned char)date->pointers[3]; - break; - } - /*}}}*/ - case 2: /* third one */ /*{{{*/ - { - ca_days=((unsigned char)date->pointers[5])+(((unsigned char)date->pointers[6])<<8); - ca_hour=(unsigned char)date->pointers[7]; - ca_min=(unsigned char)date->pointers[8]; - u_days=((unsigned char)date->pointers[9])+(((unsigned char)date->pointers[10])<<8); - u_hour=(unsigned char)date->pointers[11]; - u_min=(unsigned char)date->pointers[12]; - protectMode=(unsigned char)date->pointers[13]; - break; - } - /*}}}*/ - } - if (i->sb->cnotatime) - { - i->ctime=cpm2unix_time(ca_days,ca_hour,ca_min); - i->atime=0; - } - else - { - i->ctime=0; - i->atime=cpm2unix_time(ca_days,ca_hour,ca_min); - } - i->mtime=cpm2unix_time(u_days,u_hour,u_min); - } - else i->atime=i->mtime=i->ctime=0; + /* read timestamps */ /*{{{*/ + protectMode = readTimeStamps(i,lowestExt); /*}}}*/ /* Determine the inode attributes */ @@ -1003,6 +1295,9 @@ int cpmNamei(const struct cpmInode *dir, const char *filename, struct cpmInode * i->mode|=0444; if (!(dir->sb->dir[lowestExt].ext[0]&0x80)) i->mode|=0222; if (extension[0]=='C' && extension[1]=='O' && extension[2]=='M') i->mode|=0111; + + readDsStamps(i,lowestExt); + return 0; } /*}}}*/ @@ -1014,7 +1309,7 @@ void cpmStatFS(const struct cpmInode *ino, struct cpmStatFS *buf) d=ino->sb; buf->f_bsize=d->blksiz; - buf->f_blocks=(d->tracks*d->sectrk*d->secLength)/d->blksiz; + buf->f_blocks=d->size; buf->f_bfree=0; buf->f_bused=-(d->maxdir*32+d->blksiz-1)/d->blksiz; for (i=0; ialvSize; ++i) @@ -1064,12 +1359,12 @@ int cpmUnlink(const struct cpmInode *dir, const char *fname) drive=dir->sb; if (splitFilename(fname,dir->sb->type,name,extension,&user)==-1) return -1; if ((extent=findFileExtent(drive,user,name,extension,0,-1))==-1) return -1; + drive->dirtyDirectory=1; drive->dir[extent].status=(char)0xe5; do { drive->dir[extent].status=(char)0xe5; } while ((extent=findFileExtent(drive,user,name,extension,extent+1,-1))>=0); - if (writePhysDirectory(drive)==-1) return -1; alvInit(drive); return 0; } @@ -1100,11 +1395,11 @@ int cpmRename(const struct cpmInode *dir, const char *old, const char *new) } do { + drive->dirtyDirectory=1; drive->dir[extent].status=newuser; memcpy7(drive->dir[extent].name, newname, 8); memcpy7(drive->dir[extent].ext, newext, 3); } while ((extent=findFileExtent(drive,olduser,oldname,oldext,extent+1,-1))!=-1); - if (writePhysDirectory(drive)==-1) return -1; return 0; } /*}}}*/ @@ -1191,7 +1486,7 @@ int cpmReaddir(struct cpmFile *dir, struct cpmDirent *ent) { int first=dir->pos-RESERVED_ENTRIES; - if ((cur=dir->ino->sb->dir+(dir->pos-RESERVED_ENTRIES))->status>=0 && cur->status<=(dir->ino->sb->type==CPMFS_P2DOS ? 31 : 15)) + if ((cur=dir->ino->sb->dir+(dir->pos-RESERVED_ENTRIES))->status>=0 && cur->status<=(dir->ino->sb->type&CPMFS_HI_USER ? 31 : 15)) { /* determine first extent for the current file */ /*{{{*/ for (i=0; iino->sb->maxdir; ++i) if (i!=(dir->pos-RESERVED_ENTRIES)) @@ -1367,7 +1662,9 @@ int cpmWrite(struct cpmFile *file, const char *buf, int count) file->ino->sb->dir[extent].extnoh=EXTENTH(extentno); file->ino->sb->dir[extent].blkcnt=0; file->ino->sb->dir[extent].lrc=0; + time(&file->ino->ctime); updateTimeStamps(file->ino,extent); + updateDsStamps(file->ino,extent); } findext=0; findblock=1; @@ -1387,6 +1684,9 @@ int cpmWrite(struct cpmFile *file, const char *buf, int count) start=0; end=(blocksize-1)/file->ino->sb->secLength; memset(buffer,0,blocksize); + time(&file->ino->ctime); + updateTimeStamps(file->ino,extent); + updateDsStamps(file->ino,extent); } /*}}}*/ else /* read existing block and set start/end to cover modified parts */ /*{{{*/ @@ -1402,6 +1702,7 @@ int cpmWrite(struct cpmFile *file, const char *buf, int count) } /*}}}*/ /* fill block and write it */ /*{{{*/ + file->ino->sb->dirtyDirectory=1; while (file->pos!=nextblockpos && count) { buffer[file->pos%blocksize]=*buf++; @@ -1411,6 +1712,7 @@ int cpmWrite(struct cpmFile *file, const char *buf, int count) --count; } (void)writeBlock(file->ino->sb,block,buffer,start,end); + time(&file->ino->mtime); if (file->ino->sb->size<256) for (last=15; last>=0; --last) { if (file->ino->sb->dir[extent].pointers[last]) @@ -1430,20 +1732,26 @@ int cpmWrite(struct cpmFile *file, const char *buf, int count) file->ino->sb->dir[extent].extnol=EXTENTL(extentno); file->ino->sb->dir[extent].extnoh=EXTENTH(extentno); file->ino->sb->dir[extent].blkcnt=((file->pos-1)%16384)/128+1; - file->ino->sb->dir[extent].lrc=file->pos%128; + if (file->ino->sb->type & CPMFS_EXACT_SIZE) + { + file->ino->sb->dir[extent].lrc = (128 - (file->pos%128)) & 0x7F; + } + else + { + file->ino->sb->dir[extent].lrc=file->pos%128; + } updateTimeStamps(file->ino,extent); + updateDsStamps(file->ino,extent); /*}}}*/ if (file->pos==nextextpos) findext=1; else if (file->pos==nextblockpos) findblock=1; } - writePhysDirectory(file->ino->sb); return got; } /*}}}*/ /* cpmClose -- close */ /*{{{*/ int cpmClose(struct cpmFile *file) { - if (file->mode&O_WRONLY) return (writePhysDirectory(file->ino->sb)); return 0; } /*}}}*/ @@ -1469,6 +1777,7 @@ int cpmCreat(struct cpmInode *dir, const char *fname, struct cpmInode *ino, mode drive=dir->sb; if ((extent=findFreeExtent(dir->sb))==-1) return -1; ent=dir->sb->dir+extent; + drive->dirtyDirectory=1; memset(ent,0,32); ent->status=user; memcpy(ent->name,name,8); @@ -1476,20 +1785,22 @@ int cpmCreat(struct cpmInode *dir, const char *fname, struct cpmInode *ino, mode ino->ino=extent; ino->mode=s_ifreg|mode; ino->size=0; + time(&ino->atime); time(&ino->mtime); time(&ino->ctime); ino->sb=dir->sb; updateTimeStamps(ino,extent); - writePhysDirectory(dir->sb); + updateDsStamps(ino,extent); return 0; } /*}}}*/ + /* cpmAttrGet -- get CP/M attributes */ /*{{{*/ int cpmAttrGet(struct cpmInode *ino, cpm_attr_t *attrib) { - *attrib = ino->attr; - return 0; + *attrib = ino->attr; + return 0; } /*}}}*/ /* cpmAttrSet -- set CP/M attributes */ /*{{{*/ @@ -1505,6 +1816,7 @@ int cpmAttrSet(struct cpmInode *ino, cpm_attr_t attrib) drive = ino->sb; extent = ino->ino; + drive->dirtyDirectory=1; /* Strip off existing attribute bits */ memcpy7(name, drive->dir[extent].name, 8); memcpy7(extension, drive->dir[extent].ext, 3); @@ -1524,7 +1836,6 @@ int cpmAttrSet(struct cpmInode *ino, cpm_attr_t attrib) memcpy(drive->dir[extent].name, name, 8); memcpy(drive->dir[extent].ext, extension, 3); } while ((extent=findFileExtent(drive, user,name,extension,extent+1,-1))!=-1); - if (writePhysDirectory(drive)==-1) return -1; /* Update the stored (inode) copies of the file attributes and mode */ ino->attr=attrib; @@ -1537,25 +1848,20 @@ int cpmAttrSet(struct cpmInode *ino, cpm_attr_t attrib) /* cpmChmod -- set CP/M r/o & sys */ /*{{{*/ int cpmChmod(struct cpmInode *ino, mode_t mode) { - /* Convert the chmod() into a chattr() call that affects RO */ - int newatt = ino->attr & ~CPM_ATTR_RO; + /* Convert the chmod() into a chattr() call that affects RO */ + int newatt = ino->attr & ~CPM_ATTR_RO; - if (!(mode & (S_IWUSR|S_IWGRP|S_IWOTH))) newatt |= CPM_ATTR_RO; - return cpmAttrSet(ino, newatt); + if (!(mode & (S_IWUSR|S_IWGRP|S_IWOTH))) newatt |= CPM_ATTR_RO; + return cpmAttrSet(ino, newatt); } /*}}}*/ -/* cpmSync -- write directory back */ /*{{{*/ -int cpmSync(struct cpmSuperBlock *sb) +/* cpmUtime -- set timestamps */ /*{{{*/ +void cpmUtime(struct cpmInode *ino, struct utimbuf *times) { - return (writePhysDirectory(sb)); -} -/*}}}*/ -/* cpmUmount -- free super block */ /*{{{*/ -void cpmUmount(struct cpmSuperBlock *sb) -{ - free(sb->alv); - free(sb->skewtab); - free(sb->dir); - if (sb->passwdLength) free(sb->passwd); + ino->atime = times->actime; + ino->mtime = times->modtime; + time(&ino->ctime); + updateTimeStamps(ino,ino->ino); + updateDsStamps(ino,ino->ino); } /*}}}*/