/* This file is part of 'minixfs' Copyright 1991,1992,1993,1994 S.N.Henson */ #include "minixfs.h" #include "proto.h" #include "global.h" FILESYS minix_filesys = { (FILESYS *) 0, #ifdef SYSUPDATE FS_CASESENSITIVE | FS_LONGPATH | FS_DO_SYNC, #else FS_CASESENSITIVE | FS_LONGPATH, #endif 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, m_release,m_dupcookie, #ifdef SYSUPDATE l_sync #endif }; extern DEVDRV minix_dev; static restore_dev=-1; /* * the kernel calls this when it detects a disk change. Note the mnt_flags * flag MNT_CHANGE signals that the drive is being changed to force relogging * of it's root directory and it should not be completely changed. */ /* * Hack alert: disk changes on mounted filesystems are ungracefully handled. * Basically, if it isn't the first forced change then they are ignored. See * comment (3) in #if does_not_work_yet for reason. This isn't any great loss * because changing mounted filesystems is very naughty (causing various Unix * variants to panic, in more senses than one. If Unix doesn't handle it why * should I? ). Actually since changes, by necessity force the mounted * structure to be destroyed, there isn't much we can gracefully do. */ long m_dskchng(d) int d; { FILEPTR *f, **last; super_info *psblk; char ignore; psblk=super_ptr[d]; TRACE("Disk Change drive %c",d+'A'); if(psblk && psblk!=DFS) { ignore = psblk->mnt_flags & MNT_CHANGE; /* If mounted then (for now) ignore changes */ if(!ignore && psblk->mnt_inode) return 0; } else ignore=0; if(psblk && psblk!=DFS && !ignore) { Kfree(psblk->ibitmap); #if does_not_work_yet /* Need to do three things: * 1. Make sure all children are no longer mounted. * 2. Make sure filesystem is no longer searched in lookups * of parent device. * 3. (sigh) Force media change of all descendents without * disturbing mounted structure, so MiNT will reread the * root dirs properly. */ if(psblk->mnt_inode) /* Umount this filesystem */ { super_info **ublk,*tsblk; /* Unlink from list on parent device */ for(ublk=&super_ptr[psblk->mnt_dev]->mnt_first;*ublk; ublk=&(*ublk)->mnt_next) if(*ublk==psblk) { *ublk=psblk->mnt_next; break; } /* Umount all children */ for(tsblk=psblk->mnt_first;tsblk;tsblk=tsblk->mnt_next) tsblk->mnt_inode=0; } #endif Kfree(psblk); } if(!ignore) super_ptr[d]=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); /* Free any memory associated to file pointers of this drive. */ last = &firstptr; for (f = *last; f != 0; f = *last) { if (f->fc.dev == d) { f_cache *fch = (f_cache *) f->devinfo; /* The lock structure is shared between the fileptr's. Make sure that it is freed only once. */ if (!f->next || f->next->fc.dev != d || f->next->fc.index != f->fc.index) { LOCK *lck, *nextlck; nextlck = *fch->lfirst; while ((lck = nextlck) != 0) { nextlck = lck->next; Kfree (lck); } Kfree (fch->lfirst); } Kfree (fch); /* Remove this fileptr from the list. */ *last = f->next; f->next = 0; } else last = &f->next; } if(!ignore) minix_sanity(d); return 1; } /* * 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. This is means that a * minix disk inserted into a drive will be unrecognisable at boot up and * a forced disk change is needed. However for MiNT 1.05 (and presumably * later) drives are initialised on first access so this isn't needed. */ long m_root(dev,dir) int dev; fcookie *dir; { int ret; static first_init = 2; extern FILESYS dummy_filesys; super_info **psblk; psblk = super_ptr+dev; ret=0; if( (kernel->maj_version==0 ) || (kernel->maj_version==1 && kernel->min_version < 5 ) ) { /* the first 2 checks (on A: and B:) we fail automatically */ if (first_init ) { --first_init; return -1; } } /* If not present, see if it's valid */ if( *psblk || ( dev >= 0 && (ret=minix_sanity(dev)) ) ) { if(ret ==-1 || *psblk==DFS ) dir->fs = &dummy_filesys; else { dir->fs=&minix_filesys; /* Aux field tells original device */ dir->aux= dev | AUX_DRV ; dir->index=ROOT_INODE; /* If mounted trace back to root filesystem */ while((*psblk)->mnt_inode) { dev=(*psblk)->mnt_dev; psblk=super_ptr+dev; } } dir->dev=dev; return 0; } return -1; } long m_lookup(dir,name,entry) fcookie *dir; char *name; fcookie *entry; { if(!*name) { *entry=*dir; entry->aux=0; return 0; } if(dir->index==ROOT_INODE && !strcmp(name,"..")) { *entry=*dir; /* If mounted treat as a lookup from mount point */ if( cross_mount(entry) ) return EMOUNT; DEBUG("m_lookup: crossing mount point"); entry->index = search_dir(name,entry->index,entry->dev,FIND); if(entry->index < 0 ) return entry->index; entry->aux=0; return 0; } entry->index=search_dir(name,dir->index,dir->dev,FIND); entry->dev=dir->dev; if(entry->index < 0 ) return entry->index ; entry->aux=0; entry->fs=&minix_filesys; if( check_mount(entry) ) DEBUG("Crossed mount point"); return 0; } long m_creat(dir,name,mode,attr,entry) fcookie *dir; char *name; unsigned mode; int attr; fcookie *entry; { long pos; d_inode ripnew; unshort newfile; char *ext; /* 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,dir->dev) && ( 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=Geteuid(); ripnew.i_gid=Getegid(); 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); if(cache_mode) l_sync(); entry->fs = dir->fs; entry->dev = dir->dev; entry->index=newfile; entry->aux=0; return 0; } DEVDRV * m_getdev(file,special) fcookie *file; long *special; { return(&minix_dev); } long m_getxattr(file,xattr) fcookie *file; XATTR *xattr; { d_inode rip; long time_tmp; long extra; super_info *psblk; psblk=super_ptr[file->dev]; read_inode(file->index,&rip,file->dev); /* Minix and gcc use different values for FIFO's */ if((rip.i_mode & I_TYPE) == I_NAMED_PIPE) xattr->mode = S_IFIFO | (rip.i_mode & ALL_MODES); else xattr->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) xattr->mode = S_IFLNK | (rip.i_mode & ALL_MODES); #endif /* Fake attr field a bit , to keep TOS happy */ if(IS_DIR(rip))xattr->attr=FA_DIR; else xattr->attr=(rip.i_mode & 0222) ? 0 : FA_RDONLY; xattr->index=file->index; xattr->dev=file->dev; /* Char and block special files need major/minor device nos filled in */ if(IM_SPEC(rip.i_mode)) xattr->rdev=rip.i_zone[0]; else xattr->rdev=0; xattr->nlink=rip.i_nlinks; xattr->uid=rip.i_uid; xattr->gid=rip.i_gid; xattr->size=rip.i_size; xattr->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) */ xattr->nblocks = (xattr->size + (BLOCK_SIZE-1)) / BLOCK_SIZE; extra = 0; if (xattr->nblocks > psblk->dzpi) extra++; /* correct for the indirection block */ if (xattr->nblocks > psblk->ndbl) { extra++; /* correct for double indirection block */ extra += (xattr->nblocks - psblk->ndbl) / psblk->zpind; /* and single indirection blocks */ } if (xattr->nblocks > psblk->ndbl + (long) psblk->zpind * psblk->zpind) { extra++; /* correct for triple indir block */ /* and double indirection blocks */ extra += ((xattr->nblocks - psblk->ndbl - (long) psblk->zpind * psblk->zpind) / ((long) psblk->zpind * psblk->zpind)); } xattr->nblocks += extra; time_tmp=Dostime(_corr(rip.i_mtime)); xattr->mtime=time_tmp >> 16; xattr->mdate=time_tmp & (0xffff); time_tmp=Dostime(_corr(rip.i_atime)); xattr->atime=time_tmp >> 16; xattr->adate=time_tmp & (0xffff); time_tmp=Dostime(_corr(rip.i_ctime)); xattr->ctime=time_tmp >> 16; xattr->cdate=time_tmp & (0xffff); xattr->reserved2=0; xattr->reserved3[0]=0; xattr->reserved3[1]=0; return 0; } long m_chown(file, uid , gid) fcookie *file; int uid,gid; { d_inode rip; read_inode(file->index,&rip,file->dev); if(uid!=-1)rip.i_uid=uid; if(gid!=-1)rip.i_gid=gid; rip.i_ctime=Unixtime(Timestamp(),Datestamp()); write_inode(file->index,&rip,file->dev); if(cache_mode) l_sync(); return 0; } long m_chmode(file, mode) fcookie *file; unsigned mode; { d_inode rip; super_info *psblk=super_ptr[file->dev]; read_inode(file->index,&rip,file->dev); rip.i_mode=(rip.i_mode & I_TYPE)|(mode & ALL_MODES); if(psblk->version)rip.i_ctime=Unixtime(Timestamp(),Datestamp()); write_inode(file->index,&rip,file->dev); if(cache_mode) l_sync(); return 0; } long m_mkdir(dir,name,mode) fcookie *dir; char *name; unsigned mode; { unshort newdir; d_inode rip,ripnew; long 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=Geteuid(); ripnew.i_gid=Getegid(); 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,(long)(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); if(cache_mode) l_sync(); return EACCDN; } rip.i_nlinks++; write_inode(dir->index,&rip,dir->dev); l_write(dir->index,pos,2L,&newdir,dir->dev); if(cache_mode) l_sync(); return(0); } long m_rmdir(dir,name) fcookie *dir; char *name; { long chunk,left; long inum; int i,incr; super_info *psblk; d_inode rip,rip2; if((inum=search_dir(name,dir->index,dir->dev,FIND))<0)return inum; /* Is anything mounted on this dir ? */ for(psblk=super_ptr[dir->dev]->mnt_first;psblk;psblk=psblk->mnt_next) if(psblk->mnt_inode==inum) { DEBUG("m_rmdir: can't delete mount point"); return EACCDN; } 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;idev,1)) { 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); if( lpath && (ldir.dev==dir->dev) && (ldir.index==inum) ) { Kfree(lpath); lpath=0; } if(cache_mode) l_sync(); return(0); } /* Unix-like unlink ... handle regulars, symlinks and specials. * */ long m_remove(dir,name) fcookie *dir; char *name; { long inum,ret; char spec; /* Special file */ 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) && !IS_SYM(rip) ) { if(!IM_SPEC(rip.i_mode)) return EACCDN; spec=1; } else spec=0; if((ret=search_dir(name,dir->index,dir->dev,KILL))<0) return ret; if(--rip.i_nlinks==0) { if(spec || !inode_busy(inum,dir->dev,1)) /* Is inode busy ? */ { if(!spec) trunc_inode(&rip,dir->dev,0L,0); rip.i_mode=0; free_inode(inum,dir->dev); } } write_inode(inum,&rip,dir->dev); if(cache_mode) l_sync(); 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 */ long m_getname(root,dir,pathname,length) fcookie *root,*dir; char *pathname; short length; { long inum,pinum; unsigned dev; int chunk; long left; int incr; short plength; super_info *psblk; psblk=super_ptr[dir->dev]; if(no_length) length=PATH_MAX; if(lpath && lroot.dev==root->dev && lroot.index==root->index && ldir.dev==dir->dev && ldir.index==dir->index) { TRACE("m_getname: cache hit"); if(length <= llength) return ENAMETOOLONG; strcpy(pathname,lpath); return 0; } *pathname=0; if( dir->dev==root->dev && dir->index==root->index) return 0; incr=psblk->increment; inum=dir->index; dev=dir->dev; plength=0; if(inum==ROOT_INODE && psblk->mnt_inode) { dev=psblk->mnt_dev; inum=psblk->mnt_inode; psblk=super_ptr[dev]; } while( inum!=root->index && inum!= ROOT_INODE ) { d_inode rip; cache *tmp; pinum=search_dir("..",inum,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,dev); for(chunk=0; (left=cnext_zone(&rip,chunk,&tmp,dev)/DIR_ENTRY_SIZE) && inum!=pinum ;chunk++) { char tname[MNAME_MAX+1]; int i; for(i=0;ibuffer->bdir[i].d_inum==inum) { strncpy(tname,tmp->buffer->bdir[i].d_name,MMAX_FNAME(incr)); tname[MMAX_FNAME(incr)]=0; strrev(tname); plength+=strlen(tname)+1; if(length <= plength) return ENAMETOOLONG; strcat(pathname,tname); strcat(pathname,"\\"); inum=pinum; } } if(left==0 && inum!=pinum) { ALERT("m_getname inode %d orphaned or bad ..",inum); return EINTRN; } /* Cross mount point if possible */ if(inum==ROOT_INODE && psblk->mnt_inode) { dev=psblk->mnt_dev; inum=psblk->mnt_inode; psblk=super_ptr[dev]; } } 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); llength=plength; } lroot=*root; ldir=*dir; return 0; } long m_opendir(dirh,flag) DIR *dirh; int flag; { dirh->index=0; return 0; } long m_readdir(dirh,name,namelen,fc) DIR *dirh; char *name; int namelen; fcookie *fc; { d_inode rip; cache *tmp; unsigned entry,chunk; super_info *psblk; long limit; int flag,incr; psblk=super_ptr[dirh->fc.dev]; if(dirh->flags) flag=do_trans(DIR_TOS,dirh->fc.dev); 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); incr=psblk->increment; while( (limit=cnext_zone(&rip,chunk,&tmp,dirh->fc.dev)/DIR_ENTRY_SIZE) ) { while( entry < limit) { dir_struct *try=&tmp->buffer->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; /* If turbo mode set atime here: we'll only * change the cache here so it wont cause * lots of I/O */ if( cache_mode==TURBO && super_ptr[dirh->fc.dev]->version && dirh->fc.dev > 1 ) set_atime(&dirh->fc); return 0; } } if(entry!=NR_DIR_ENTRIES)return ENMFIL; else entry=0; chunk++; } return ENMFIL; } long m_rewinddir(dirh) DIR *dirh; { dirh->index=0; return 0; } long m_closedir(dirh) DIR *dirh; { /* Access time is set here if we aren't in TURBO cache mode. Otherwise we * would be sync'ing on every dir read which would be far too slow. See note * in set_atime(). */ if( cache_mode!=TURBO && super_ptr[dirh->fc.dev]->version && dirh->fc.dev > 1 ) { set_atime(&dirh->fc); l_sync(); } dirh->fc.index=0; return 0; } /* Set the atime of a V2 inode for filesystems. There is a snag here: if the * disk is changed then this is likely not to be written out before the whole * cache is invalidated. So we set the status to '3' which means that it is * not alerted if this is dirty when invalidated (hardly the end of the world * if the atime is slightly wrong!) */ void set_atime(fc) fcookie *fc; { d_inode *rip; int *status; rip=get_inode2(fc->index,fc->dev,&status,NOGUESS); rip->i_atime=Unixtime(Timestamp(),Datestamp()); if(*status!=2) *status=3; } long m_rlabel(dir,name,namelen) fcookie *dir; char *name; int namelen; { return EFILNF; } long m_wlabel(dir,name) fcookie *dir; char *name; { return EACCDN; } 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,buffer[1]+1)+1; buffer[2]=512L; buffer[3]=2L; return(0); } 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,gid,id; d_inode rip; extern long init_addr; uid = Geteuid(); gid = Getegid(); switch(cmd) { case MFS_VERIFY: *((long *)arg)=MFS_MAGIC; return 0; /* Sync the filesystem */ case MFS_SYNC: TRACE("Done l_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); return 0; case MFS_GTRANS: *((long *) arg)=fs_mode[dir->dev]; return 0; case MFS_STRANS: if(uid) return EACCDN; fs_mode[dir->dev]=*((long *)arg); return 0; case MFS_PHYS: *((struct phys_part *)arg)=ppart[dir->dev]; return 0; case MFS_IADDR: *((long *)arg)=(long)&init_addr; return 0; case MFS_UPDATE: if(cache_mode!=TURBO ) return -1; switch(arg) { case 0: return update_suspend; case 1: TRACE("Minixfs: update suspended"); update_suspend=1; return 0; case 2: TRACE("Minixfs: update restarted"); update_suspend=0; return 0; case 3: if(Addroottimeout) return -1; return update_pid; default: return EINVFN; } /* Mounting and umounting. * This basically involves cookie translation when crossing the mount point. * However this presents an interesting problem. Suppose E:\usr has D: mounted * on it. If we are in directory E:\usr\ we are in the root directory of D:. * If we do a cd \ then we get sent back to the root cookie directory of D:, * which we want to be E:. The only way to change the root cookie at present is * to force a disk change, this is no problem. * The fun starts when we want to umount D:. Changing D: will have no effect * because the root cookie of D: is now on E: and since E: hasn't changed the * root cookie on D: wont change either. If we change E: then we will get the * root cookie of D: changed but we also change E: as well. * The only way out is to fiddle the aux field of the root cookie so it shows * the original device. The cookie from m_root is copied with the dup_cookie * function, if we kludge this at umount time to read the aux field and copy * the original device the root cookies should be set back. This relies very * heavily on the structure of filesys.c in MiNT, but it's better than nothing. * Repeat after me: KLUDGE, KLUDGE, KLUDGE, KLUDGE, KLUDGE!!! */ case MFS_MOUNT: { char tpath[]="A:"; fcookie fc; unsigned dev; super_info *mptr; if(uid) return EACCDN; /* Lookup the entry */ if( (inum=m_lookup(dir,name,&fc)) ) return inum; read_inode(fc.index,&rip,fc.dev); /* Must be a directory */ if(!IS_DIR(rip)) { DEBUG("MFS_MOUNT: not a directory!"); return EACCDN; } if(fc.index==ROOT_INODE) { DEBUG("MFS_MOUNT: can't mount on root!"); return EACCDN; } if(!arg) return EINVFN; dev = ( ( mfs_mount *) arg)->dev ; if(fc.dev==dev) { DEBUG("MFS_MOUNT: can't mount on self!"); return EACCDN; } tpath[0]='A'+dev; /* Sync filesystem (and force access if unrecognised) */ d_cntl(MFS_SYNC,tpath,0l); psblk=super_ptr[dev]; if(!psblk || psblk==DFS) { DEBUG("MFS_MOUNT: not a Minixfs filesystem!"); return EACCDN; } if(psblk->mnt_inode) { DEBUG("MFS_MOUNT: filesystem already mounted!"); return EACCDN; } if(psblk->mnt_first) { DEBUG("MFS_MOUNT: filesystem has others mounted!"); return EACCDN; } for(mptr=super_ptr[fc.dev]->mnt_first; mptr;mptr=mptr->mnt_next) { if( mptr->mnt_inode==fc.index ) { DEBUG("MFS_MOUNT: inode already mounted on!"); return EACCDN; } } mptr=super_ptr[fc.dev]; psblk->mnt_next=mptr->mnt_first; mptr->mnt_first=psblk; psblk->mnt_dev=fc.dev; psblk->mnt_inode=fc.index; psblk->mnt_flags |= MNT_CHANGE; /* Force change */ d_lock(1,dev); d_lock(0,dev); d_cntl(MFS_SYNC,tpath,0l); psblk->mnt_flags &= ~MNT_CHANGE; return 0; } case MFS_UMOUNT: { fcookie fc; super_info **pptr,*pdev; char tpath[]="A:"; if(uid) return EACCDN; /* Lookup path */ if( ( inum=m_lookup(dir,name,&fc) ) ) return inum; psblk=super_ptr[fc.dev]; /* Can't have other devices mounted */ if(psblk->mnt_first) { DEBUG("MFS_UMOUNT: device busy"); return EACCDN; } if(!psblk->mnt_inode) { DEBUG("MFS_UMOUNT: not mounted"); return EACCDN; } /* Unlink from parent filesystem list */ for(pptr=&super_ptr[psblk->mnt_dev]->mnt_first;*pptr; pptr=&(*pptr)->mnt_next ) { if(*pptr==psblk) { *pptr = psblk->mnt_next; break; } } pdev=psblk; /* Find root device */ while(pdev->mnt_inode) pdev = super_ptr[pdev->mnt_dev]; psblk->mnt_inode=0; pdev->mnt_flags |= MNT_CHANGE; /* Make dupcookie restore dev fields */ restore_dev = psblk->dev; /* Change root device */ d_lock(1,pdev->dev); d_lock(0,pdev->dev); tpath[0] = 'A'+pdev->dev; d_cntl(MFS_SYNC,tpath,0l); restore_dev = -1; pdev->mnt_flags &= ~MNT_CHANGE; return 0; } case FUTIME: case FTRUNCATE: { fcookie fc; read_inode(dir->index,&rip,dir->dev); /* Have we got 'x' access for current dir ? */ if (check_mode(uid,gid,&rip,S_IXUSR)) return EACCDN; /* Lookup the entry */ if( (inum=m_lookup(dir,name,&fc)) ) return inum; read_inode(fc.index,&rip,fc.dev); if(cmd==FUTIME) { short *timeptr = (short *) arg; /* The owner or super-user can always touch, others only if timeptr == 0 and write permission. */ if (uid && uid != rip.i_uid && (timeptr || check_mode (uid, gid, &rip, S_IWUSR))) return EACCDN; rip.i_ctime = Unixtime(Timestamp (), Datestamp ()); if(timeptr) { rip.i_atime = Unixtime(timeptr[0], timeptr[1]); rip.i_mtime = Unixtime(timeptr[2], timeptr[3]); } else rip.i_atime = rip.i_mtime = rip.i_ctime; write_inode (fc.index, &rip, fc.dev); if (cache_mode != TURBO) l_sync(); return 0; } if(!IS_REG(rip)) return EACCDN; /* Need write access as well */ if (check_mode(uid, gid, &rip, S_IWUSR)) return EACCDN; itruncate(fc.index,fc.dev,*((long *)arg)); if (cache_mode != TURBO) l_sync (); return 0; } case MFS_LOPEN: { long fcount; openf_list *flist = (openf_list *) arg; fcount=0; inum=0; for(f=firstptr;f;f=f->next) { /* If same file or wrong device, skip */ if( f->fc.dev!=dir->dev || f->fc.index==inum) continue; inum=f->fc.index; flist->flist[fcount++]=inum; if(fcount==flist->limit) return ERANGE; } flist->flist[fcount]=0; } return 0; case MFS_MKNOD: { long pos; unsigned inm,mode; if(uid) return EACCDN; mode = arg & 0xffff; /* Char and block specials only at present */ if(!IM_SPEC(mode))return ERANGE; /* Create new name */ pos=search_dir(name,dir->index,dir->dev,ADD); if(pos < 0) return pos; inm=alloc_inode(dir->dev); if(!inm) return EWRITF; bzero(&rip,sizeof(d_inode)); rip.i_mode = mode; rip.i_uid = uid; rip.i_gid = gid; rip.i_nlinks = 1; rip.i_mtime=Unixtime(Timestamp(), Datestamp()); rip.i_atime=rip.i_mtime; rip.i_ctime=rip.i_mtime; rip.i_zone[0]= arg >> 16; write_inode(inm,&rip,dir->dev); l_write(dir->index,pos,2L,&inm,dir->dev); if(cache_mode) l_sync(); } return 0; default: return EINVFN; } } /* 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. */ long m_rename(olddir,oldname,newdir,newname) fcookie *olddir; char *oldname; fcookie *newdir; char *newname; { long finode,ret; d_inode rip; long 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 (lpath && ldir.dev == olddir->dev && (ldir.index == finode || (dirmove && is_parent (ldir.index, finode, olddir->dev) > 0))) { 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; { unshort ino = finode; l_write (newdir->index, pos, 2L, &ino, 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*super_ptr[newdir->dev]->increment) ALERT("m_rename: Unexpected .. position in inode %ld",finode); { unshort ino = newdir->index; l_write (finode, pos, 2L, &ino, 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); } if(cache_mode) l_sync(); /* 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 (lpath && ldir.dev == olddir->dev && (ldir.index == finode || (dirren && is_parent (ldir.index, finode, olddir->dev) > 0))) { Kfree (lpath); lpath=0; } return 0; } /* Minix hard-link, you can't make a hardlink to a directory ... it causes * too much trouble, use symbolic links instead. */ long m_hardlink(fromdir,fromname,todir,toname) fcookie *fromdir; char *fromname; fcookie *todir; char *toname; { long finode; d_inode rip; long 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) && !IM_SPEC(rip.i_mode)) || (rip.i_nlinks >=MINIX_MAX_LINK) ) return EACCDN; /* Create new entry */ if((pos=search_dir(toname,todir->index,todir->dev,ADD))<0) return pos; { unshort ino = finode; l_write (todir->index, pos, 2L, &ino, todir->dev); } rip.i_nlinks++; rip.i_ctime=Unixtime(Timestamp(),Datestamp()); write_inode(finode,&rip,fromdir->dev); if(cache_mode) l_sync(); return 0; } /* Symbolic links ... basically similar to a regular file with one zone */ long m_symlink(dir,name,to) fcookie *dir; char *name; char *to; { d_inode rip; long pos; unshort newinode; if(!*to) { DEBUG("m_symlink: invalid null filename"); return EACCDN; } /* Strip U: prefix */ if ((to[0] == 'u' || to[0] == 'U') && to[1] == ':' && to[2] == '\\') to += 2; 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); rip.i_uid=Geteuid(); rip.i_gid=Getegid(); 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); if(cache_mode) l_sync(); return 0; } long m_readlink(file,buf,len) fcookie *file; char *buf; int len; { long 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 (temp.bdata[0] == '/' && len > 2) { *buf++ = 'u'; *buf++ = ':'; len -= 2; } if(stob_ncpy(buf, (char *) &temp,len)) { DEBUG("m_readlink: name too long"); return ERANGE; } TRACE("m_readlink returned %s",buf); return 0; } /* the only settable attribute is FA_RDONLY; if the bit is set, * the mode is changed so that no write permission exists */ long m_chattr(file,attr) fcookie *file; int attr; { long 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); if(cache_mode) 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); if(cache_mode) l_sync(); } } return 0; } 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 UNLIMITED; /* At last ! */ 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; case DP_CASE: return DP_CASESENS; /* Well sort of ... */ default: return EINVFN; } } long m_release(fc) fcookie *fc; { return 0; } long m_dupcookie(dest,src) fcookie *dest,*src; { unsigned tmpaux; tmpaux=dest->aux; *dest=*src; if(restore_dev!=-1 && (tmpaux & (AUX_DEV|AUX_DRV))== restore_dev|AUX_DRV ) dest->dev = tmpaux & AUX_DEV; return 0; }