home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GEMini Atari
/
GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso
/
files
/
mint
/
mfs055
/
minixfs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-08-03
|
74KB
|
3,012 lines
/* This is an attempt to add minix filesystem support to MiNT ... use
* at your own risk!
*/
/* 'Minixfs' Copyright S.N.Henson. See 'copying' file for more info.
* Version 0.55.
* Revision history .....
* 0.0 Single-partition read-only version with no filename conversion.
* 0.1 Added multi-partition support and filename conversion for TOS
* domain ... added cache.
* 0.2 Added write support 90% of TOS functions emulated. No write
* cache yet ... and no access checking.
* 0.25 Fixed m_seek bug.
* 0.3 Added write cache and remaining tos functions fixed some bugs
* when long files were read/written. Made search_dir more
* tos-like.
* 0.31 Fixed some bugs . Made it permissible to delete an open file
* (just like unix). Added full-disk error checks and minor code
* tidy up.
* 0.32 Added 'zlast' to speed up free zone searches. Also added
* consecutive zone read/write to speed up i/o . Fixed bugs
* all over the place with curdir .
* 0.33 Fixed code for > 512 byte sectors. Merged add_zone and
* find_zone, new code should support sparse files .
* 0.34 Tentative support for floppies and removeable media . Tidied up
* and removed redundant code (like minix_init() ) .
* 0.35 Added access checking to all relevant functions.
* 0.40 Added some un*x like inode related functions , fixed up m_rename
* to be more robust (and related things in find_path). Fixed up
* m_creat and merged zap_inode() with new function trunc_inode().
* Not to mention fixing the occasional bug (or two).
* 0.41 Cleared up a few bugs , separated system and user caches.
* 0.50 Changed stuff all over the place for new fs code in 0.9 and
* addition of file sharing code, hard and symbolic links (yeah!).
* 0.51 New compilation option TOS_LWR and tosify filenames from readdir
* only if in compatability mode (and all domains).
* 0.511 Nasty bug fixed in find_zone which screwed files bigger than 1Mb.
* 0.52 Added V2 filesystem support . Extended RWABS now used , as required.
* Code changed all over the place , new alerts added if some problems
* are noticed in the filesystem.
* 0.53 Added code to handle large directory entries.
* 0.54 Overhaul of tosify code , translation now selectable by fscntl.
* 0.541 Fixed bug which crashed v2 filesystems.
* 0.55 Added ROBUST compilation option and file locking.
* .........................................................................
* Note : 0.31 was distributed with MiNT 0.7
* 0.40 with MiNT 0.8
*
* .........................................................................
* TODO : (No idea when, and in no particular order)
* Add some more Un*x features , directory setu/gid ,
* stickies and compaction
* Make routines more robust to disk i/o errors.
* Tidy up code !
* Support for special files .
* Dare I mention it .... Make it multi-thread .
*/
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include "atarierr.h"
#include "filesys.h"
#include "minixfs.h"
#include "mfsproto.h"
#define DOM_TOS 0
#define DOM_MINT 1
#define NUM_DRIVES 32
#ifndef NULL
#define NULL 0L
#endif
/* Useful macro , is non zero only if 'x' is not a power of two */
#define NPOW2(x) ( ( (x) & (x-1) )!=0)
/* Dates smaller than 1st Jan 1980 confuse dostime _corr avoids them */
#define _corr(t) ( (t > 315532800L) ? t : 315532800L)
FILESYS minix_filesys = {
(FILESYS *) 0,
FS_CASESENSITIVE,
m_root, m_lookup, m_creat, m_getdev,
m_getxattr, m_chattr, m_chown,
m_chmode, m_mkdir,
m_rmdir, m_remove, m_getname,
m_rename, m_opendir, m_readdir,
m_rewinddir, m_closedir,
m_pathconf, m_dfree,
m_wlabel, m_rlabel,
m_symlink, m_readlink,
m_hardlink, m_fscntl, m_dskchng
};
DEVDRV minix_dev = {
m_open, m_write, m_read,
m_seek, m_ioctl, m_datime,
m_close,m_select, m_unselect
};
extern FILESYS tos_filesys;
static bufr temp;
super_info *super_ptr[NUM_DRIVES]; /* Pointer to super block for each drive */
#define MINIXDRIVE(i) (super_ptr[i] != 0)
char d_check=1; /* Enable / disable device checking */
char lrecno; /* set to '1' if RWABS permits 'lrecno' parameter */
char checklrecno; /* Set to 1 if lrecno checked for */
char lockok; /* Set to 1 if locking calls allowed */
FILEPTR *firstptr; /* First FILEPTR in chained list */
long trans_mode=TRANS_DEFAULT; /* Translation mode */
/* mini-cache for m_getname */
char *lpath;
fcookie lroot,ldir;
super_info sblks[NUM_MINIX];
cache_control syscache,usrcache;
/*
* this points to the structure that has all the useful functions that
* the kernel told us about
*/
struct kerinfo *kernel;
#define CCONWS (void)(*kernel->dos_tab[0x09])
#define RWABS (*kernel->bios_tab[4])
#define GETBPB (void *)(*kernel->bios_tab[7])
#define Timestamp (*kernel->dos_tab[0x2c])
#define Datestamp (*kernel->dos_tab[0x2a])
#define Domain() (*kernel->dos_tab[0x119])(-1)
#define Getpid (*kernel->dos_tab[0x10b])
#define Getuid (*kernel->dos_tab[0x10f])
#define Getgid (*kernel->dos_tab[0x114])
#define DEBUG (*kernel->debug)
#define ALERT (*kernel->alert)
#define TRACE (*kernel->trace)
#define FATAL (*kernel->fatal)
#define Kmalloc (*kernel->kmalloc)
#define Kfree (*kernel->kfree)
#define Stricmp (*kernel->stricmp)
#define Strlwr (*kernel->strlwr)
#define Unixtime (*kernel->unixtim)
#define Dostime (*kernel->dostim)
#define Denyshare (*kernel->denyshare)
#define Denylock (*kernel->denylock)
#define Changedrive (*kernel->drvchng)
#ifdef NDEBUG
#define assert(expression)
#else
# ifdef __STDC__
#define assert(expression) \
((expression) ? 0 : FATAL("assert(`%s') failed at line %ld of %s.", \
#expression, (long)__LINE__, __FILE__))
# else
#define assert(expression) if(expression) FATAL("assert(%s) failed", \
"expression")
# endif
#endif
/*
* this must be the first function; it is called by the kernel when the
* file system is being loaded, and should return the file system
* structure
*/
FILESYS *
minix_init(k)
struct kerinfo *k;
{
kernel = k;
CCONWS("Minix file system driver for MiNT. Version 0.55 Copyright \
1991,1992 S.N.Henson\r\n");
if( (k->maj_version > 0) || (k->min_version >= 94) ) lockok=1;
else CCONWS("File Locking not Installed , Use MiNT 0.94 or newer\r\n");
return &minix_filesys;
}
int
minix_sanity(drv)
int drv;
{
int i;
d_inode rip;
int *bpb;
int scsiz;
super_info *psblk;
for(i=0;i<NUM_MINIX && sblks[i].sblk.s_ninodes;i++)
;
if(i==NUM_MINIX) {
DEBUG("No room for more Minix Partitions");
return(0);
}
psblk=&sblks[i];
bpb=GETBPB(drv);
if (!bpb) {
DEBUG("Getbpb failed!");
return(0);
}
scsiz=bpb[0]/512;
if(bpb[0]%512 || (scsiz!=1 && scsiz!=2)){
DEBUG("Partition %c skipped : unsupported sector size",drv+'A');
return(0);
}
(void)RWABS(2,&temp,2/scsiz,(SUPER_BLOCK<<1)/scsiz,drv);
psblk->sblk=*((super_block *)&temp);
if(( (psblk->sblk.s_magic==SUPER_MAGIC) ||
(psblk->sblk.s_magic==SUPER_V2))
&& psblk->sblk.s_ninodes) {
if(psblk->sblk.s_log_zsize) {
DEBUG("Cannot read partition %c Zone-size>Block-size",drv+'A');
psblk->sblk.s_ninodes=0;
return 0;
}
if(psblk->sblk.s_magic==SUPER_MAGIC)
{
TRACE("Drive %c V1 filesyetem",drv+'A');
psblk->version=0;
psblk->ipb=INODES_PER_BLOCK;
psblk->zpind=NR_INDIRECTS;
psblk->sblk.s_zones=psblk->sblk.s_nzones;
psblk->dzpi=NR_DZONE_NUM;
psblk->ndbl=NR_DBL;
}
else
{
TRACE("Drive %c V2 filesystem",drv+'A');
psblk->version=1;
psblk->ipb=INODES_PER_BLOCK2;
psblk->zpind=NR_INDIRECTS2;
psblk->dzpi=NR_DZONE_NUM2;
psblk->ndbl=NR_DBL2;
}
/* Does the partition need lrecno ? */
#ifdef LRDEBUG
if(drv > 1 )
#else
if( ( (psblk->sblk.s_zones > 32767) && scsiz==1)
|| psblk->sblk.s_zones > 65534 )
#endif
{
if(!lrecno)
{
if(checklrecno || nolrecno(temp.bdata,scsiz,drv) )
{
DEBUG("Partition %c too big",drv+'A');
psblk->sblk.s_ninodes=0;
return 0;
}
}
psblk->big=1;
}
else psblk->big=0;
super_ptr[drv]=psblk; /* Point drive at super block */
psblk->ioff = psblk->sblk.s_imap_blks + psblk->sblk.s_zmap_blks
+ 2 ;
psblk->scsiz=scsiz;
/* If we get this far allocate a cache if non-present */
if(!syscache.start && init_cache()) {
DEBUG("No room for Cache");
return(0);
}
read_inode(ROOT_INODE,&rip,drv);
if(IS_DIR(rip)) {
void *p;
int dot=-1,dotdot=-1;
p=Kmalloc((unsigned) (BLOCK_SIZE*(psblk->sblk.s_imap_blks+
psblk->sblk.s_zmap_blks)));
if(!p) {
DEBUG("No room for bitmaps");
return(0);
}
psblk->ibitmap=p;
psblk->zbitmap=p+BLOCK_SIZE*psblk->sblk.s_imap_blks;
crwabs(2,p,psblk->sblk.s_imap_blks+psblk->sblk.s_zmap_blks
,2,drv);
psblk->idirty=0;
psblk->zdirty=0;
psblk->zlast=0;
psblk->dev=drv;
/* Final step , read in the root directory zone 1 and check the '.' and '..'
* spacing , The spacing between the '.' and '..' will be used as an indicator
* of the directory entry size. If in doubt assume a normal minix filesystem.
*/
read_zone(rip.i_zone[0],&temp,drv,&syscache);
for(i=0;i<min(NR_DIR_ENTRIES,rip.i_size/DIR_ENTRY_SIZE);i++)
{
if(temp.bdir[i].d_inum)
{
if(!strcmp(temp.bdir[i].d_name,"."))
{
if(dot==-1) dot=i;
else
{
ALERT("Drive %c multiple ""."" in root dir!!",drv+'A');
dot=-1;
i=NR_DIR_ENTRIES;
}
}
if(!strcmp(temp.bdir[i].d_name,".."))
{
if(dotdot==-1) dotdot=i;
else
{
ALERT("Drive %c multiple "".."" in root directory",drv+'A');
dotdot=-1;
i=NR_DIR_ENTRIES;
}
}
}
}
if( (dotdot==-1) || (dot==-1) )
{
ALERT("Drive %c no . or .. in root directory",drv+'A');
psblk->increment=1;
}
else psblk->increment=dotdot-dot;
if( (psblk->increment < 1)
|| NPOW2(psblk->increment)
|| (psblk->increment > MAX_INCREMENT ))
{
ALERT("Drive %c weird . .. positions",drv+'A');
psblk->increment=1;
}
#ifdef BIGDIRTEST
ALERT("Drive %c increment %d",drv+'A',psblk->increment);
#endif
return(1);
}
else {
DEBUG("root inode on drive %c is not a directory??",
drv+'A');
}
}
psblk->sblk.s_ninodes=0;
return(0);
}
/* This function returns '1' if the RWABS present does not support the extra
* long parameter used for large partitions or if the drive is floppy , it
* sets 'checklrecno' if this test has been performed, and 'lrecno' if this
* extra parameter is supported , what it does is to read in the super blocks
* block using the extra parameter and checks it with the one already read.
*/
int nolrecno(cmptest2,scsiz,drv)
char *cmptest2;
int scsiz;
int drv;
{
char *cmptest;
if(drv < 2) return 1;
if(cmptest=Kmalloc(BLOCK_SIZE))
{
if(!RWABS(2,cmptest,2/scsiz,-1,drv,
(long)((SUPER_BLOCK<<1)/scsiz)))
{
if(!bcmp(cmptest,cmptest2,BLOCK_SIZE))
{
lrecno=1;
#ifdef LRDEBUG
ALERT("lrecno supported");
#else
TRACE("lrecno supported");
#endif
}
}
Kfree(cmptest);
}
else DEBUG("minixfs nolrecno:Kmalloc failed");
checklrecno++;
if(lrecno) return 0;
return 1;
}
/*
* the kernel calls this when it detects a disk change
*/
static long m_dskchng(d)
int d;
{
TRACE("Disk Change drive %c",d+'A');
Kfree(super_ptr[d]->ibitmap);
super_ptr[d]->sblk.s_ninodes=0;
/* this may affect the m_getname cache, too */
if (lpath && (d == lroot.dev || d == ldir.dev)) {
Kfree(lpath);
lpath = 0;
}
/* Since the disk has changed always invalidate cache */
m_invalidate(d);
/* If new drive not minix update super block pointer */
if (!minix_sanity(d)) super_ptr[d] = 0;
return 1;
}
/* Invalidate all cache entries for a given drive */
void m_invalidate(drv)
int drv;
{
char warned=0;
cache *p;
for(p=syscache.start;p!=usrcache.end;p++)
if(p->drive==drv && p->status)
{
if(p->status==2 && !warned++)
ALERT("Cache entry not written out when drive %c invalidated",drv+'A');
p->status=0;
}
}
/* Zone and inode number range checkers , print out 'mesg' and the
* drive,zone if invalid
*/
int chk_zone(num,drv,mesg)
long num;
int drv;
char *mesg;
{
super_info *psblk=super_ptr[drv];
if(!num) return 0; /* special for zeroed out block */
if(num < psblk->sblk.s_firstdatazn || num >= psblk->sblk.s_zones)
{
ALERT("%s : Illegal zone , drive %c zone %ld",mesg,drv+'A',num);
return 1;
}
return 0;
}
int chk_inode(num,psblk,mesg)
super_info *psblk;
long num;
char *mesg;
{
if(num < 1 || num > psblk->sblk.s_ninodes)
{
ALERT("%s : Illegal inode number , drive %c inode %ld",mesg,
psblk->dev+'A',num);
return 1;
}
return 0;
}
/* This is a 'corrected rwabs' , it behaves as though sector-size==1K
* irrespective of what it really is. Currently it only supports 512 byte
* and 1K sectors , also if the extended rwabs is needed use it .
*/
void crwabs(rw,buf,num,recno,dev)
int rw;
void *buf;
unsigned num;
long recno;
int dev;
{
long r;
super_info *psblk=super_ptr[dev];
/* If something *really* bad happened forbid overwrite of
* boot sector and super block
*/
if(rw && recno < 2)
{
ALERT("Crwabs, Illegal Write: drive %c sector %ld",dev+'A',recno);
return ;
}
if(d_check && !MINIXDRIVE(dev)) {
ALERT("Minixfs : attempted I/O with illegal partition %d!",
dev);
ALERT("Minixfs : please submit a bug report");
return ;
}
if(psblk->scsiz==1)
{
if(psblk->big) r = RWABS(rw,buf,num<<1,-1,dev,recno<<1);
else r = RWABS(rw,buf,num<<1,(unsigned)(recno<<1),dev);
}
else
{
if(psblk->big) r = RWABS(rw,buf,num<<1,-1,dev,recno);
r = RWABS(rw,buf,num,(unsigned)recno,dev);
}
if (r) ALERT("minixfs: Rwabs returned %ld block %ld drive %d"
, r,recno,dev);
return ;
}
/* For this kind of file system a cache is absolutely essential ,
without it your hard-disk will sound like a buzz-saw .... the
idea is fairly simple , for every block requested if it is not in
the cache load it in at the current position .Then return a
pointer to the block . Additionally all writes go to the cache if
an entry exists for the specified block . This means that when a
cache entry is overwritten we must write out the entry if it is
'dirty' , the function l_sync() writes out the entire cache along
with the zone and inode bitmaps. All functions , except write , call
l_sync() on each call . Write calls l_sync() only after a certain
amount of data has been written , this stops lots of little writes
taking too much time .... However for an increase in performance ,
two caches are used , one for inodes and directories , one for
files , these are kmalloc()'ed at the same time so that one follows the
other in memory , this simplifies cache-flushing for example.
*/
/* Initialise cache */
int init_cache()
{
cache *ctemp;
/* Start with system cache */
if(!(ctemp=Kmalloc((SCACHE_SIZE+UCACHE_SIZE)*SIZEOF(cache))))
return 1;
syscache.start=ctemp;
syscache.end=&ctemp[SCACHE_SIZE];
syscache.pos=syscache.start;
usrcache.start=syscache.end;
usrcache.pos=usrcache.start;
usrcache.end=&usrcache.start[UCACHE_SIZE];
/* Invalidate all entries */
for(ctemp=syscache.start;ctemp!=usrcache.end;ctemp++)
ctemp->status=0;
return 0;
}
void
l_sync()
{
cache *p;
int i;
/* Write out dirty cache entries */
for(p=syscache.start;p!=usrcache.end;p++)
{
if(p->status==2)
{
crwabs(3,&p->buffer,1,p->block,p->drive);
p->status=1;
}
}
/* Now inode and zone bitmaps */
for(i=0;i<NUM_DRIVES;i++)
{
if(MINIXDRIVE(i)) {
super_info *psblk;
psblk=super_ptr[i];
if(psblk->idirty)
crwabs(3,psblk->ibitmap,psblk->sblk.s_imap_blks,2,i);
if(psblk->zdirty)
crwabs(3,psblk->zbitmap,psblk->sblk.s_zmap_blks,
psblk->sblk.s_imap_blks+2,i);
psblk->idirty=0;
psblk->zdirty=0;
}
}
}
/* Return cache entry for numr,drive if it exists or NULL */
cache *in_cache(numr,drive,control)
long numr;
int drive;
cache_control *control;
{
cache *p;
for(p=control->start;p!=control->end;p++)
if((p->status) && (p->block==numr) && (p->drive==drive))
return(p);
return NULL;
}
/* Return a pointer to block numr,drive loading and updating cache if needed */
bufr *cget_block(numr,drive,control)
long numr;
int drive;
cache_control * control;
{
return(&(cache_get(numr,drive,control)->buffer));
}
cache *
cache_get(numr,drive,control)
long numr;
int drive;
cache_control *control;
{
cache *p;
if(p=in_cache(numr,drive,control))return(p);
/* Read block in */
if(control->pos==control->end)
control->pos=control->start;
/* Write out dirty entries before they are overwritten */
if(control->pos->status==2)
crwabs(3,&control->pos->buffer,1,control->pos->block,
control->pos->drive);
crwabs(2,&control->pos->buffer,1,numr,drive);
/* Update Cache */
control->pos->drive=drive;
control->pos->block=numr;
control->pos->status=1;
return(control->pos++);
}
/* Write out block, search cache and if 'hit' update and mark as dirty
* otherwise just write out block .
*/
int
cput_block(numr,drive,buf,control)
long numr;
int drive;
void *buf;
cache_control *control;
{
cache *p;
if(p=in_cache(numr,drive,control)){
if(buf!=&p->buffer) bcopy(buf,&p->buffer,BLOCK_SIZE);
p->status=2;
return 0;
}
crwabs(3,buf,1,numr,drive);
return 0;
}
int read_inode(num,rip,drv)
unsigned num;
d_inode *rip;
int drv;
{
bufr *tmp;
super_info *psblk=super_ptr[drv];
if(chk_inode(num,psblk,"read_inode")) return 1;
num-=1;
tmp=cget_block(num/psblk->ipb+psblk->ioff,drv,&syscache);
if(psblk->version) *rip=tmp->binode[num%psblk->ipb];
else
{
d_inode1 *oldrip;
int i;
oldrip=&tmp->binode1[num%psblk->ipb];
/* Convert V1 inode to V2 */
rip->i_mode=oldrip->i_mode;
rip->i_nlinks=oldrip->i_nlinks;
rip->i_uid=oldrip->i_uid;
rip->i_gid=oldrip->i_gid;
rip->i_size=oldrip->i_size;
rip->i_atime=oldrip->i_mtime;
rip->i_mtime=oldrip->i_mtime;
rip->i_ctime=oldrip->i_mtime;
for(i=0;i< NR_ZONE_NUMS;i++) rip->i_zone[i]=oldrip->i_zone[i];
rip->i_zone[NR_ZONE_NUMS]=0;
}
return(0);
}
int write_inode(num,rip,drv)
unsigned num;
d_inode *rip;
int drv;
{
cache *tmp;
super_info *psblk=super_ptr[drv];
if(chk_inode(num,psblk,"write_inode")) return 1;
num-=1;
tmp=cache_get(num/psblk->ipb+psblk->ioff,drv,&syscache);
if(psblk->version) tmp->buffer.binode[num%psblk->ipb]=*rip;
else
{
d_inode1 *oldrip;
int i;
oldrip=&tmp->buffer.binode1[num%psblk->ipb];
/* Convert V2 inode to V1 */
oldrip->i_mode=rip->i_mode;
oldrip->i_nlinks=rip->i_nlinks;
oldrip->i_uid=rip->i_uid;
oldrip->i_gid=rip->i_gid;
oldrip->i_size=rip->i_size;
oldrip->i_mtime=rip->i_mtime;
for(i=0;i<NR_ZONE_NUMS;i++) oldrip->i_zone[i]=rip->i_zone[i];
}
tmp->status=2;
return(0);
}
int read_zone(num,buf,drive,control)
long num ;
void *buf;
int drive;
cache_control *control;
{
if(chk_zone(num,drive,"read_zone")) return 1;
if(num) bcopy(cget_block(num,drive,control),buf,(size_t)BLOCK_SIZE);
else bzero(buf,(size_t)BLOCK_SIZE);
return(0);
}
/* Only ever used for directories so always syscache */
bufr *get_zone(num,drive)
long num ;
int drive;
{
if(chk_zone(num,drive,"get_zone")) FATAL("Halted");
return(cget_block(num,drive,&syscache));
}
int write_zone(num,buf,drive,control)
long num ;
void *buf;
int drive;
cache_control *control;
{
if(chk_zone(num,drive,"write_zone")) return 1;
cput_block(num,drive,buf,control);
return(0);
}
/* This is a 'clever' write_zone which recognises consecutive blocks and
* queues requests until it gets one out of sequence.This allows large
* i/o requests to be done with a single Rwabs for consecutive blocks
* which is much faster than lots of little ones.
*/
int write_zones(num,buf,drive,control)
long num;
void *buf;
int drive;
cache_control *control;
{
static void *qstart,*qnext;
static zone_nr nstart,nnext;
static zone_nr count;
static short qdrive=-1;
cache *p;
/* Update cache if necessary */
if(chk_zone(num,drive,"write_zones")) return 1;
if(p=in_cache(num,drive,control))
{
bcopy(buf,&p->buffer,(size_t)BLOCK_SIZE);
p->status=1;
}
if(buf!=qnext || nnext!=num || qdrive!=drive)/* Flush out queue */
{
if(qdrive!=-1)crwabs(3,qstart,count,nstart,qdrive);
qdrive=drive;
qstart=buf;
qnext=buf+BLOCK_SIZE;
nstart=num;
nnext=num+1;
count=1;
}
else
{
qnext+=BLOCK_SIZE;
count++;
nnext++;
}
return 0;
}
/* This is an equivalent for read ... but this is a bit harder as it is
* not obvious what to do with the cache . What we finally do is to
* always get data from the cache if we can , though this could easily
* turn a large consecutive i/o request into lots of little ones . The
* cache is not filled from the new read unless we are only reading
* 1 zone ... basically this assumes that if the user reads several zones
* then the program will be doing doing some sort of cacheing itself .
*/
int read_zones(num,buf,drive,control)
long num;
void *buf;
int drive;
cache_control *control;
{
static void *qstart,*qnext;
static zone_nr nstart,nnext;
static zone_nr count;
static short qdrive=-1;
cache *p;
if(chk_zone(num,drive,"read_zones")) return 1;
/* Read from cache if necessary */
if(p=in_cache(num,drive,control))
{
bcopy(&p->buffer,buf,(size_t)BLOCK_SIZE);
drive=-1; /* Force flush of queued entries */
}
if(buf!=qnext || nnext!=num || qdrive!=drive)/* Flush out queue */
{
if(qdrive!=-1)
{
if(count==1)read_zone(nstart,qstart,qdrive,control);
else {
if(nnext) crwabs(2,qstart,count,
nstart,qdrive);
else bzero(qstart,count*(size_t)BLOCK_SIZE);
}
}
qdrive=drive;
qstart=buf;
qnext=buf;
nstart=num;
nnext=num;
count=0;
}
if(qdrive!=-1)
{
qnext+=BLOCK_SIZE;
count++;
if(nnext)nnext++;
}
return 0;
}
/* This routine finds the zone 'numr' of an inode , traversing indirect
* and double indirect zones as required if flag!=0 zones are added as
* required . Having two filesystem versions makes this a bit trickier ...
*/
long find_zone(rip,numr,drive,flag)
d_inode *rip;
long numr;
int drive;
int flag;
{
long temp_zone,temp_zone2;
bufr *tmp,*tmp2;
super_info *psblk=super_ptr[drive];
char vers; /* Filesys version */
vers=psblk->version;
/* Past EOF ? */
if((numr*BLOCK_SIZE >= rip->i_size) && !flag ) return(0);
/* Zone in inode ? */
if(numr < psblk->dzpi)
{
temp_zone=rip->i_zone[numr];
if(temp_zone || !flag ) return temp_zone;
return(rip->i_zone[numr]=alloc_zone(drive));
}
/* In indirect zone then ? */
else if(numr < psblk->ndbl)
{
if(rip->i_zone[7])
{
tmp=get_zone(rip->i_zone[7],drive);
temp_zone=PIND(vers,tmp,numr-psblk->dzpi);
if( temp_zone || !flag )return temp_zone;
temp_zone=alloc_zone(drive);
if(temp_zone)
{
PIND(vers,tmp,numr-psblk->dzpi)=temp_zone;
write_zone(rip->i_zone[7],tmp,drive,&syscache);
}
return temp_zone;
}
else
{
if(!flag || !(rip->i_zone[7]=alloc_zone(drive))) return 0;
bzero(&temp,(size_t)BLOCK_SIZE);
temp_zone=alloc_zone(drive);
IND(vers,temp,numr-psblk->dzpi)=temp_zone;
write_zone(rip->i_zone[7],&temp,drive,&syscache);
return temp_zone;
}
}
else/* Erk double indirect .... */
{
if(rip->i_zone[8]) {
tmp=get_zone(rip->i_zone[8],drive);
temp_zone2=PIND(vers,tmp,(numr-psblk->ndbl)/psblk->zpind);
if(temp_zone2)
{
tmp2=get_zone(temp_zone2,drive);
temp_zone=PIND(vers,tmp2,(numr-psblk->ndbl)%psblk->zpind);
if(temp_zone || !flag)return temp_zone;
if(temp_zone=alloc_zone(drive))
{
PIND(vers,tmp2,(numr-psblk->ndbl)%psblk->zpind)=temp_zone;
write_zone(temp_zone2,tmp2,drive,&syscache);
}
return temp_zone;
}
else
{
if(!flag ||!(temp_zone2=alloc_zone(drive)) )return 0;
PIND(vers,tmp,(numr-psblk->ndbl)/psblk->zpind)=temp_zone2;
write_zone(rip->i_zone[8],tmp,drive,&syscache);
bzero(&temp,(size_t)BLOCK_SIZE);
temp_zone=alloc_zone(drive);
IND(vers,temp,(numr-psblk->ndbl)%psblk->zpind)=temp_zone;
write_zone(temp_zone2,&temp,drive,&syscache);
return temp_zone;
}
}
if(!flag || !(rip->i_zone[8]=alloc_zone(drive)) ) return 0;
bzero(&temp,(size_t)BLOCK_SIZE);
temp_zone=alloc_zone(drive);
if(temp_zone)
{
IND(vers,temp,(numr-psblk->ndbl)/psblk->zpind)=temp_zone;
write_zone(rip->i_zone[8],&temp,drive,&syscache);
}
else return 0;
bzero(&temp,(size_t)BLOCK_SIZE);
temp_zone2=alloc_zone(drive);
IND(vers,temp,(numr-psblk->ndbl)%psblk->zpind)=temp_zone2;
write_zone(temp_zone,&temp,drive,&syscache);
return temp_zone2;
}
}
/* This reads zone number 'numr' of an inode .
* It returns the actual number of valid characters in 'numr' , this is only
* used for directories so it is hard-coded for the system cache.
*/
int next_zone(rip,numr,buf,drive)
d_inode *rip;
long numr;
void *buf;
int drive;
{
long ztemp;
off_t ret;
ret=min(rip->i_size-numr*BLOCK_SIZE,BLOCK_SIZE);
if(ret <= 0)return 0;
ztemp=find_zone(rip,numr,drive,0);
read_zone(ztemp,buf,drive,&syscache);
return (int)ret;
}
/* As above but reads in cache pointer */
int cnext_zone(rip,numr,buf,drive)
d_inode *rip;
long numr;
bufr **buf;
int drive;
{
long ztemp;
off_t ret;
ret=min(rip->i_size-numr*BLOCK_SIZE,BLOCK_SIZE);
if(ret <= 0)return 0;
ztemp=find_zone(rip,numr,drive,0);
*buf=get_zone(ztemp,drive);
return (int)ret;
}
/* search_dir serves 3 functions dependent on 'mode'
0 Search a directory for the entry 'name' if found return its inode number.
1 Create an entry 'name' return position of d_inum .
2 Delete an entry 'name' return inode num for success .
3 Find entry 'name', return position of d_inum.
In all cases failure is denoted by a negative error number .
*/
off_t search_dir(name,inum,drive,flag)
char *name;
unsigned inum;
int drive;
int flag;
{
int entry,count;
long zone; /* Zone number within dir */
off_t lstfree; /* Last unused dir entry */
d_inode rip;
bufr *tmp;
int tflag;
int incr;
static char tname[MNAME_MAX];
tflag=do_trans(SRCH_TOS);
incr=super_ptr[drive]->increment;
strcpy(tname,tosify(name,tflag,MMAX_FNAME(incr)));
read_inode(inum,&rip,drive);
/* Must be a directory ! */
if(!IS_DIR(rip)) return EPTHNF;
lstfree=-1l;
for(zone=0; (count=cnext_zone(&rip,zone,&tmp,drive)/DIR_ENTRY_SIZE);
zone++)
{
for(entry=0;entry<count;entry+=incr) {
dir_struct *try=&tmp->bdir[entry];
mino_t inumtemp;
inumtemp=try->d_inum;
if(inumtemp &&
(!strncmp(name,try->d_name,MMAX_FNAME(incr)) || (tflag &&
(!strcmp(tname,tosify(try->d_name,tflag,MMAX_FNAME(incr))) ) )))
{
if(flag==KILL)
{
/* Never ever allow unlinking of '.' or '..' */
if(zone==0 && entry<2)return EACCDN;
try->d_inum=0;
/* Next bit pinched from Minix,
* store old inode num in last 2 bytes of name
* This allows recovery in case of accident
*/
*((mino_t *)&try->d_name[MMAX_FNAME(incr)-2])=inumtemp;
write_zone(find_zone(&rip,zone,drive,0),tmp
,drive,&syscache);
}
if(flag==ADD) return EACCDN;
if(flag==FIND || flag==KILL ) return inumtemp;
if(flag==POS) return entry*DIR_ENTRY_SIZE+zone*BLOCK_SIZE;
}
if(lstfree==-1l && !inumtemp)
lstfree=zone*BLOCK_SIZE+entry*DIR_ENTRY_SIZE;
}
}
if(flag==ADD) {
dir_struct add[MAX_INCREMENT];
strncpy(tname,name,MMAX_FNAME(incr) );
tname[MMAX_FNAME(incr)]=0;
if(badname(tname)) return EACCDN;
if( do_trans(LWR_TOS) ) Strlwr(tname);
strncpy(add[0].d_name,tname,MMAX_FNAME(incr) );
add[0].d_inum=0;
if(l_write(inum,lstfree,(off_t)(DIR_ENTRY_SIZE*incr),add,drive)
!=(DIR_ENTRY_SIZE*incr) ) return EACCDN;
return( lstfree==-1l ? rip.i_size : lstfree);
}
return EFILNF;
}
/* Return '1' is 'name' has any illegal charaters in it */
int badname(name)
char *name;
{
for(;*name;name++) if(BADCHR(*name))
{
DEBUG("minixfs: Bad character in filename");
return 1;
}
return 0;
}
/* New filesys routines for 0.9 , the 'cookie' for minix filesystems is very
simple , 'dev' is the Rwabs device number , 'index' is the inode number , in
addition the aux field is used when a fileptr is unlinked */
/*
* Note: in the first round of initialisations, we assume that floppy
* drives (A and B) don't belong to us; but in a later disk change,
* they may very well be ours, so we remember that
*/
static long m_root(dev,dir)
int dev;
fcookie *dir;
{
static first_init = 2;
int i;
long ret = -1;
/* the first 2 checks (on A: and B:) we fail automatically */
if (first_init) {
--first_init;
return ret;
}
d_check=0;
/* Check if drive already recognised */
for(i=0;i<NUM_MINIX;i++)
if(sblks[i].sblk.s_ninodes && sblks[i].dev==dev) break;
/* If not present , see if it's valid */
if( i < NUM_MINIX || ( dev >= 0 && minix_sanity(dev) ) ) {
dir->fs=&minix_filesys;
dir->dev=dev;
dir->aux=0;
dir->index=ROOT_INODE; /* Always same for minix filesytems */
ret=0;
}
return ret;
}
static long m_lookup(dir,name,entry)
fcookie *dir;
char *name;
fcookie *entry;
{
if(!*name)
{
*entry=*dir;
return 0;
}
if(dir->index==ROOT_INODE && !strcmp(name,".."))
{
*entry=*dir;
DEBUG("m_lookup returned EMOUNT");
return EMOUNT;
}
entry->index=search_dir(name,dir->index,dir->dev,FIND);
if(entry->index < 0 ) return entry->index ;
entry->dev=dir->dev;
entry->aux=0;
entry->fs=&minix_filesys;
return 0;
}
static long m_creat(dir,name,mode,attr,entry)
fcookie *dir;
char *name;
unsigned mode;
int attr;
fcookie *entry;
{
off_t pos;
d_inode ripnew;
mino_t newfile;
char *ext;
pos=0; /* Keep gcc -Wall happy */
/* Create dir entry */
if((pos=search_dir(name,dir->index,dir->dev,ADD))<0)
{
return pos;
}
/* Get new inode */
if(!(newfile=alloc_inode(dir->dev)))
{
DEBUG("m_getdev: no free inodes");
return EWRITF;
}
/* Set up inode */
bzero(&ripnew,sizeof(d_inode));
/* If creating a file with approriate extensions
* automatically give it execute permissions .
*/
if(do_trans(AEXEC_TOS) && ( ext=strrchr(name,'.') ) )
{
ext++;
if(
/* Insert your favourite extensions here */
!( Stricmp(ext,"TTP") && Stricmp(ext,"PRG")
&& Stricmp(ext,"APP") && Stricmp(ext,"TOS")
&& Stricmp(ext,"ACC") && Stricmp(ext, "GTP")))
mode |= 0111;
}
ripnew.i_mode= I_REGULAR | mode;
ripnew.i_uid=Getuid();
ripnew.i_gid=Getgid();
ripnew.i_nlinks=1;
ripnew.i_mtime=Unixtime(Timestamp(), Datestamp());
ripnew.i_atime=ripnew.i_mtime;
ripnew.i_ctime=ripnew.i_mtime;
write_inode(newfile,&ripnew,dir->dev);
l_write(dir->index,pos,2L,&newfile,dir->dev);
#ifdef WRITETHRU
l_sync();
#endif
entry->fs = dir->fs;
entry->dev = dir->dev;
entry->index=newfile;
entry->aux=0;
return 0;
}
static DEVDRV * m_getdev(file,special)
fcookie *file;
long *special;
{
return(&minix_dev);
}
static long m_getxattr(file,xattr)
fcookie *file;
XATTR *xattr;
{
off_t inum = file->index;
return(istat(inum,file->dev,xattr));
}
static long m_chown(file, uid , gid)
fcookie *file;
int uid,gid;
{
off_t inum = file->index;
return(ichown(inum,file->dev,uid,gid));
}
static long m_chmode(file, mode)
fcookie *file;
unsigned mode;
{
off_t inum = file->index;
return(ichmod(inum,file->dev,mode));
}
static long m_mkdir(dir,name,mode)
fcookie *dir;
char *name;
unsigned mode;
{
mino_t newdir;
d_inode rip,ripnew;
off_t pos;
int incr;
dir_struct blank[MAX_INCREMENT*2];
incr=super_ptr[dir->dev]->increment;
if((pos=search_dir(name,dir->index,dir->dev,ADD))<0)return pos;
read_inode(dir->index,&rip,dir->dev);
if(rip.i_nlinks>=MINIX_MAX_LINK)return EACCDN;
/* Get new inode */
if(!(newdir=alloc_inode(dir->dev)))return EACCDN;
/* Set up inode */
bzero(&ripnew,sizeof(d_inode));
ripnew.i_mode=I_DIRECTORY | (mode & 0777);
ripnew.i_uid=Getuid();
ripnew.i_gid=Getgid();
ripnew.i_nlinks=2;
ripnew.i_mtime=Unixtime(Timestamp(), Datestamp());
ripnew.i_ctime=ripnew.i_mtime;
ripnew.i_atime=ripnew.i_mtime;
write_inode(newdir,&ripnew,dir->dev);
/* Set up new directory */
strcpy(blank[0].d_name,".");
blank[0].d_inum=newdir;
strcpy(blank[incr].d_name,"..");
blank[incr].d_inum=dir->index;
if(l_write((unsigned)newdir,-1L,(off_t)(DIR_ENTRY_SIZE*2*incr),
blank,dir->dev)!=(incr*DIR_ENTRY_SIZE*2) )
{
ripnew.i_mode=0;
ripnew.i_nlinks=0;
write_inode(newdir,&ripnew,dir->dev);
free_inode(newdir,dir->dev);
l_sync();
return EACCDN;
}
rip.i_nlinks++;
write_inode(dir->index,&rip,dir->dev);
l_write(dir->index,pos,2L,&newdir,dir->dev);
#ifdef WRITETHRU
l_sync();
#endif
return(0);
}
static long m_rmdir(dir,name)
fcookie *dir;
char *name;
{
long chunk,left;
long inum;
int i,incr;
d_inode rip,rip2;
if((inum=search_dir(name,dir->index,dir->dev,FIND))<0)return inum;
read_inode(inum,&rip,dir->dev);
read_inode(dir->index,&rip2,dir->dev);
if(!IS_DIR(rip))return EFILNF;
incr=super_ptr[dir->dev]->increment;
/* Check if dir is actually empty */
for(chunk=0;(left=next_zone(&rip,chunk,&temp,dir->dev)/DIR_ENTRY_SIZE);
chunk++)
{
for(i=0;i<left;i+=incr)
if(temp.bdir[i].d_inum && strcmp(".",temp.bdir[i].d_name)
&& strcmp("..",temp.bdir[i].d_name))
return EACCDN ;
}
if(!inode_busy(inum,dir->dev))
{
trunc_inode(&rip,dir->dev,0L,0);
rip.i_mode=0;
free_inode(inum,dir->dev);
}
rip.i_nlinks=0;
write_inode(inum,&rip,dir->dev);
read_inode(dir->index,&rip,dir->dev);
rip.i_mtime=Unixtime(Timestamp(), Datestamp());
rip.i_nlinks--;
write_inode(dir->index,&rip,dir->dev);
search_dir(name,dir->index,dir->dev,KILL);
#ifdef WRITETHRU
l_sync();
#endif
return(0);
}
/* Unix-like unlink ... only works on regular files and symlinks but it should
* be safe to unlink an open file
*/
static long m_remove(dir,name)
fcookie *dir;
char *name;
{
long inum,ret;
d_inode rip;
inum=search_dir(name,dir->index,dir->dev,FIND);
if(inum<0) return inum;
read_inode(inum,&rip,dir->dev);
if(!IS_REG(rip) && ((rip.i_mode & I_TYPE) != I_SYMLINK) ) return EACCDN;
if((ret=search_dir(name,dir->index,dir->dev,KILL))<0) return ret;
if(--rip.i_nlinks==0)
{
if(!inode_busy(inum,dir->dev)) /* Is inode busy ? */
{
trunc_inode(&rip,dir->dev,0L,0);
rip.i_mode=0;
free_inode(inum,dir->dev);
}
}
write_inode(inum,&rip,dir->dev);
#ifdef WRITETHRU
l_sync();
#endif
return(0);
}
/* This function is inefficient , it uses the standard sys V method of
* finding out the pathname of the cwd : for each part of the path, search
* the parent for a link with the same inode number as '..' , append this to the
* path until we get to root dir , then reverse order of dirs. This way no
* temporary buffers are allocated which could overflow or kmalloc to fail ...
*/
/* In fact its so inefficient a mini-cache remembers the last call info */
static long m_getname(root,dir,pathname)
fcookie *root,*dir;
char *pathname;
{
mino_t inum,pinum;
int chunk;
long left;
int incr;
if(lpath && lroot.dev==root->dev &&
lroot.index==root->index && ldir.dev==dir->dev &&
ldir.index==dir->index)
{
TRACE("m_getname: cache hit");
strcpy(pathname,lpath);
return 0;
}
*pathname=0;
incr=super_ptr[dir->dev]->increment;
inum=dir->index;
while(inum!=root->index && inum!=ROOT_INODE)
{
d_inode rip;
bufr *tmp;
pinum=search_dir("..",inum,dir->dev,FIND);
/* Parent inum */
if(pinum < 0) /* If this happens we're in trouble */
{
ALERT("No .. in inode %d , drive %c",inum,dir->dev+'A');
return pinum;
}
read_inode(pinum,&rip,dir->dev);
for(chunk=0;
(left=cnext_zone(&rip,chunk,&tmp,dir->dev)/DIR_ENTRY_SIZE) &&
inum!=pinum ;chunk++)
{
char tname[MNAME_MAX+1];
int i;
for(i=0;i<left && inum!=pinum ;i+=incr)
if(tmp->bdir[i].d_inum==inum)
{
strncpy(tname,tmp->bdir[i].d_name,MMAX_FNAME(incr));
tname[MMAX_FNAME(incr)]=0;
strrev(tname);
strcat(pathname,tname);
strcat(pathname,"\\");
inum=pinum;
}
}
if(left==0 && inum!=pinum) {
ALERT("m_getname inode %d orphaned or bad ..",inum);
return EINTRN;
}
}
if(inum==ROOT_INODE && root->index!=ROOT_INODE)
{
DEBUG("m_getname: Hmmmm root is not a parent of dir");
return EINTRN;
}
strrev(pathname);
if(lpath)Kfree(lpath);
if(lpath=Kmalloc(strlen(pathname)+1))strcpy(lpath,pathname);
lroot=*root;
ldir=*dir;
return 0;
}
/* Minix fs device driver */
/* Under minixfs there is no 'per file structure' , that is all references to
the same file are independent.This complicates file sharing a bit , the 'next'
field points to the next fileptr for the minixfs , so that a search checks the
list sequentially (the global variable 'firstptr' is the start of the list) ,
references to the same file are grouped together so that the first reference
can act as a list pointer to denyshare() , though the last reference's 'next'
pointer is temporarily set to NULL to keep denyshare() happy. For file locking
the 'devinfo' field points to the first LOCK structure for this file (if any).
*/
static long m_open(f)
FILEPTR *f;
{
FILEPTR *lst,*elst,*tmplst;
d_inode rip;
/* do some sanity checking */
read_inode(f->fc.index,&rip,f->fc.dev);
/* maybe we should allow read access to directories, but since no other
* MiNT file systems do, it doesn't really seem necessary -- ERS
*/
if (!IS_REG(rip)) {
DEBUG("m_open: not a regular file");
return EACCDN;
}
/* should we truncate the file? */
if (f->flags & O_TRUNC) {
trunc_inode(&rip,f->fc.dev,0L,1);
rip.i_size = 0;
rip.i_mtime = Unixtime(Timestamp(), Datestamp());
write_inode(f->fc.index,&rip,f->fc.dev);
#ifdef WRITETHRU
l_sync();
#endif
}
/* Find first pointer to same file , if any */
for(lst=firstptr;lst;lst=lst->next)
if((f->fc.dev==lst->fc.dev) && (f->fc.index==lst->fc.index))break;
if(!lst) /* Not found */
{
/* Stick new fptr at top */
f->next=firstptr;
firstptr=f;
f->devinfo=0;
return 0;
}
/* Find last pointer to file */
for(elst=lst;elst->next;elst=elst->next)
if((elst->next->fc.dev!=lst->fc.dev) ||
(elst->next->fc.index!=lst->fc.index))break;
tmplst=elst->next;
elst->next=0;
if(Denyshare(lst,f))
{
elst->next=tmplst;
return EACCDN;
}
elst->next=f;
f->next=tmplst;
f->devinfo=elst->devinfo;
return 0;
}
static long
m_close(f, pid)
FILEPTR *f;
int pid;
{
FILEPTR *last;
/* If locked remove any locks for this pid */
if(f->flags & O_LOCK)
{
LOCK *lck,**oldl,*tdev;
TRACE("minixfs: removing locks for pid %d",pid);
tdev=(LOCK *)f->devinfo;
oldl=&tdev;
lck=tdev;
while(lck)
{
if(lck->l.l_pid == pid)
{
*oldl=lck->next;
Kfree(lck);
}
else oldl = &lck->next;
lck = *oldl;
}
if(((long)tdev)!=f->devinfo) /* First member changed */
{
FILEPTR *fp;
char found=0;
TRACE("m_close resetting devinfo");
for(fp=firstptr;fp;fp=fp->next)
{
if(fp->fc.index==f->fc.index &&
fp->fc.dev==f->fc.dev)
{
fp->devinfo=(long)tdev;
found=1;
}
else if(found) break;
}
}
}
if (f->links <= 0) {
if(f->fc.aux & AUX_DEL) /* Marked for deletion ? */
{
d_inode rip;
if(inode_busy(f->fc.index,f->fc.dev)!=2)
{
read_inode(f->fc.index,&rip,f->fc.dev);
trunc_inode(&rip, f->fc.dev,0L,0);
rip.i_mode=0;
write_inode(f->fc.index,&rip,f->fc.dev);
free_inode(f->fc.index,f->fc.dev);
}
}
if(f==firstptr) firstptr=f->next;
else
{
for(last=firstptr;last && (last->next!=f);last=last->next);
if(!last)ALERT("Minixfs FILEPTR chain corruption!");
else last->next=f->next;
}
}
l_sync(); /* always sync on close */
TRACE("m_close done");
return 0;
}
/* Minix read , all manner of horrible things can happen during
* a read , if fptr->pos is not block aligned we need to copy a partial
* block then a load of full blocks then a final possibly partial block
* any of these can hit EOF and we mustn't copy anything past EOF ...
* my poor head :-(
*/
static long
m_read(f,buf,len)
FILEPTR *f;
char *buf;
long len;
{
register void *p=buf;
d_inode rip;
zone_nr chunk,znew;
off_t left=len;
cache_control *control;
super_info *psblk;
assert(f);
assert(f->dev == &minix_dev);
psblk=super_ptr[f->fc.dev];
chunk=f->pos/BLOCK_SIZE;
read_inode(f->fc.index,&rip,f->fc.dev);
control= IS_DIR(rip) ? &syscache : &usrcache;
/* Are we at EOF ? */
if(f->pos>=rip.i_size)
return(0);
while(left) {
off_t c_left,cnum;
znew=find_zone(&rip,chunk,f->fc.dev,0);
/* Valid characters in current block */
c_left=min(rip.i_size-chunk*(long)BLOCK_SIZE,BLOCK_SIZE)
-(f->pos & (BLOCK_SIZE-1));
if(c_left<=0)break;
cnum=min(c_left,left);
chunk++;
if(cnum==BLOCK_SIZE)read_zones(znew,p,f->fc.dev,control);
else
{
read_zone(znew,&temp,f->fc.dev,control);
bcopy(&temp.bdata[f->pos & (BLOCK_SIZE-1)],p,cnum);
}
p+=cnum;
f->pos+=cnum;
left-=cnum;
}
read_zones(0,NULL,-1,control);
/* Normally we dont care about setting atime , since the writing of
* inodes will simply ignore the extra fields for V1 filesystems ,
* however read doesn't usually write inodes so we check if its V2
* since its silly writing to a V1 filesystem with the modified access
* time because it wont be stored ! Also for floppies never update atime
* this is a bit of a hack , should really test write protection and act
* accordingly. Also note l_sync() is not called here , it's hardly the
* end of the world if atime is not updated because of a crash and it
* improves performance a bit by delaying the update until close().
*/
if(psblk->version && (psblk->dev > 1))
{
rip.i_atime=Unixtime(Timestamp(),Datestamp());
write_inode(f->fc.index,&rip,f->fc.dev);
}
return(len-left);
}
/* seek is a bit easier */
static long m_seek(f,offset,flag)
FILEPTR *f;
long offset;
int flag;
{
d_inode rip;
#ifdef ROBUST
long oldpos;
oldpos=f->pos;
#endif
read_inode(f->fc.index,&rip,f->fc.dev);
switch(flag) {
case SEEK_SET :
if( offset >= 0 ) f->pos=offset;
else return(ERANGE);
break;
case SEEK_CUR :
if(f->pos+offset >= 0 ) f->pos+=offset;
else return(ERANGE);
break;
case SEEK_END :
if(rip.i_size >= -offset ) f->pos=rip.i_size + offset;
else return(ERANGE);
break;
default :
return(EINVFN);
}
#ifdef ROBUST
if(oldpos/BLOCK_SIZE!=f->pos/BLOCK_SIZE) f->fc.aux|=AUX_SYNC;
#endif
return (f->pos);
}
static long m_write(f,buf,len)
FILEPTR *f;
char *buf;
long len;
{
long ret;
assert(f);
assert(f->dev == &minix_dev);
ret=l_write(f->fc.index,f->pos,len,buf,f->fc.dev);
if(ret<0) return ret;
#ifdef ROBUST
/* This checks if m_seek has scheduled a sync or if the write has
* crossed a block boundary , if so sync the filesystem
*/
if( (f->fc.aux & AUX_SYNC) ||
( ( (f->pos & (BLOCK_SIZE-1))+ret) >=BLOCK_SIZE) )
{
l_sync();
f->fc.aux &=~AUX_SYNC;
}
#endif
f->pos+=ret;
return(ret);
}
static long m_ioctl(f,mode,buf)
FILEPTR *f;
int mode;
void *buf;
{
if((mode==FIONREAD) || (mode==FIONWRITE))
{
*((long *) buf)=1;
return 0;
}
/* File locking code , as with sharing a bit of care is needed since no
* per file structure exists , what we do is use the 'devinfo' field of
* a pointer to a linked list of locks , care is needed because if the
* first lock vanishes then we need to correct the devinfo field of all
* pointers to this file.
*/
if((mode==F_SETLK) || (mode==F_GETLK) )
{
LOCK t,*lck,*lastlck;
struct flock *fl;
int cpid; /* Current proc pid */
if(!lockok)
{
DEBUG("Locking Not Installed");
return EINVFN;
}
fl= ( struct flock *)buf;
t.l=*fl;
switch(t.l.l_whence)
{
case SEEK_SET:
break;
case SEEK_CUR:
t.l.l_start+=f->pos;
case SEEK_END:
{
d_inode rip;
read_inode(f->fc.index,&rip,f->fc.dev);
t.l.l_start=rip.i_size-t.l.l_start;
}
break;
default:
DEBUG("Invalid value for l_whence");
return EINVFN;
}
if(t.l.l_start < 0) t.l.l_start=0;
t.l.l_whence=0;
if(mode == F_GETLK) {
lck = Denylock((LOCK *)f->devinfo,&t);
if(lck) *fl = lck->l;
else fl->l_type = F_UNLCK;
return 0;
}
cpid=Getpid();
if(t.l.l_type==F_UNLCK)
{
/* Try to find the lock */
lastlck=(LOCK *)f->devinfo;
for(lck=lastlck;lck;lck=lck->next)
if( lck->l.l_pid == cpid &&
lck->l.l_start == t.l.l_start &&
lck->l.l_len == t.l.l_len)
break;
else lastlck=lck;
if(lck)
{
Kfree(lck);
if(lck==lastlck) /* Oooer, devinfo lock freed */
{
FILEPTR *fp;
char found=0;
for(fp=firstptr;fp;fp=fp->next)
{
if(fp->fc.index==f->fc.index &&
fp->fc.dev==f->fc.dev)
{
f->devinfo=(long)lck->next;
found=1;
}
else if(found) break;
}
if(!found)
{
ALERT("minixfs chain corruption!");
return EINVFN;
}
}
else lastlck->next=lck->next;
return 0;
}
else return ENSLOCK;
}
lck=Denylock((LOCK *)f->devinfo,&t);
if(lck) return ELOCKED;
lck = Kmalloc(SIZEOF(LOCK));
if(!lck) return ENSMEM;
lck->l = t.l;
lck->l.l_pid = cpid;
if(f->devinfo) /* Locks already there ? */
{
/* Yes, add new lock after first */
lck->next=((LOCK *)f->devinfo)->next;
((LOCK *)f->devinfo)->next=lck;
}
else /* No , initialise all devinfo fields */
{
FILEPTR *fp;
char found=0;
lck->next=0;
for(fp=firstptr;fp;fp=fp->next)
{
if(fp->fc.dev==f->fc.dev &&
fp->fc.index==f->fc.index)
{
found=1;
fp->devinfo=(long)lck;
}
else if(found) break;
}
if(!found)
{
ALERT("minixfs: chain corruption!");
return EINVFN;
}
}
f->flags |=O_LOCK; /* Lock op done on FILEPTR */
return 0;
}
return EINVFN;
}
/* Made this a bit like utimes , sets atime,mtime to give ctime=current time */
static long m_datime(f,timeptr,flag)
FILEPTR *f;
int *timeptr;
int flag;
{
d_inode rip;
super_info *psblk;
psblk=super_ptr[f->fc.dev];
read_inode(f->fc.index,&rip,f->fc.dev);
switch (flag)
{
case 0 :
*((long *)timeptr)=Dostime(_corr(rip.i_mtime));
break;
case 1 :
rip.i_mtime=Unixtime(timeptr[0],timeptr[1]);
rip.i_atime=rip.i_mtime;
rip.i_ctime=Unixtime(Timestamp(),Datestamp());
write_inode(f->fc.index,&rip,f->fc.dev);
#ifdef WRITETHRU
l_sync();
#endif
break;
default :
return -1;
break;
}
return 0;
}
static long m_select(f,proc,mode)
FILEPTR *f;
long proc;
int mode;
{
return 1;
}
static void m_unselect(f,proc,mode)
FILEPTR *f;
long proc;
int mode;
{
/* Do nothing */
}
static long m_opendir(dirh,flag)
DIR *dirh;
int flag;
{
d_inode rip;
read_inode(dirh->fc.index,&rip,dirh->fc.dev);
dirh->index=0;
return 0;
}
static long m_readdir(dirh,name,namelen,fc)
DIR *dirh;
char *name;
int namelen;
fcookie *fc;
{
d_inode rip;
bufr *tmp;
unsigned entry,chunk;
super_info *psblk;
off_t limit;
int flag,incr;
psblk=super_ptr[dirh->fc.dev];
if(dirh->flags) flag=do_trans(DIR_TOS);
else flag=0;
if(!dirh->fc.index)return EACCDN;
entry=dirh->index % NR_DIR_ENTRIES;
chunk=dirh->index / NR_DIR_ENTRIES;
read_inode(dirh->fc.index,&rip,dirh->fc.dev);
/* Not absolutely valid here but near enough.See note in m_read too */
if(psblk->version && (dirh->fc.dev > 1) )
{
rip.i_atime=Unixtime(Timestamp(),Datestamp());
write_inode(dirh->fc.index,&rip,dirh->fc.dev);
}
incr=super_ptr[dirh->fc.dev]->increment;
while(limit=cnext_zone(&rip,chunk,&tmp,dirh->fc.dev)/DIR_ENTRY_SIZE)
{
while( entry < limit)
{
dir_struct *try=&tmp->bdir[entry];
entry+=incr;
if(try->d_inum)
{
char *tmpnam;
tmpnam=tosify(try->d_name,flag,MMAX_FNAME(incr));
if (dirh->flags==0)
{
namelen -= sizeof(long);
if (namelen <= 0) return ERANGE;
*((long *)name) = (long)try->d_inum;
name += sizeof(long);
}
strncpy(name,tmpnam,namelen);
dirh->index=entry+chunk*NR_DIR_ENTRIES;
/* set up a file cookie for this entry */
fc->dev = dirh->fc.dev;
fc->aux = 0;
fc->index = (long)try->d_inum;
fc->fs = &minix_filesys;
if(strlen(tmpnam) >= namelen)
return ENAMETOOLONG;
return 0;
}
}
if(entry!=NR_DIR_ENTRIES)return ENMFIL;
else entry=0;
chunk++;
}
return ENMFIL;
}
static long m_rewinddir(dirh)
DIR *dirh;
{
dirh->index=0;
return 0;
}
static long m_closedir(dirh)
DIR *dirh;
{
dirh->fc.index=0;
l_sync();
return 0;
}
static long m_rlabel(dir,name,namelen)
fcookie *dir;
char *name;
int namelen;
{
return EACCDN;
}
static long m_wlabel(dir,name)
fcookie *dir;
char *name;
{
return EFILNF;
}
static long m_dfree(dir,buffer)
fcookie *dir;
long *buffer;
{
super_info *psblk ;
psblk = super_ptr[dir->dev];
buffer[1] = psblk->sblk.s_zones-psblk->sblk.s_firstdatazn;
buffer[0] = buffer[1] - count_bits(psblk->zbitmap,
(ushort)(buffer[1]+1))+1;
buffer[2]=512L;
buffer[3]=2L;
return(0);
}
static long m_fscntl(dir,name,cmd,arg)
fcookie *dir;
char *name;
int cmd;
long arg;
{
FILEPTR *f;
mfs_info *inf;
super_info *psblk;
long inum;
int uid,id;
d_inode rip;
uid=Getuid();
switch(cmd)
{
case MFS_VERIFY:
*((long *)arg)=MFS_MAGIC;
return 0;
/* Sync the filesystem */
case MFS_SYNC:
l_sync();
return 0;
/* Invalidate all cache entries for a given drive */
case MFS_CINVALID:
if(uid) return EACCDN;
m_invalidate(dir->dev);
return 0;
/* Invalidate all fileptrs for a given drive */
case MFS_FINVALID:
if(uid) return EACCDN;
id=Getpid();
for(f=firstptr;f;f=f->next)if(f->fc.dev==dir->dev)m_close(f,id);
return 0;
case MFS_INFO:
psblk=super_ptr[dir->dev];
inf=(mfs_info *)arg;
inf->total_zones=psblk->sblk.s_zones-psblk->sblk.s_firstdatazn;
inf->total_inodes=psblk->sblk.s_ninodes;
inf->version=psblk->version+1;
inf->increment=psblk->increment;
inf->free_inodes=inf->total_inodes-
count_bits(psblk->ibitmap,inf->total_inodes+1)-1;
inf->free_zones=inf->total_zones-count_bits(psblk->zbitmap,inf->total_zones+1)+1;
return 0;
case MFS_IMODE:
if(uid) return EACCDN;
inum=search_dir(name,dir->index,dir->dev,FIND);
if(inum < 0 ) return inum;
read_inode(inum,&rip,dir->dev);
rip.i_mode=arg;
write_inode(inum,&rip,dir->dev);
case MFS_GTRANS:
*((long *) arg)=trans_mode;
return 0;
case MFS_STRANS:
if(uid) return EACCDN;
trans_mode=*((long *)arg);
return 0;
default:
return EINVFN;
}
}
long count_bits(buf,num)
ushort *buf;
long num;
{
long i,count;
count = 0;
for(i = 0; i < num/16; i++) {
if(buf[i]==65535)count+=16;
else count+=bitcount(buf[i]);
}
count += bitcount((ushort)(buf[num/16] & (( 1l<< (num%16)) -1l)));
return(count);
}
int bitcount(wrd)
unsigned int wrd;
{
long i;
int count;
count=0;
for(i=1;i<65536;i<<=1)if(wrd & i)count++;
return(count);
}
/* I *HATE* this function , it attempts to turn a Minix filename into one
which wont cause TOS/DESKTOP etc to blow up , feel free to relace with
someting better (I doubt there could be much worse) if flag==0 just return
a null terminated version of 'name', mnamlength is the maximum filename length
*/
char *tosify(name,flag,mnamlength)
char *name;
int flag;
int mnamlength;
{
static char first[MNAME_MAX+8];
char *p,*ldt;
static char final[MNAME_MAX+8];
if(!strcmp(name,".") || !strcmp(name,".."))return(name);
strncpy(first,name,mnamlength);
first[mnamlength]=0;
if(flag==0)return(first);
ldt=strrchr(first,'.');
for(p=first;*p;p++)
if(islower(*p))
*p=toupper(*p);
else if(*p=='.')
*p=',';
if( !ldt || ldt==first || !ldt[1]) {
first[8]=0;
strcpy(final,first);
} else {
*ldt=0;
ldt[4]=0;
strcpy(final,first);
final[8]=0;
strcat(final,".");
strcat(final,ldt+1);
}
return(final);
}
/* Bitmap handling stuff */
long alloc_zone(drive)
int drive;
{
long save;
super_info *psblk=super_ptr[drive];
if( !(save=alloc_bit(psblk->zbitmap,psblk->sblk.s_zones
-psblk->sblk.s_firstdatazn+1,psblk->zlast)) )
return 0;
psblk->zdirty=1; /* Mark zone bitmap as dirty */
if(save>psblk->zlast)psblk->zlast=save;
return(save+psblk->sblk.s_firstdatazn-1);
}
mino_t alloc_inode(drive)
int drive;
{
ushort save;
super_info *psblk=super_ptr[drive];
if(!(save=alloc_bit(psblk->ibitmap,psblk->sblk.s_ninodes+1,0)))
return 0;
psblk->idirty=1; /* Mark inode bitmap as dirty */
return(save);
}
/* Release a zone */
int free_zone(zone,drive)
long zone;
int drive;
{
super_info *psblk=super_ptr[drive];
long save,ret;
if(chk_zone(zone,drive,"free_zone")) return 1;
save=zone+1-psblk->sblk.s_firstdatazn;
ret=free_bit(psblk->zbitmap,save);
psblk->zdirty=1; /* Mark zone bitmap as dirty */
if(save<psblk->zlast)psblk->zlast=save;
if(!ret) ALERT("Drive %d zone %ld freeing already free zone !",drive,zone);
return(ret);
}
/* Release an inode */
int free_inode(inum,drive)
unsigned inum;
int drive;
{
long ret;
super_info *psblk=super_ptr[drive];
if(chk_inode(inum,psblk,"free_inode")) return 0;
ret=free_bit(psblk->ibitmap,inum);
psblk->idirty=1; /* Mark inode bitmap as dirty */
if(!ret) ALERT("Drive %d inode %d , freeing already free inode!",drive,inum);
return(ret);
}
/* This routine is used for allocating both free inodes and free zones
* Search a bitmap for a zero , then return its bit number and change it
* to a one ...... but without exceeding 'num' bits
*/
long alloc_bit(buf,num,last)
ushort *buf;
long num,last;
{
long i,j,k;
k=1;
for(i=last>>4;(i<=(num>>4)) && (buf[i]==65535);i++)
;
if( i > (num>>4) )
return(0);
else {
for(j=0;j<16;j++) {
if(!(buf[i] & k)) {
long free;
free=i*16+j;
if(free>=num)return 0;
buf[i]|=k;
return(free);
}
k<<=1;
}
}
ALERT("minixfs: alloc_bit: This can't happen !");
return 0;
}
/* zero a bit of a bitmap return 0 if already zero */
long free_bit(buf,bitnum)
ushort *buf;
long bitnum;
{
register long index=bitnum>>4;
register ushort bit = 1 << (bitnum & 15);
long ret;
ret=buf[index] & bit;
buf[index]&= ~bit;
return(ret);
}
/* l_write is used internally for doing things a normal user cannot such
* as writing to a directory ... it accepts 5 parameters , an inode num
* a position (current position of write) a count which is the number of
* characters to write,a buffer and a drive , it updates i_size as needed
* and allocates zones as required , it is nastier than a read because it
* has to write partial blocks within valid blocks and to write beyond EOF
*/
long l_write(inum,pos,len,buf,drive)
unsigned inum;
off_t pos;
off_t len;
void *buf;
int drive;
{
register void *p=buf; /* Current position in buffer */
d_inode rip;
long chunk;
off_t left=len;
long zne;
cache_control *control;
read_inode(inum,&rip,drive);
/* Work out which cache to use */
control = IS_DIR(rip) ? &syscache : &usrcache;
if(pos==-1l) pos=rip.i_size; /* If pos==-1 append */
chunk=pos/BLOCK_SIZE;
while(left) /* Loop while characters remain to be written */
{
off_t zoff;
ushort wleft;
zne=find_zone(&rip,chunk++,drive,1); /* Current zone in file */
if(zne==0)break; /* Partition full */
zoff = pos & (BLOCK_SIZE -1); /* Current zone position */
wleft=min(BLOCK_SIZE-zoff,left); /*Left to write in curr blk*/
if((zoff) || ( (left < BLOCK_SIZE) && (pos+left<rip.i_size)))
read_zone(zne,&temp,drive,control);
else if(wleft!=BLOCK_SIZE)bzero(&temp,(size_t)BLOCK_SIZE);
if(wleft!=BLOCK_SIZE)
{
bcopy(p,&temp.bdata[zoff],(size_t)wleft);
write_zone(zne,&temp,drive,control);
}
else write_zones(zne,p,drive,control);
pos+=wleft;
p+=wleft;
if(pos>rip.i_size)rip.i_size=pos;
left-=wleft;
}
write_zones(0,NULL,-1,control);
rip.i_mtime=Unixtime(Timestamp(), Datestamp());
write_inode(inum,&rip,drive); /* Update inode */
return(len-left);
}
/* inode_busy() scans the list for a given inode , this is used to stop
* unlink blowing away a busy inode. In addition f->aux flag is set this is
* so a subsequent close can recognise that the inode is marked for zapping
*/
int inode_busy(inum,drive)
unsigned inum;
int drive;
{
FILEPTR *f;
int found;
found=0;
for(f=firstptr;f;f=f->next)
{
if((f->fc.index==inum) && (f->fc.dev==drive))
{
f->fc.aux|=AUX_DEL;
if(found==1)found=2;
else found=1;
}
else if(found) break; /* Since same files consecutive , halt search */
}
return (found);
}
/* m_rename , move a file or directory . Directories need special attention
* because if /usr/foo is moved to /usr/foo/bar then the filesystem will be
* damaged by making the /usr/foo directory inaccessible. The sanity checking
* performed is very simple but should cover all cases: Start at the parent
* of the destination , check if this is the source inode , if not then
* move back to '..' and check again , repeatedly check until the root inode
* is reached , if the source is ever seen on the way back to the root then
* the rename is invalid , otherwise it should be OK.
*/
static long m_rename(olddir,oldname,newdir,newname)
fcookie *olddir;
char *oldname;
fcookie *newdir;
char *newname;
{
long finode,ret;
d_inode rip;
off_t pos;
char dirmove,dirren;
dirmove=0;
dirren=0;
/* Check cross drives */
if(olddir->dev!=newdir->dev)return EXDEV;
/* Check new doesn't exist and path is otherwise valid */
finode=search_dir(newname,newdir->index,newdir->dev,FIND);
if(finode>0) return EACCDN;
if(finode!=EFILNF) return finode;
/* Check old path OK */
if((finode=search_dir(oldname,olddir->index,olddir->dev,FIND))<0)
return finode;
read_inode(finode,&rip,olddir->dev);
/* Sanity check movement of directories */
if(IS_DIR(rip))
{
dirren=1;
if(olddir->index!=newdir->index)
{
#ifdef MFS_NMOVE_DIR
return EACCDN;
#else
d_inode riptemp;
ret=is_parent(newdir->index,finode,olddir->dev);
if(ret < 0) return ret;
if(ret) return EACCDN;
read_inode(newdir->index,&riptemp,newdir->dev);
if(riptemp.i_nlinks==MINIX_MAX_LINK) return EACCDN;
TRACE("minixfs: valid directory move");
dirmove=1;
#endif
}
}
/* Check the m_getname cache is not invalidated by this move ....
if no dir move, invalidate if the ldir is the name being changed ,
if we move dir's then if the olddir is a parent of ldir , invalidate */
if(ldir.dev==olddir->dev && ( ldir.index==finode ||
(dirmove && is_parent(ldir.index,finode,olddir->dev)==1 )))
{
if(lpath)Kfree(lpath);
lpath=0;
}
/* Create new entry */
if((pos=search_dir(newname,newdir->index,newdir->dev,ADD))<0) return pos;
/* Delete old path */
if((finode=search_dir(oldname,olddir->index,olddir->dev,KILL))<0)
return finode;
l_write(newdir->index,pos,2L,&((mino_t)finode)+1,newdir->dev);
/* When moving directories , fixup things like '..' and nlinks of old and
* new dirs
*/
if(dirmove)
{
pos=search_dir("..",finode,newdir->dev,POS);
if(pos<0)
{
ALERT("m_rename: no .. in inode %ld",finode);
return EACCDN;
}
if(pos!=DIR_ENTRY_SIZE)
ALERT("m_rename: Unexpected .. position in inode %ld",finode);
l_write(finode,pos,2L,&((mino_t) newdir->index)+1,newdir->dev);
read_inode(olddir->index,&rip,olddir->dev);
rip.i_nlinks--;
write_inode(olddir->index,&rip,olddir->dev);
read_inode(newdir->index,&rip,newdir->dev);
rip.i_nlinks++;
write_inode(newdir->index,&rip,newdir->dev);
}
#ifdef WRITETHRU
l_sync();
#endif
/* Check the m_getname cache is not invalidated by this move ....
* if no dir alter, invalidate if the ldir is the name being changed ,
* if we alter dir's then if the moved dir is a parent of ldir , invalidate.
*/
if(ldir.dev==olddir->dev && ( ldir.index==finode ||
(dirren && is_parent(ldir.index,finode,olddir->dev)==1 )))
{
if(lpath)Kfree(lpath);
lpath=0;
}
return 0;
}
/* Return '1' is dir2 is a parent of dir1 , otherwise 0 or negative error
* number
*/
static long is_parent(dir1,dir2,drive)
unsigned dir1,dir2;
int drive;
{
long itemp=dir1;
for(;;)
{
if(itemp==dir2)
{
DEBUG("minixfs: invalid directory move");
return 1;
}
if(itemp==ROOT_INODE)break;
itemp=search_dir("..",itemp,drive,FIND);
if(itemp < 0)
{
ALERT("Couldn't trace inode %d back to root",dir1);
return EACCDN;
}
}
return 0;
}
/* Minix hard-link , you can't make a hardlink to a directory ... it causes
* too much trouble , use symbolic links instead.
*/
static long m_hardlink(fromdir,fromname,todir,toname)
fcookie *fromdir;
char *fromname;
fcookie *todir;
char *toname;
{
long finode;
d_inode rip;
off_t pos;
/* Check cross drives */
if(fromdir->dev!=todir->dev)return EXDEV;
/* Check new doesn't exist and path is otherwise valid */
finode=search_dir(toname,todir->index,todir->dev,FIND);
if(finode>0) return EACCDN;
if(finode!=EFILNF) return finode;
/* Check old path OK */
if((finode=search_dir(fromname,fromdir->index,fromdir->dev,FIND))<0)
return finode;
read_inode(finode,&rip,fromdir->dev);
if(!IS_REG(rip) || (rip.i_nlinks >=MINIX_MAX_LINK) ) return EACCDN;
/* Create new entry */
if((pos=search_dir(toname,todir->index,todir->dev,ADD))<0) return pos;
l_write(todir->index,pos,2L,&((mino_t)finode)+1,todir->dev);
rip.i_nlinks++;
rip.i_ctime=Unixtime(Timestamp(),Datestamp());
write_inode(finode,&rip,fromdir->dev);
#ifdef WRITETHRU
l_sync();
#endif
return 0;
}
/* Symbolic links ... basically similar to a regular file with one zone */
static long m_symlink(dir,name,to)
fcookie *dir;
char *name;
char *to;
{
d_inode rip;
off_t pos;
mino_t newinode;
if(!*to)
{
DEBUG("m_symlink: invalid null filename");
return EACCDN;
}
if(strlen(to)>=SYMLINK_NAME_MAX)
{
DEBUG("minixfs: Symbolic link name too long");
return ERANGE;
}
if((pos=search_dir(name,dir->index,dir->dev,ADD))<0) return pos;
if(!(newinode=alloc_inode(dir->dev)))
{
DEBUG("minixfs: symlink drive %c,no free inodes",dir->dev+'A');
return EACCDN;
}
bzero(&rip,sizeof(d_inode));
rip.i_mode=I_SYMLINK | 0777;
rip.i_size=strlen(to)+1;
rip.i_uid=Getuid();
rip.i_gid=Getgid();
rip.i_mtime=Unixtime(Timestamp(),Datestamp());
rip.i_ctime=rip.i_mtime;
rip.i_atime=rip.i_mtime;
rip.i_nlinks=1;
if(!(rip.i_zone[0]=alloc_zone(dir->dev)))
{
free_inode(newinode,dir->dev);
DEBUG("minixfs: symlink drive %c no free zones",dir->dev+'A');
return EACCDN;
}
btos_cpy((char *)&temp,to);
write_zone(rip.i_zone[0],&temp,dir->dev,&syscache);
write_inode(newinode,&rip,dir->dev);
l_write(dir->index,pos,2L,&newinode,dir->dev);
#ifdef WRITETHRU
l_sync();
#endif
return 0;
}
static long m_readlink(file,buf,len)
fcookie *file;
char *buf;
int len;
{
off_t inum = file->index;
d_inode rip;
read_inode(inum,&rip,file->dev);
if( (rip.i_mode & I_TYPE)!=I_SYMLINK)
{
DEBUG("minixfs: attempted readlink on non-symlink");
return EACCDN;
}
read_zone(rip.i_zone[0],&temp,file->dev,&syscache);
if(stob_ncpy(buf, (char *) &temp,len))
{
DEBUG("m_readlink: name too long");
return ERANGE;
}
TRACE("m_readlink returned %s",buf);
return 0;
}
/* Copy characters from 'from' to 'to' translating backslashes to slashes */
void btos_cpy(to,from)
char *to,*from;
{
char c;
do {
c=*from++;
if(c=='\\')*to++='/';
else *to++=c;
}
while(c);
}
/* Translate slashes to backslashes , return zero if all characters copied */
int stob_ncpy(to,from,n)
char *to,*from;
long n;
{
char c;
if(n==0) return 0;
do
{
c=*from++;
if(c=='/')*to++='\\';
else *to++=c;
}
while(--n && c );
if(c)
{
*--to='\0';
return 1;
}
return 0;
}
/* the only settable attribute is FA_RDONLY; if the bit is set,
* the mode is changed so that no write permission exists
*/
static long m_chattr(file,attr)
fcookie *file;
int attr;
{
off_t inum = file->index;
int drive = file->dev;
d_inode rip;
if ( (attr & FA_RDONLY) ) {
read_inode(inum,&rip,drive);
rip.i_mode &= ~(0222); /* turn off write permission */
rip.i_ctime=Unixtime(Timestamp(),Datestamp());
write_inode(inum,&rip,drive);
l_sync();
} else if (attr == 0) {
read_inode(inum,&rip,drive);
if ( (rip.i_mode & 0222) == 0 ) {
rip.i_mode |= ( (rip.i_mode&0444) >> 1 );
/* turn write permission back on */
rip.i_ctime=Unixtime(Timestamp(),Datestamp());
write_inode(inum,&rip,drive);
l_sync();
}
}
return 0;
}
static long m_pathconf(dir,which)
fcookie *dir;
int which;
{
switch(which) {
case -1:
return DP_MAXREQ;
case DP_IOPEN:
return UNLIMITED;
case DP_MAXLINKS:
return MINIX_MAX_LINK;
case DP_PATHMAX:
return PATH_MAX;
/*(although there is no restriction on minixfs itself)*/
case DP_NAMEMAX:
return MMAX_FNAME(super_ptr[dir->dev]->increment);
case DP_ATOMIC:
return BLOCK_SIZE; /* we can write at least a block atomically */
case DP_TRUNC:
return DP_AUTOTRUNC;
default:
return EINVFN;
}
}
/* Decide whether to translate or not according to 'flag' this assumes 'flag'
* is the tos domain form and flag<<1 is the mint domain form. This code has
* been written so Domain() is called at most once .
*/
int do_trans(flag)
long flag;
{
long tmode,mmode;
tmode = trans_mode & flag ;
mmode = trans_mode & (flag<<1) ;
/* If both modes , always translate */
if(mmode && tmode) return 1;
/* If neither , never */
if(!mmode && !tmode) return 0;
if(mmode)
{
if (Domain()==DOM_MINT) return 1;
else return 0;
}
if( tmode && (Domain()==DOM_TOS) ) return 1;
return 0;
}
/* Truncate an inode to 'count' zones, this is used by unlink() as well as
* (f)truncate() . Bit tricky this , we have to note which blocks to free,
* and free indirection/double indirection blocks too but iff all the blocks
* inside them are free too. We also need to keep count of how much to leave
* alone , sparse files complicate this a bit .... so do 2 fs versions ....
*/
void trunc_inode(rip,drive,count,zap)
d_inode *rip;
int drive;
long count; /* number of blocks to leave */
int zap; /* flag to alter inode */
{
int i,j;
bufr *tmp;
char some,dirty;
super_info *psblk=super_ptr[drive];
char vers;
vers=psblk->version;
/* Handle zones in inode first */
if(count<psblk->dzpi)
{
for(i=count;i<psblk->dzpi;i++) {
if(rip->i_zone[i])
free_zone(rip->i_zone[i],drive);
if(zap)rip->i_zone[i]=0;
}
count=0;
}
else count-=psblk->dzpi;
/* Handle indirect zone */
if(count< psblk->zpind) {
some=0;
dirty=0;
if(rip->i_zone[7]) {
tmp=get_zone(rip->i_zone[7],drive);
for(i=0;i<psblk->zpind;i++) {
if(PIND(vers,tmp,i)) {
if(count)some=1;
else {
free_zone(PIND(vers,tmp,i),drive);
if(zap)PIND(vers,tmp,i)=0;
dirty=1;
}
}
if(count)count--;
}
if(!some) {
free_zone(rip->i_zone[7],drive);
if(zap)rip->i_zone[7]=0;
}
else if(dirty)
write_zone(rip->i_zone[7],tmp,drive,&syscache);
}
}
else count-=psblk->zpind;
/* Handle double indirect ... */
if(rip->i_zone[8]) {
some=0;
dirty=0;
read_zone(rip->i_zone[8],&temp,drive,&syscache);
for(i=0;i<psblk->zpind;i++) {
if(IND(vers,temp,i)) {
char lsome,ldirty; /* local some,dirty for inds */
lsome=0;
ldirty=0;
tmp=get_zone(IND(vers,temp,i),drive);
for(j=0;j<psblk->zpind;j++) {
if(PIND(vers,tmp,j)) {
if(count) {
some=1;
lsome=1;
}
else {
free_zone(PIND(vers,tmp,j),drive);
if(zap)PIND(vers,tmp,j)=0;
ldirty=1;
dirty=1;
}
}
if(count)count--;
}
if(!lsome) {
free_zone(IND(vers,temp,i),drive);
if(zap)IND(vers,temp,i)=0;
}
else if(ldirty)
write_zone(IND(vers,temp,i),tmp,drive,&syscache);
}
else
{
if(count>=psblk->zpind)count-=psblk->zpind;
else count=0;
}
}
if(!some) {
free_zone(rip->i_zone[8],drive);
if(zap)rip->i_zone[8]=0;
}
else if(dirty)write_zone(rip->i_zone[8],&temp,drive,&syscache);
}
}
/* Now for some u*ix like routines */
/* We will get a load of ixxxx routines which operate on inodes,
* the path/fd equivalents can then easily be derived from them
* e.g. fstat , stat from istat , chmod , fchmod from ichmod.
*/
long istat(inum,drive,st)
unsigned inum;
int drive;
XATTR *st;
{
d_inode rip;
off_t time_tmp;
super_info *psblk;
psblk=super_ptr[drive];
read_inode(inum,&rip,drive);
/* Minix and gcc use different values for FIFO's */
if((rip.i_mode & I_TYPE) == I_NAMED_PIPE)
st->attr= S_IFIFO | (rip.i_mode & ALL_MODES);
else st->mode=rip.i_mode;
/* We could potentially have trouble with symlinks too */
#if I_SYMLINK != S_IFLNK
if( (rip.i_mode & I_TYPE) == I_SYMLINK)
st->attr= S_IFLNK | (rip.i_mode & ALL_MODES);
#endif
/* Fake attr field a bit , to keep TOS happy */
if(IS_DIR(rip))st->attr=FA_DIR;
else st->attr=(rip.i_mode & 0222) ? 0 : FA_RDONLY;
st->index=inum;
st->dev=drive;
#if 0
/* Char and block special files need major/minor device nos filled in */
if(((rip.i_mode & I_TYPE) == I_CHAR_SPECIAL )||
( (rip.i_mode & I_TYPE) == I_BLOCK_SPECIAL ))
st->reserved=rip.i_zone[0];
else st->reserved=0;
#endif
st->nlink=rip.i_nlinks;
st->uid=rip.i_uid;
st->gid=rip.i_gid;
st->size=rip.i_size;
st->blksize = BLOCK_SIZE;
/* Note: the nblocks calculation is accurate only if the file is
* contiguous. It usually will be, and if it's not, it shouldn't
* matter ('du' will return values that are slightly too high)
*/
st->nblocks = (st->size + (BLOCK_SIZE-1)) / BLOCK_SIZE;
if (st->nblocks >= psblk->dzpi)
st->nblocks++; /* correct for the indirection block */
if (st->nblocks > psblk->ndbl) {
st->nblocks++; /* correct for double indirection block */
st->nblocks += ((st->nblocks-(psblk->ndbl+2))/psblk->zpind);
/* and single indirection blocks */
}
time_tmp=Dostime(_corr(rip.i_mtime));
st->mtime=time_tmp >> 16;
st->mdate=time_tmp & (0xffff);
time_tmp=Dostime(_corr(rip.i_atime));
st->atime=time_tmp >> 16;
st->adate=time_tmp & (0xffff);
time_tmp=Dostime(_corr(rip.i_ctime));
st->ctime=time_tmp >> 16;
st->cdate=time_tmp & (0xffff);
return 0;
}
long ichmod(inum,drive,mode)
unsigned inum;
int drive;
unsigned mode;
{
d_inode rip;
super_info *psblk=super_ptr[drive];
read_inode(inum,&rip,drive);
rip.i_mode=(rip.i_mode & I_TYPE)|(mode & ALL_MODES);
if(psblk->version)rip.i_ctime=Unixtime(Timestamp(),Datestamp());
write_inode(inum,&rip,drive);
l_sync();
return 0;
}
long ichown(inum,drive,user,group)
unsigned inum;
int drive;
int user,group;
{
d_inode rip;
read_inode(inum,&rip,drive);
if(user!=-1)rip.i_uid=user;
if(group!=-1)rip.i_gid=group;
rip.i_ctime=Unixtime(Timestamp(),Datestamp());
write_inode(inum,&rip,drive);
l_sync();
return 0;
}
long itruncate(inum,drive,length)
unsigned inum;
int drive;
off_t length;
{
off_t count;
d_inode rip;
read_inode(inum,&rip,drive);
/* Regulars only , clever directory compaction stuff later ... */
if(!IS_REG(rip))return EACCDN;
/* If file smaller than 'length' nothing to do */
if(rip.i_size <= length)return 0;
count=(length+1023)/1024;
trunc_inode(&rip,drive,count,1);
rip.i_size=length;
write_inode(inum,&rip,drive);
l_sync();
return 0;
}