/* #includes */ /*{{{C}}}*//*{{{*/
-#undef _POSIX_SOURCE
-#define _POSIX_SOURCE 1
-#undef _POSIX_C_SOURCE
-#define _POSIX_C_SOURCE 2
-
-#ifdef DMALLOC
-#include "dmalloc.h"
-#endif
+#include "config.h"
#include <sys/stat.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
-#include "config.h"
+
#include "cpmdir.h"
#include "cpmfs.h"
+
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
/*}}}*/
/* #defines */ /*{{{*/
#undef CPMFS_DEBUG
#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
}
*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;
}
/*}}}*/
+/* time conversions */
+/* cpm2unix_time -- convert CP/M time to UTC */ /*{{{*/
+static time_t cpm2unix_time(int days, int hour, int min)
+{
+ /* CP/M stores timestamps in local time. We don't know which */
+ /* timezone was used and if DST was in effect. Assuming it was */
+ /* the current offset from UTC is most sensible, but not perfect. */
+
+ int year,days_per_year;
+ static int days_per_month[]={31,0,31,30,31,30,31,31,30,31,30,31};
+ char **old_environ;
+ static char gmt0[]="TZ=GMT0";
+ static char *gmt_env[]={ gmt0, (char*)0 };
+ struct tm tms;
+ time_t lt,t;
+
+ time(<);
+ t=lt;
+ tms=*localtime(<);
+ old_environ=environ;
+ environ=gmt_env;
+ lt=mktime(&tms);
+ lt-=t;
+ tms.tm_sec=0;
+ tms.tm_min=((min>>4)&0xf)*10+(min&0xf);
+ tms.tm_hour=((hour>>4)&0xf)*10+(hour&0xf);
+ tms.tm_mday=1;
+ tms.tm_mon=0;
+ tms.tm_year=78;
+ tms.tm_isdst=-1;
+ for (;;)
+ {
+ year=tms.tm_year+1900;
+ days_per_year=((year%4)==0 && ((year%100) || (year%400)==0)) ? 366 : 365;
+ if (days>days_per_year)
+ {
+ days-=days_per_year;
+ ++tms.tm_year;
+ }
+ else break;
+ }
+ for (;;)
+ {
+ days_per_month[1]=(days_per_year==366) ? 29 : 28;
+ if (days>days_per_month[tms.tm_mon])
+ {
+ days-=days_per_month[tms.tm_mon];
+ ++tms.tm_mon;
+ }
+ else break;
+ }
+ t=mktime(&tms)+(days-1)*24*3600;
+ environ=old_environ;
+ t-=lt;
+ return t;
+}
+/*}}}*/
+/* unix2cpm_time -- convert UTC to CP/M time */ /*{{{*/
+static void unix2cpm_time(time_t now, int *days, int *hour, int *min)
+{
+ struct tm *tms;
+ int i;
+
+ tms=localtime(&now);
+ *min=((tms->tm_min/10)<<4)|(tms->tm_min%10);
+ *hour=((tms->tm_hour/10)<<4)|(tms->tm_hour%10);
+ for (i=1978,*days=0; i<1900+tms->tm_year; ++i)
+ {
+ *days+=365;
+ if (i%4==0 && (i%100!=0 || i%400==0)) ++*days;
+ }
+ *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 */ /*{{{*/
static void alvInit(const struct cpmSuperBlock *d)
/*}}}*/
for (i=0; i<d->maxdir; ++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);
{
block=(unsigned char)d->dir[i].pointers[j];
if (d->size>=256) block+=(((unsigned char)d->dir[i].pointers[++j])<<8);
- if (block)
+ if (block && block<d->size)
{
#ifdef CPMFS_DEBUG
fprintf(stderr,"alvInit: allocate block %d\n",block);
/*}}}*/
/* 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; i<blocks; ++i)
- {
- if (readBlock(drive,i,(char*)(drive->dir+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; i<blocks; ++i)
- {
- if (writeBlock(drive,i,(char*)(drive->dir+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)
{
{
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;
static void updateTimeStamps(const struct cpmInode *ino, int extent)
{
struct PhysDirectoryEntry *date;
- struct tm *t;
int i;
int ca_min,ca_hour,ca_days,u_min,u_hour,u_days;
#ifdef CPMFS_DEBUG
fprintf(stderr,"CPMFS: updating time stamps for inode %d (%d)\n",extent,extent&3);
#endif
- /* compute ctime/atime */ /*{{{*/
- t=localtime(ino->sb->cnotatime ? &ino->ctime : &ino->atime);
- ca_min=((t->tm_min/10)<<4)|(t->tm_min%10);
- ca_hour=((t->tm_hour/10)<<4)|(t->tm_hour%10);
- for (i=1978,ca_days=0; i < 1900 + t->tm_year; ++i)
- {
- ca_days+=365;
- if (i%4==0 && (i%100!=0 || i%400==0)) ++ca_days;
- }
- ca_days += t->tm_yday + 1;
- /*}}}*/
- /* compute mtime */ /*{{{*/
- t=localtime(&ino->mtime);
- u_min=((t->tm_min/10)<<4)|(t->tm_min%10);
- u_hour=((t->tm_hour/10)<<4)|(t->tm_hour%10);
- for (i=1978,u_days=0; i < 1900 + t->tm_year; ++i)
- {
- u_days+=365;
- if (i%4==0 && (i%100!=0 || i%400==0)) ++u_days;
- }
- u_days += t->tm_yday + 1;
- /*}}}*/
- if ((ino->sb->type==CPMFS_P2DOS || ino->sb->type==CPMFS_DR3) && (date=ino->sb->dir+(extent|3))->status==0x21)
+ 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_CPM3_DATES) && (date=ino->sb->dir+(extent|3))->status==0x21)
{
+ ino->sb->dirtyDirectory=1;
switch (extent&3)
{
case 0: /* first entry */ /*{{{*/
}
}
/*}}}*/
+/* 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; i<argc; ++i)
+ {
+ int found;
+
+ for (j=0,found=0; j<entries; ++j)
+ {
+ if (match(dirent[j].name,argv[i]))
+ {
+ if (*gargc==gargcap) *gargv=realloc(*gargv,sizeof(char*)*(gargcap ? (gargcap*=2) : (gargcap=16)));
+ (*gargv)[*gargc]=strcpy(malloc(strlen(dirent[j].name)+1),dirent[j].name);
+ ++*gargc;
+ ++found;
+ }
+ }
+ }
+ free(dirent);
+}
+/*}}}*/
+/* superblock management */
/* diskdefReadSuper -- read super block from diskdefs file */ /*{{{*/
static int diskdefReadSuper(struct cpmSuperBlock *d, const char *format)
{
FILE *fp;
int insideDef=0,found=0;
- if ((fp=fopen(DISKDEFS,"r"))==(FILE*)0 && (fp=fopen("diskdefs","r"))==(FILE*)0)
+ d->type=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)
else if (strcmp(argv[0],"blocksize")==0) d->blksiz=strtol(argv[1],(char**)0,0);
else if (strcmp(argv[0],"maxdir")==0) d->maxdir=strtol(argv[1],(char**)0,0);
else if (strcmp(argv[0],"skew")==0) d->skew=strtol(argv[1],(char**)0,0);
+ else if (strcmp(argv[0],"skewtab")==0)
+ {
+ int pass,sectors;
+
+ for (pass=0; pass<2; ++pass)
+ {
+ char *s;
+
+ sectors=0;
+ for (s=argv[1]; *s; )
+ {
+ int phys;
+ char *end;
+
+ phys=strtol(s,&end,10);
+ if (pass==1) d->skewtab[sectors]=phys;
+ if (end==s)
+ {
+ fprintf(stderr,"%s: invalid skewtab `%s' at `%s'\n",cmd,argv[1],s);
+ exit(1);
+ }
+ s=end;
+ ++sectors;
+ if (*s==',') ++s;
+ }
+ if (pass==0) d->skewtab=malloc(sizeof(int)*sectors);
+ }
+ }
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]!='#')
+ {
+ fprintf(stderr,"%s: invalid keyword `%s'\n",cmd,argv[0]);
+ exit(1);
+ }
}
else if (argc==2 && strcmp(argv[0],"diskdef")==0)
{
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;
}
}
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;
}
/*}}}*/
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);
[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;
d->blksiz = 128 << boot_spec[6];
d->maxdir = (d->blksiz / 32) * boot_spec[7];
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 (*a==*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;
- dirent=realloc(dirent,sizeof(struct cpmDirent)*(dirsize=32));
- while (cpmReaddir(&dir,dirent+entries))
+ /* Read ds file in its entirety */
+ off=0;
+ for (i=dsoffset; i<dsoffset+dsblks; i++)
{
- ++entries;
- if (entries==dirsize) dirent=realloc(dirent,sizeof(struct cpmDirent)*(dirsize*=2));
+ if (readBlock(sb,i,((char*)sb->ds)+off,0,-1)==-1) return -1;
+ off+=sb->blksiz;
}
- for (i=optin; i<argc; ++i)
+
+ /* Verify checksums */
+ buf = (unsigned char *)sb->ds;
+ for (i=0; i<dsrecs; i++)
{
- for (j=0; j<entries; ++j) if (i<argc && match(dirent[j].name,argv[i]))
+ unsigned cksum, j;
+ cksum=0;
+ for (j=0; j<127; j++) cksum += buf[j];
+ if (buf[j]!=(cksum&0xff))
{
- if (*gargc==gargcap) *gargv=realloc(*gargv,sizeof(char*)*(gargcap ? (gargcap*=2) : (gargcap=16)));
- (*gargv)[*gargc]=strcpy(malloc(strlen(dirent[j].name)+1),dirent[j].name);
- ++*gargc;
+#ifdef CPMFS_DEBUG
+ fprintf( stderr, "!!!TIME&.DAT file failed cksum at record %i\n", i );
+#endif
+ free(sb->ds);
+ sb->ds = (struct dsDate *)0;
+ return -1;
}
+ buf += 128;
}
+ return 0;
}
/*}}}*/
-
/* cpmReadSuper -- get DPB and init in-core data for drive */ /*{{{*/
int cpmReadSuper(struct cpmSuperBlock *d, struct cpmInode *root, const char *format)
{
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);
- /* generate skew table */ /*{{{*/
- if (( d->skewtab = malloc(d->sectrk*sizeof(int))) == (int*)0)
- {
- fprintf(stderr,"%s: can not allocate memory for skew sector table\n",cmd);
- exit(1);
- }
- if (strcmp(format,"apple-do")==0)
- {
- static int skew[]={0,6,12,3,9,15,14,5,11,2,8,7,13,4,10,1};
- memcpy(d->skewtab,skew,d->sectrk*sizeof(int));
- }
- else if (strcmp(format,"apple-po")==0)
- {
- static int skew[]={0,9,3,12,6,15,1,10,4,13,7,8,2,11,5,14};
- memcpy(d->skewtab,skew,d->sectrk*sizeof(int));
- }
- else
+ Device_setGeometry(&d->dev,d->secLength,d->sectrk,d->tracks,d->offset);
+ if (d->skewtab==(int*)0) /* generate skew table */ /*{{{*/
{
int i,j,k;
+ if (( d->skewtab = malloc(d->sectrk*sizeof(int))) == (int*)0)
+ {
+ fprintf(stderr,"%s: can not allocate memory for skew sector table\n",cmd);
+ exit(1);
+ }
+ memset(d->skewtab,0,d->sectrk*sizeof(int));
for (i=j=0; i<d->sectrk; ++i,j=(j+d->skew)%d->sectrk)
{
while (1)
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; i<blocks; ++i)
+ {
+ if (readBlock(d,i,(char*)(d->dir+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;
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; i<dsrecs; i++ )
+ {
+ unsigned cksum, j;
+ cksum=0;
+ for ( j=0; j<127; j++ ) cksum += buf[j];
+ buf[j] = cksum & 0xff;
+ buf += 128;
+ }
+ dsoffset=(sb->maxdir*32+(sb->blksiz-1))/sb->blksiz;
+ dsblks=(dsrecs*128+(sb->blksiz-1))/sb->blksiz;
+
+ off=0;
+ for (i=dsoffset; i<dsoffset+dsblks; i++)
+ {
+ if (writeBlock(sb,i,((char*)(sb->ds))+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; i<blocks; ++i)
+ {
+ if (writeBlock(sb,i,(char*)(sb->dir+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;
- char **old_environ;
- static char gmt0[]="TZ=GMT0";
- static char *gmt_env[]={ gmt0, (char*)0 };
int highestExtno,highestExt=-1,lowestExtno,lowestExt=-1;
int protectMode=0;
/*}}}*/
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
i->ino=lowestExt;
i->mode=s_ifreg;
i->sb=dir->sb;
- old_environ=environ;
- environ=gmt_env; /* for mktime() */
- if
- (
- (dir->sb->type==CPMFS_P2DOS || dir->sb->type==CPMFS_DR3)
- && (date=dir->sb->dir+(lowestExt|3))->status==0x21
- )
- /* set time stamps */ /*{{{*/
- {
- /* variables */ /*{{{*/
- int u_days=0,u_hour=0,u_min=0;
- int ca_days=0,ca_hour=0,ca_min=0;
- struct tm tms;
- /*}}}*/
- 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->name[8];
- 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;
- }
- /*}}}*/
- }
- /* compute CP/M to UNIX time format */ /*{{{*/
- tms.tm_sec=0;
- tms.tm_min=((ca_min>>4)&0xf)*10+(ca_min&0xf);
- tms.tm_hour=((ca_hour>>4)&0xf)*10+(ca_hour&0xf);
- tms.tm_mday=1;
- tms.tm_mon=0;
- tms.tm_year=78;
- tms.tm_isdst=-1;
- if (i->sb->cnotatime)
- {
- i->ctime=mktime(&tms)+(ca_days-1)*24*3600;
- i->atime=0;
- }
- else
- {
- i->ctime=0;
- i->atime=mktime(&tms)+(ca_days-1)*24*3600;
- }
- tms.tm_min=((u_min>>4)&0xf)*10+(u_min&0xf);
- tms.tm_hour=((u_hour>>4)&0xf)*10+(u_hour&0xf);
- i->mtime=mktime(&tms)+(u_days-1)*24*3600;
- /*}}}*/
- }
+ /* read timestamps */ /*{{{*/
+ protectMode = readTimeStamps(i,lowestExt);
/*}}}*/
- else i->atime=i->mtime=i->ctime=0;
- environ=old_environ;
/* Determine the inode attributes */
i->attr = 0;
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;
}
/*}}}*/
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; i<d->alvSize; ++i)
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;
}
}
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;
}
/*}}}*/
{
/* variables */ /*{{{*/
struct PhysDirectoryEntry *cur=(struct PhysDirectoryEntry*)0;
- char buf[13];
+ char buf[2+8+1+3+1]; /* 00foobarxy.zzy\0 */
int i;
char *bufp;
int hasext;
{
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; i<dir->ino->sb->maxdir; ++i) if (i!=(dir->pos-RESERVED_ENTRIES))
}
*bufp='\0';
/*}}}*/
- ent->reclen=strlen(buf);
+ assert(bufp<=buf+sizeof(buf));
+ ent->reclen=bufp-buf;
strcpy(ent->name,buf);
ent->off=dir->pos;
++dir->pos;
/* cpmWrite -- write */ /*{{{*/
int cpmWrite(struct cpmFile *file, const char *buf, int count)
{
- int findext=1,findblock=1,extent=-1,extentno=-1,got=0,nextblockpos=-1,nextextpos=-1;
+ int findext=1,findblock=-1,extent=-1,extentno=-1,got=0,nextblockpos=-1,nextextpos=-1;
int blocksize=file->ino->sb->blksiz;
int extcap=(file->ino->sb->size<256 ? 16 : 8)*blocksize;
- int block=-1,start=-1,end=-1,ptr=-1;
+ int block=-1,start=-1,end=-1,ptr=-1,last=-1;
char buffer[16384];
while (count>0)
{
- if (findext)
+ if (findext) /*{{{*/
{
extentno=file->pos/16384;
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);
nextextpos=(file->pos/extcap)*extcap+extcap;
- findext=0;
- findblock=1;
- updateTimeStamps(file->ino,extent);
- }
- if (findblock)
- {
- if (start!=-1)
- {
- int last;
-
- last=writeBlock(file->ino->sb,block,buffer,start,end);
- if (file->ino->sb->size<256) for (last=15; last>=ptr; --last)
- {
- if (file->ino->sb->dir[extent].pointers[last]) break;
- }
- else for (last=14; last>=ptr; last-=2)
- {
- if (file->ino->sb->dir[extent].pointers[last] || file->ino->sb->dir[extent].pointers[last+1]) break;
- }
- if (last==ptr) /* we wrote the last used block of this extent */
- {
- file->ino->sb->dir[extent].extnol=EXTENTL((file->pos-1)/16384);
- file->ino->sb->dir[extent].extnoh=EXTENTH((file->pos-1)/16384);
- file->ino->sb->dir[extent].blkcnt=((file->pos-1)%16384)/128+1;
- file->ino->sb->dir[extent].lrc=file->pos%128;
- }
- }
if (extent==-1)
{
if ((extent=findFreeExtent(file->ino->sb))==-1) return (got==0 ? -1 : got);
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;
+ }
+ /*}}}*/
+ if (findblock) /*{{{*/
+ {
ptr=(file->pos%extcap)/blocksize;
if (file->ino->sb->size>=256) ptr*=2;
block=(unsigned char)file->ino->sb->dir[extent].pointers[ptr];
if (file->ino->sb->size>=256) block+=((unsigned char)file->ino->sb->dir[extent].pointers[ptr+1])<<8;
- if (block==0)
+ if (block==0) /* allocate new block, set start/end to cover it */ /*{{{*/
{
if ((block=allocBlock(file->ino->sb))==-1) return (got==0 ? -1 : got);
file->ino->sb->dir[extent].pointers[ptr]=block&0xff;
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
+ /*}}}*/
+ else /* read existing block and set start/end to cover modified parts */ /*{{{*/
{
start=(file->pos%blocksize)/file->ino->sb->secLength;
end=((file->pos%blocksize+count)>blocksize ? blocksize-1 : (file->pos%blocksize+count-1))/file->ino->sb->secLength;
if (file->pos%file->ino->sb->secLength) readBlock(file->ino->sb,block,buffer,start,start);
if (end!=start && (file->pos+count-1)<blocksize) readBlock(file->ino->sb,block,buffer+end*file->ino->sb->secLength,end,end);
}
+ /*}}}*/
nextblockpos=(file->pos/blocksize)*blocksize+blocksize;
findblock=0;
}
- buffer[file->pos%blocksize]=*buf++;
- ++file->pos;
- if (file->ino->size<file->pos) file->ino->size=file->pos;
- ++got;
- if (file->pos==nextblockpos) { if (file->pos==nextextpos) findext=1; else findblock=1; }
- --count;
- }
- if (start!=-1)
- {
- int last;
-
- last=writeBlock(file->ino->sb,block,buffer,start,end);
- if (file->ino->sb->size<256) for (last=15; last>=ptr; --last)
+ /*}}}*/
+ /* fill block and write it */ /*{{{*/
+ file->ino->sb->dirtyDirectory=1;
+ while (file->pos!=nextblockpos && count)
+ {
+ buffer[file->pos%blocksize]=*buf++;
+ ++file->pos;
+ if (file->ino->size<file->pos) file->ino->size=file->pos;
+ ++got;
+ --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]) break;
+ if (file->ino->sb->dir[extent].pointers[last])
+ {
+ break;
+ }
}
- else for (last=14; last>=ptr; last-=2)
+ else for (last=14; last>0; last-=2)
{
- if (file->ino->sb->dir[extent].pointers[last] || file->ino->sb->dir[extent].pointers[last+1]) break;
+ if (file->ino->sb->dir[extent].pointers[last] || file->ino->sb->dir[extent].pointers[last+1])
+ {
+ last/=2;
+ break;
+ }
}
- if (last==ptr) /* we wrote the last used block of this extent */
+ if (last>0) extentno+=(last*blocksize)/extcap;
+ 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;
+ 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].extnol=EXTENTL((file->pos-1)/16384);
- file->ino->sb->dir[extent].extnoh=EXTENTH((file->pos-1)/16384);
- file->ino->sb->dir[extent].blkcnt=((file->pos-1)%16384)/128+1;
file->ino->sb->dir[extent].lrc=file->pos%128;
- writePhysDirectory(file->ino->sb);
}
+ updateTimeStamps(file->ino,extent);
+ updateDsStamps(file->ino,extent);
+ /*}}}*/
+ if (file->pos==nextextpos) findext=1;
+ else if (file->pos==nextblockpos) findblock=1;
}
return got;
}
/* cpmClose -- close */ /*{{{*/
int cpmClose(struct cpmFile *file)
{
- if (file->mode&O_WRONLY) return (writePhysDirectory(file->ino->sb));
return 0;
}
/*}}}*/
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);
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 */ /*{{{*/
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);
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;
/* 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);
-}
-/*}}}*/
-/* cpmSync -- write directory back */ /*{{{*/
-int cpmSync(struct cpmSuperBlock *sb)
-{
- return (writePhysDirectory(sb));
+ if (!(mode & (S_IWUSR|S_IWGRP|S_IWOTH))) newatt |= CPM_ATTR_RO;
+ return cpmAttrSet(ino, newatt);
}
/*}}}*/
-/* cpmUmount -- free super block */ /*{{{*/
-void cpmUmount(struct cpmSuperBlock *sb)
+/* cpmUtime -- set timestamps */ /*{{{*/
+void cpmUtime(struct cpmInode *ino, struct utimbuf *times)
{
- 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);
}
/*}}}*/