view vdisk.c @ 44:b26c23331d02

add more function on vdisk
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Thu, 19 Jul 2018 11:31:17 +0900
parents 7a83a6a1685a
children 07c84761da6f
line wrap: on
line source

/********************************************************************
* Virtual RBF - Random Block File Manager
*
*         Shinji KONO  (kono@ie.u-ryukyu.ac.jp)  2018/7/17
*         GPL v1 license
*/

#define engine extern
#include "v09.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <dirent.h>
#include <string.h>


#ifdef USE_MMU
extern char *prog ;   // for disass
extern Byte * mem0(Byte *iphymem, Word adr, Byte *immu) ;
#define pmem(a)  mem0(phymem,a,mmu)
#define umem(a)  (mem[0x41+IOPAGE]?mem0(phymem,a,&mem[0x21+IOPAGE]):mem0(phymem,a,&mem[0x20+IOPAGE]))
#else
#define pmem(a)  (&mem[a])
#define umem(a)  (&mem[a])
#endif

#define MAXPDV 256

typedef struct pathDesc {
    char *name;
    FILE *fp;
    int mode;
    int inode ;  // lower 24 bit of unix inode, os9 lsn
    int num ;
    char use ;
    char dir;
    char *fd ;
    char *dirfp;
} PathDesc, *PathDescPtr;


PathDesc pdv[MAXPDV];

PathDesc *findPD() {
    for(int i=0;i<MAXPDV;i++) {
        if (!pdv[i].use) {
            pdv[i].use = 1;
            pdv[i].num = i;
            pd[i].mode = 0;
            pd[i].fp = 0;
            pd[i].dirfp = 0;
            pd[i].name = 0;
            pd[i].fd = 0;
            return &pdv[i];
        }
    }
    return 0;
}

void closepd(PathDesc *pd) {
    int err = fclose(pd->fp) ;
    pd->use = 0;
    pd->fp = 0;
    pd->mode = 0;
    free(pd->dirfp); pd->dirfp = 0;
    free(pd->name); pd->name = 0;
    free(pd->fd); pd->fd = 0;
}

#define MAXPAHTLEN 256

char *addCurdir(char *name, PathDesc *curdir) {
    int ns = strlen(name);
    int ps = ns;
    int ds=0 ;
    if (curdir==0 && name[0]!='/') return 0; // no current directory
    if (name[0]!='/') ps += (ds=strlen(curdir->name))+1;
    char *path = (char*)malloc(ps+1);
    int i = 0;
    if (ds) {
        for(;i<ds;i++) path[i] = curdir->name[i];
        path[i++] = '/';
    }
    for(int j=0;j<ns;j++,i++) path[i] = name[j];
    path[i++] = 0;
    return path;
}

char * checkFileName(char *path, PathDesc *pd, PathDesc *curdir) {
    char *p = path;
    char *name = path;
    int maxlen = MAXPAHTLEN;
    while(*p!=0 && (*p&80)==0 && maxlen-->0) p++;
    if (maxlen==MAXPAHTLEN) return 0;
    if (*p!=0) {
        name = (char *)malloc(p-path+1); 
        strncpy(path,p, MAXPAHTLEN-maxlen);
    }
    char *name1 = addCurdir(name,curdir);
    if (name1!=name && name1!=path) free(name);
    pd->name = name1;
    return p;
}

void os9setmode(char &os9mode,int mode) {
    char m = 0;
    if (mode&S_IFDIR) m|=0x80;
    if (mode&S_IRUSR) m|=0x01;
    if (mode&S_IWUSR) m|=0x02;
    if (mode&S_IXUSR) m|=0x04;
    if (mode&S_IROTH) m|=0x08;
    if (mode&S_IWOTH) m|=0x10;
    if (mode&S_IXOTH) m|=0x20;
    m|=0x60; // always sharable
    *os9mode = m;
}

char * os9toUnixAttr(Byte attr) {
    if ((attr&0x1) && (attr&0x2)) return "r+";
    if (!(attr&0x1) && (attr&0x2)) return "w";
    if ((attr&0x1) && !(attr&0x2)) return "r";
    return "r";
}

/*
 *   os9 file descriptor
 *   * File Descriptor Format
 *
 * The file descriptor is a sector that is present for every file
 * on an RBF device.  It contains attributes, modification dates,
 * and segment information on a file.
 *
 *               ORG       0
 *FD.ATT         RMB       1                   Attributes
 *FD.OWN         RMB       2                   Owner
 *FD.DAT         RMB       5                   Date last modified
 *FD.LNK         RMB       1                   Link count
 *FD.SIZ         RMB       4                   File size
 *FD.Creat       RMB       3                   File creation date (YY/MM/DD)
 *FD.SEG         EQU       .                   Beginning of segment list
 * Segment List Entry Format
               ORG       0
 * FDSL.A         RMB       3                   Segment beginning physical sector number
 * FDSL.B         RMB       2                   Segment size
 * FDSL.S         EQU       .                   Segment list entry size
 * FD.LS1         EQU       FD.SEG+((256-FD.SEG)/FDSL.S-1)*FDSL.S
 * FD.LS2         EQU       (256/FDSL.S-1)*FDSL.S
 * MINSEC         SET       16
 */

/*
 *   os9 directory structure
 *
 *               ORG       0
 *DIR.NM         RMB       29                  File name
 *DIR.FD         RMB       3                   File descriptor physical sector number
 *DIR.SZ         EQU       .                   Directory record size
 */


/* read direcotry entry */
int os9opendir(PathDesc pd) {
    DIR *dir;
    struct dirent *dp;
    dir = opendir(pd->name);
    pd->name=fname;
    int dircount = 0;
    while ((dp = readdir(dirp)) != NULL) dircout++;   // pass 1 to determine the size
    if (dircount==0) return 0;  // should contains . and .. at least
    pd->sz = dircount*DIR_SZ
    pd->dirfp = (char *)malloc(dircount*DIR_SZ);
    rewinddir(dir);
    int i = 0;
    while ((dp = readdir(dirp)) != NULL) {
        i += DIR_SZ;
        if (i>pd->SZ) return 0;
        int j = 0;
        for(j = 0; j < DIR_NM ; j++) {
            if (j< dp->d_namlen) 
               pd->dirfp[j] = dp->d_name[j];
            else
               pd->dirfp[j] = 0;
        }
        pd->dirfp[j] = (d_ino&0xff0000)>>16;
        pd->dirfp[j+1] = (d_ino&0xff00)>>8;
        pd->dirfp[j+2] = d_ino&0xff;
    }
    pd->fp = fmemopen(pd->dirfp,pd->sz,"r");
    return 0;
}


/* read file descriptor of Path Desc 
 *    create file descriptor sector if necessary
 *    if buf!=0, copy it 
 */
int filedescriptor(Byte *buf, int len, PathDesc *pd) {
    struct stat st;
    if (pd->fd) return 1;
    pd->fd = (char *)malloc(256);
    stat((pd->name,&st);
    os9setmode(pd->fd,st.st_mode);
    pd->fd+FD_OWN=(st.st_uid&0xff00)>>8;
    pd->fd+FD_OWN+1=st.st_uid&0xff;
    os9setdate(pd->fd,st.st_mtimespec);
    pd->fd+FD_LNK+0=(st.st_uid&0xff000000)>>24;
    pd->fd+FD_LNK+1=(st.st_uid&0xff0000)>>16;
    pd->fd+FD_LNK+2=(st.st_uid&0xff00)>>8;
    pd->fd+FD_LNK+3=st.st_nlink&0xff;
    os9setdate(pd->fd,st.st_ctimespec);
    // dummy segment list
    for(int i=0x10 ; i < 256; i++) pd->fd[i] = 0;
    return 0;
}

/* read direcotry entry for any file in the directory 
 *     we only returns a file descriptor only in the current opened directory
 *
 *     inode==0 should return disk id section
 *     inode==bitmap should return disk sector map for os9 free command
 */
int fdinfo(Byte *buf,int len, int inode) {
    int i;
    for(i=0;i<MAXPDV;i++) {
        PathDesc *pd = pdv+i;
        if (!pd->use || !pd->dir) continue;
        //  find inode in directory
        char *dir = (char *)pd->dirfp;
        while( dir < dir + pd->sz ) {
            Byte *p = (Byte *)(dir + DIR_NM);
            int dinode = (p[0]<<16)+(p[1]<<8)+p[2];
            if (inode == dir) {
                filedescriptor(buf,len,pd);
                return 1;
            }
        }
    }
    return 0;
}

/*
 *   each command should have preallocated os9 path descriptor on Y
 *
 *   name or buffer, can be in a user map, pmem check that drive number ( mem[0x41+IOPAGE]  0 sys 1 user )
 *   current directory path number                                        mem[0x42+IOPAGE]  
 */
void do_vdisk(Byte cmd) {
    int err;
    PathDesc *pd = pdv + *areg;
    PathDesc *curdir = pdv+mem[0x42+IOPAGE];
    Byte mode,attr ;
    char *path,*next,*buf;

    switch(cmd) {
        /*
        * I$Create Entry Point
        *
        * Entry: A = access mode desired
        *        B = file attributes
        *        X = address of the pathlist
        *
        * Exit:  A = pathnum
        *        X = last byte of pathlist address
        *
        * Error: CC Carry set
        *        B = errcode
        */
        case 0xd1:
            pd = findPD();
            if (pd==0) { *breg = 0xff; break; }
            mode = *areg;
            attr = *breg;
            path = (char *)pmem(xreg);
            next = pd->name  = checkFileName(path,pd,curdir);
            pd->dir = 0;
            pd->fp = fopen(pd->name, os9toUnixAttr(attr));
            if (next!=0 && pd->fp ) {
                xreg += ( next - path );
                pd->use = 1;
            } else  {
                *breg = 0xff;
                free(pd->name);
            }
            break;

        /*
        * I$Open Entry Point
        *
        * Entry: A = access mode desired
        *        X = address of the pathlist
        *
        * Exit:  A = pathnum
        *        X = last byte of pathlist address
        *
        * Error: CC Carry set
        *        B = errcode
        */
        case 0xd2:
            pd = findPD();
            *breg = 0xff;
            if (pd==0) break; 
            mode = *areg;
            attr = *breg;
            pd->fp = 0;
            path = (char*)pmem(xreg);
            next = pd->name = checkFileName(path,pd,curdir);
            if (next!=0) {
                struct stat buf;
                if (stat(pd->name,&buf)!=0) break;
                if ((buf.st_mode & S_IFMT) == S_IFDIR) {
                    pd->dir = 1;
                    os9opendir(pd);
                } else {
                    char *fname;
                    pd->dir = 0;
                    if (curdir==0 && name[0]!='/') return 0; // no current directory
                    pd->fp = fopen( fname=findfile(pd->name,curdir),"r+");
                    free(fname);
                }
            }
            if (next!=0 && pd->fp !=0) {
                *areg = pd->num;
                xreg += ( next - path );
            } 
            break;

        /*
        * I$MakDir Entry Point
        *
        * Entry: X = address of the pathlist
        *        B = directory attributes
        *
        * Exit:  X = last byte of pathlist address
        *
        * Error: CC Carry set
        *        B = errcode
        */

        case 0xd3:
            pd = findPD();
            *breg = 0xff;
            if (pd==0) break; 
            path = (char*)pmem(xreg);
            next =  checkFileName(path,pd,curdir);
            if (next!=0 &&  mkdir(pd->name,0)!= 0 ) {
                xreg += ( next - path );
                *breg = 0;
            } 
            closepd(pd);
            break;

        /*
        * I$ChgDir Entry Point
        *
        *    data dir   P$DIO 3-5     contains open dir Path number 
        *    exec dir   P$DIO 9-11    contains open dir Path number 
        *
        * Entry:
        *
        * *areg = access mode
        *    0 = Use any special device capabilities
        *    1 = Read only
        *    2 = Write only
        *    3 = Update (read and write)
        * Entry: X = address of the pathlist
        *
        * Exit:  X = last byte of pathlist address
        *        A = open directory Path Number
        *
        * Error: CC Carry set
        *        B = errcode
        */
        case 0xd4:
            pd = findPD();
            if (pd==0) { *breg = 0xff; break; }
            path = (char*)pmem(xreg);
            next = checkFileName(path,pd,curdir);
            if (next!=0 && os9opendir(pd)) {
                if (curdir!=pd) closepd(curdir);
                if (pd->name != path) {
                    free(path);
                }
                xreg += ( next - path );
                *areg = pd->num;
                *breg = 0;
                break;
            } 
            *breg = 0xff;
            break;

        /*
        * I$Delete Entry Point
        *
        * Entry:
        *
        * Exit:
        *
        * Error: CC Carry set
        *        B = errcode
        */
        case 0xd5: {
            *breg = 0xff;
            if (pd==0) break;
            struct stat st;
            path = (char*)pmem(xreg);
            next = checkFileName(path,pd,curdir);
            if (next!=0 && stat(pd->name,&st)!=0) break;
            if (next!=0 && ((st->st_mode&S_IFDIR)?rmdir(pd->name):unlink(pd->name)) == 0) {
                xreg += ( next - path );
                *breg = 0;
            } 
         }
            break;

        /*
        * I$Seek Entry Point
        *
        * Entry Conditions
        * A path number
        * X MS 16 bits of the desired file position
        * Y LS 16 bits of the desired file position
        * 
        * Exit:
        *
        * Error: CC Carry set
        *        B = errcode
        */
        case 0xd6: {
            *breg = 0xff;
            if (pd==0) break;
            off_t seek = (xreg<<16)+yreg;
            *breg = fseek(pd->fp,(off_t)seek,SEEK_SET);
            break;
            }
        /*
        * I$ReadLn Entry Point
        *
        * Entry:
        * Entry Conditions     in correct mmu map
        * A path number
        * X address at which to store data
        * Y maximum number of bytes to read
        *
        * Exit:
        *
        * Y number of bytes read
        *
        * Error: CC Carry set
        *        B = errcode
        *
        *
        */
        case 0xd7:
            *breg = 0xff;
            if (pd==0) break;
            if (pd->dir) break;
            buf = (char*)pmem(xreg);
            if (fgets(buf,yreg,pd->fp)) {
                int len = yreg;
                int i;
                for(i=0;i<len && buf[i];i++);
                if (i>0 && buf[i-1]=='\n') {
                    buf[i-1] = '\r';
                    yreg = i;
                }
                *breg = 0;
            } 
            break;

        /*
        * I$Read Entry Point
        *
        * Entry:
        *
        * Exit:
        *
        * Error: CC Carry set
        *        B = errcode
        */
        case 0xd8:
            *breg = 0xff;
            if (pd==0) break;
            buf = (char*)pmem(xreg);
            err =  fread(buf,yreg,1,pd->fp);
            *breg = err==0?0xff:0 ;
            yreg = err ;
            break;

        /*
        * I$WritLn Entry Point
        *
        * Entry:
        * A path number
        * X address of the data to write
        * Y maximum number of bytes to read

        *
        * Exit:
        * Y number of bytes written
        *
        * Error: CC Carry set
        *        B = errcode
        */
        case 0xd9:
            *breg = 0xff;
            if (pd==0) break;
            if (pd->dir) break;
            int len = yreg;
            int i = 0;
            Byte *buf = pmem(xreg);
            while(len>0 && *buf !='\r') {
                fputc(buf[i++],pd->fp);
                len--;
            }
            if (buf[i]=='\r') {
                fputc('\n',pd->fp);
                i++;
            }
            *breg = 0;
            yreg = i;
            break; 

        /*
        * I$Write Entry Point
        *
        * Entry:
        *
        * Exit:
        *
        * Error: CC Carry set
        *        B = errcode
        */
        case 0xda :
            *breg = 0xff;
            if (!pd->dir) {
                Byte *buf = pmem(xreg);
                int len = yreg;
                int err = fwrite(buf,len,1,pd->fp);
                *breg = err?0xff:0;
                yreg = err; 
            }
            break;

        /* I$Close Entry Point
        *
        * Entry: A = path number
        *
        * Exit:
        *
        * Error: CC Carry set
        *        B = errcode
        *
        */
        case 0xdb:
            *breg = 0xff;
            if (pd==0) break;
            closepd(pd);
            *breg = 0;
            break;

        /*
        * I$GetStat Entry Point
        *
        * Entry:
        *
        * Exit:
        *
        * Error: CC Carry set
        *        B = errcode
        */
        case 0xdc: {
            struct stat st;
            off_t pos;
            switch (*breg) {
                case 01: // SS.Ready
                    *breg = 0xff;
                    if (pd==0) break;
                    fstat(fileno(pd->fp),&st);
                    if ((pos = ftell(pd->fp))) {
                        xreg =  st.st_size - pos;
                        *breg = 0;
                    }
                    break;
                case 02: // SS.SIZ
                    *breg = 0xff;
                    if (pd==0) break;
                    fstat(fileno(pd->fp),&st);
                    xreg =  st.st_size ;
                    *breg = 0;
                    break;
                case 05: // SS.Pos
                    *breg = 0xff;
                    if (pd==0) break;
                    xreg =  ftell(pd->fp);
                    *breg = 0;
                    break;
                case 15: // SS.FD
                /*         SS.FD ($0F) - Returns a file descriptor
                 *                          Entry: R$A=Path #
                 *                                 R$B=SS.FD ($0F)
                 *                                 R$X=Pointer to a 256 byte buffer
                 *                                 R$Y=# bytes of FD required
                 *   this should be handled in vrbf
                 */
                    *breg = 0xff;
                    if (pd==0) break;
                    *breg = filedescriptor(pmem(xreg), yreg,pd) ;
                    break;
                case 0x20: // Pos.FDInf    mandatry for dir command
                /*         SS.FDInf ($20) - Directly reads a file descriptor from anywhere
                 *                          on drive.
                 *                          Entry: R$A=Path #
                 *                                 R$B=SS.FDInf ($20)
                 *                                 R$X=Pointer to a 256 byte buffer
                 *                                 R$Y= MSB - Length of read
                 *                                      LSB - MSB of logical sector #
                 *                                 R$U= LSW of logical sector #
                 */
                    *breg = 0xff;
                    if (pd==0) break;
                    *breg  = fdinfo(pmem(xreg),yreg,xreg*0x10000+ureg,pd);
                    break;
                default:
                *breg = 0xff;
            }
            break;
          }

        /*
        * I$SetStat Entry Point
        *
        * Entry:
        *
        * Exit:
        *
        *
        * Error: CC Carry set
        *        B = errcode
        */
        case 0xdd:
            switch (*breg) {
                case 0: // SS.Opt
                case 02: // SS.SIZ
                case 15: // SS.FD
                case 0x11: // SS.Lock
                case 0x10: // SS.Ticks
                case 0x20: // SS.RsBit
                case 0x1c: // SS.Attr
                default: 
                    *breg = 0xff;
            }
            break;
    }
}