home *** CD-ROM | disk | FTP | other *** search
- /* 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;
- }
-