1
0
Files
2022-09-29 17:59:04 +03:00

460 lines
9.2 KiB
C

/* [posix] tar pseudo-filesystem for SGI sash + PROM.
*/
#ident "$Revision: 1.31 $"
#include <arcs/types.h>
#include <arcs/errno.h>
#include <arcs/io.h>
#include <arcs/dirent.h>
#include <sys/param.h>
#include <saio.h>
#include <tar.h>
#include <libsc.h>
#include <libsk.h>
struct tardata {
char _pthbuf[TARBSIZE+1]; /* single block for headers */
int _fsize; /* file size in bytes */
int _fblkno; /* file starting block no */
};
#define TARDATA ((struct tardata *)(fsb->FsPtr))
#define fsize TARDATA->_fsize
#define fblkno TARDATA->_fblkno
#define pthbuf TARDATA->_pthbuf
#define blkrnd(n) (((n)+(TARBSIZE-1))/TARBSIZE)
#define TAR_LIST 0x0001
#define TAR_OPEN 0x0002
static int tar_walk(FSBLOCK *,char *,int,int);
static int atoo(char *,int);
int tar_install(void);
static int _tar_strat(FSBLOCK *);
static int _tar_checkfs(FSBLOCK *);
static int _tar_open(FSBLOCK *);
static int _tar_close(FSBLOCK *);
static int _tar_read(FSBLOCK *);
static int _tar_getdirent(FSBLOCK *);
static int _tar_grs(FSBLOCK *);
/*
* tar_install - register this filesystem to the standalone kernel
* and initialize any variables
*/
int
tar_install(void)
{
return fs_register (_tar_strat, "tar", DTFS_TAR);
}
/*
* _tar_strat -
*/
static int
_tar_strat(FSBLOCK *fsb)
{
/* don't support 64 bit offset (yet?) */
if (fsb->IO->Offset.hi)
return fsb->IO->ErrorNumber = EINVAL;
switch (fsb->FunctionCode) {
case FS_CHECK:
return _tar_checkfs(fsb);
case FS_OPEN:
return _tar_open(fsb);
case FS_CLOSE:
return _tar_close(fsb);
case FS_READ:
return _tar_read(fsb);
case FS_WRITE:
return fsb->IO->ErrorNumber = EROFS;
case FS_GETDIRENT:
return _tar_getdirent(fsb);
case FS_GETREADSTATUS:
return _tar_grs(fsb);
default:
return fsb->IO->ErrorNumber = ENXIO;
}
}
static int
_tar_grs(FSBLOCK *fsb)
{
IOBLOCK *iob = fsb->IO;
/* If at or past EOF, read will return 0 bytes (tar never blocks) */
if (iob->Offset.lo >= fsize)
return(iob->ErrorNumber = EAGAIN);
iob->Count = fsize - iob->Offset.lo;
return(ESUCCESS);
}
static int
is_tar(FSBLOCK *fsb)
{
pth_t *pth = (pth_t *) pthbuf;
int i,size,cs,ecs,posix;
char *p;
/* Based on magic, use tar or posix tar. */
if (strncmp(pth->pt_magic,TMAGIC,TMAGLEN)) {
/* standard tar */
size = THSZ;
posix = 0;
}
else {
/* Posix tar */
size = PTHSZ;
posix = 1;
}
/* checksumming */
ecs = atoo(pth->pt_chksum,8);
bcopy(" ",pth->pt_chksum,8);
for (i=cs=0, p=(char *)pth ; i < size ; i++, cs += *p++) ;
if (cs != ecs)
return(0);
/* kludge to pass back the type header we have */
pth->pt_chksum[0] = posix;
return(1); /* found a tar header! */
}
static int
_tar_checkfs(FSBLOCK *fsb)
{
IOBLOCK *iob = fsb->IO;
if (!(fsb->FsPtr=(void *)dmabuf_malloc(sizeof(struct tardata))))
return(iob->ErrorNumber = ENOMEM);
iob->Address = pthbuf;
iob->Count = TARBSIZE;
iob->StartBlock = 0;
if (DEVREAD(fsb) != TARBSIZE) {
return(iob->ErrorNumber = EIO);
}
if (!is_tar(fsb)) {
return(iob->ErrorNumber = EINVAL);
}
return(ESUCCESS);
}
static int
_tar_open(FSBLOCK *fsb)
{
int rc=0;
if (fsb->IO->Flags & F_WRITE)
rc = EROFS;
else if (TARDATA == NULL) /* this shouldnt happen */
rc = EIO;
else {
/* try to open */
fsb->Buffer = NULL;
fsb->IO->StartBlock = 0;
if (fsb->IO->Flags & F_DIR) {
/* Only support pseudo-root of "/" */
if (!strcmp(fsb->Filename,"/"))
return(ESUCCESS);
else
rc = ENOTDIR;
}
else {
if (tar_walk(fsb,(char *)fsb->Filename,0,TAR_OPEN)) {
dmabuf_free(TARDATA);
return(fsb->IO->ErrorNumber);
}
return(ESUCCESS);
}
}
/* return error code */
return(fsb->IO->ErrorNumber = rc);
}
static int
_tar_close(FSBLOCK *fsb)
{
if (fsb->Buffer != NULL) _free_iobbuf(fsb->Buffer);
dmabuf_free(TARDATA);
return(ESUCCESS);
}
static int
_tar_read(FSBLOCK *fsb)
{
IOBLOCK *iob = fsb->IO;
char *buf = (char *)iob->Address;
int cnt = iob->Count;
int lcnt,scnt=cnt,rc=0;
/* make sure we have a open file */
if (!fsize) {
rc = EBADF;
goto rerr;
}
/* make sure read doesn't go past EOF */
if ((iob->Offset.lo + scnt) > fsize) {
cnt = scnt = fsize - iob->Offset.lo;
if (scnt <= 0) return(iob->Count = 0);
}
/* check for a partial read */
if ((scnt < TARBSIZE) || (iob->Offset.lo % TARBSIZE)) {
tail:
lcnt = _min(TARBSIZE - (iob->Offset.lo % TARBSIZE), scnt);
iob->StartBlock = (iob->Offset.lo / TARBSIZE) + fblkno;
iob->Address = pthbuf;
iob->Count = TARBSIZE;
if (DEVREAD(fsb) != iob->Count) {
rc = iob->ErrorNumber;
goto rerr;
}
bcopy(pthbuf+(iob->Offset.lo%TARBSIZE),buf,lcnt);
buf += lcnt;
iob->Offset.lo += lcnt;
scnt -= lcnt;
}
/* read all full blocks */
if (scnt / TARBSIZE) {
/* set-up read blocked read */
lcnt = scnt & ~(TARBSIZE-1);
iob->StartBlock = (iob->Offset.lo / TARBSIZE) + fblkno;
iob->Address = buf;
iob->Count = lcnt;
/* read data */
rc = DEVREAD(fsb);
if (rc == -1) {
rc = iob->ErrorNumber;
goto rerr;
}
if (rc < lcnt) {
rc = EIO;
goto rerr;
}
/* update offsets */
buf += lcnt;
iob->Offset.lo += lcnt;
iob->StartBlock += lcnt/TARBSIZE;
scnt -= lcnt;
}
if (scnt)
goto tail;
iob->Count = cnt;
return(ESUCCESS);
rerr:
return(iob->ErrorNumber = rc);
}
/* Ascii to octal. Make sure number has a leading 0 to force octal, which
* happens on old style tar, and potentially with large values.
*/
static int
atoo(char *str, int max)
{
char tmp[16], *p;
int rc;
tmp[0] = '0';
strncpy(tmp+1,str,max);
*(tmp+1+max) = '\0';
for (p=tmp+1; *p == ' '; *p = '0', p++) ;
(void) atob(tmp,&rc);
return(rc);
}
static int
_tar_getdirent(FSBLOCK *fsb)
{
int rc;
if (fsb->IO->Count == 0)
return(fsb->IO->ErrorNumber = EINVAL);
/* If Offset is 0, then reset start block to 0 to "rewinddir()".
*/
if (fsb->IO->Offset.lo == 0 && fsb->IO->Offset.hi == 0)
fsb->IO->StartBlock = 0;
rc = tar_walk(fsb,(char *)fsb->IO->Address,fsb->IO->Count,TAR_LIST);
fsb->IO->Offset.lo++; /* one directory entry farther */
if (rc > 0) {
fsb->IO->Count = rc;
return(ESUCCESS);
}
/* ARCS specifies ENOTDIR when there are no more entries to be read.
*/
if (rc == 0)
fsb->IO->ErrorNumber = ENOTDIR;
return(fsb->IO->ErrorNumber);
}
static int
tar_walk(FSBLOCK *fsb,char *file,int count,int type)
{
pth_t *pth = (pth_t *)pthbuf;
int i,posix,size,rc=0;
IOBLOCK *iob = fsb->IO;
char *p;
/* scan tar archive */
for (;;) {
/* read a block */
iob->Address = (char *)pth;
iob->Count = TARBSIZE;
if ((size=DEVREAD(fsb)) != TARBSIZE) {
if (!size) /* hit EOF before null block */
return(0);
/* read error from driver */
rc = iob->ErrorNumber;
goto bad;
}
iob->StartBlock++;
/* dig through the header */
/* Look for a null terminating block. The Posix
* spec specifies two null blocks, but if the name
* of this header block is "", then we have an error.
*/
if (!pth->pt_name[0]) {
register long *p = (long *)pth;
for (i=0; !(*p) && (i < (PTHSZ/sizeof(long))) ;
i++,p++) ;
/* We are at the end of the archive and we
* didn't find our file -> open failed, or
* getdents done.
*/
if (i == (PTHSZ/sizeof(long))) {
if (type&TAR_LIST)
return(0);
rc = ENOENT;
goto bad;
}
else {
/* mangled header */
rc = EIO;
goto bad;
}
}
/* calc checksums */
if (!is_tar(fsb)) {
/* incorrect header checksum */
rc = EIO;
goto bad;
}
posix = pth->pt_chksum[0];
/* save size of this file */
size = atoo(pth->pt_size,12);
/* print out file info */
if (type & TAR_LIST) {
DIRECTORYENTRY *d = (DIRECTORYENTRY *)file;
int i=0,max;
CHAR *p;
d->FileAttribute = ReadOnlyFile;
if (pth->pt_typeflag == DIRTYPE)
d->FileAttribute |= DirectoryFile;
/* how much space can we use? */
max = FileNameLengthMax + ((count-1) *
sizeof(DIRECTORYENTRY)) - 1;
p = d->FileName;
if (posix && pth->pt_prefix[0]) {
pth->pt_prefix[PREFSIZE-1] = '\0';
i = strlen(pth->pt_prefix);
i = (i < max-1) ? i : max-1;
strncpy(p,pth->pt_prefix,i);
*(p += i) = '/';
p++;
max -= i;
}
pth->pt_name[NAMESIZE-1] = '\0'; /* kils pt_mode! */
i = strlen(pth->pt_name)+1;
i = (i < max) ? i : max;
strncpy(p,pth->pt_name,i);
p[i] = '\0';
if ((d->FileNameLength=strlen(p)) > FileNameLengthMax)
d->FileNameLength = FileNameLengthMax;
}
if (type & TAR_OPEN) {
/* Check if this is our man */
p = file;
if (posix && pth->pt_prefix[0]) {
int n;
pth->pt_prefix[PREFSIZE-1] = '\0';
n = strlen(pth->pt_prefix);
if (!strncmp(p,pth->pt_prefix,n) &&
(file[n] == '/'))
p = &file[n+1];
else
p = &file[1]; /* force failure */
}
if (!strncmp(p,pth->pt_name,NAMESIZE)) {
/* lastly make sure it's a regular file */
if ((pth->pt_typeflag != REGTYPE) &&
(pth->pt_typeflag != AREGTYPE)) {
/* not a regular file */
rc = EIO;
goto bad;
}
if (!size) {
/* zero byte file */
rc = EIO;
goto bad;
}
/* Yep, we're home. Save the block number,
* size and then allocate a bio buf.
*/
fblkno = iob->StartBlock;
fsize = size;
fsb->Buffer = (CHAR *)_get_iobbuf();
return(0);
}
}
/* "seek" to bn of next tar header */
iob->StartBlock += blkrnd(size);
if (type & TAR_LIST)
return(1);
}
bad:
iob->ErrorNumber = rc;
return(-1);
}