439 lines
8.9 KiB
C
439 lines
8.9 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)ufs_inode.c 1.1 94/10/31 Copyr 1986 Sun Micro";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1986 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
#include "boot/systm.h"
|
|
#include <sys/user.h>
|
|
#include <sys/vfs.h>
|
|
#include "boot/vnode.h"
|
|
#include <sys/buf.h>
|
|
#include <sys/kernel.h>
|
|
#include "boot/cmap.h"
|
|
#include <ufs/mount.h>
|
|
#include "boot/inode.h"
|
|
#include <ufs/fs.h>
|
|
#ifdef QUOTA
|
|
#include <ufs/quota.h>
|
|
#endif
|
|
|
|
#ifdef NFS_BOOT
|
|
#undef u
|
|
extern struct user u;
|
|
/*
|
|
* Small hash list for /boot.
|
|
*/
|
|
#define INOHSZ 1
|
|
/*
|
|
* Small inode list also. Note: the kernel one is set up
|
|
* by config; we just set ours up statically.
|
|
*/
|
|
#define NINODES 10
|
|
struct inode inodes[NINODES];
|
|
struct inode *inode = &inodes[0];
|
|
struct inode *inodeNINODE = &inodes[NINODES-1];
|
|
int ninode = NINODES;
|
|
#else
|
|
#define INOHSZ 64
|
|
#endif /* NFS_BOOT */
|
|
#define INOHASH(dev,ino) (((unsigned)((dev)+(ino)))%INOHSZ)
|
|
|
|
union ihead { /* inode LRU cache, Chris Maltby */
|
|
union ihead *ih_head[2];
|
|
struct inode *ih_chain[2];
|
|
} ihead[INOHSZ];
|
|
|
|
struct inode *ifreeh, **ifreet;
|
|
|
|
/*
|
|
* Convert inode formats to vnode types
|
|
*/
|
|
enum vtype iftovt_tab[] = {
|
|
VFIFO, VCHR, VDIR, VBLK, VREG, VLNK, VSOCK, VBAD
|
|
};
|
|
|
|
int vttoif_tab[] = {
|
|
0, IFREG, IFDIR, IFBLK, IFCHR, IFLNK, IFSOCK, IFMT, IFIFO
|
|
};
|
|
|
|
/*
|
|
* Initialize hash links for inodes
|
|
* and build inode free list.
|
|
*/
|
|
ihinit()
|
|
{
|
|
register int i;
|
|
register struct inode *ip = inode;
|
|
register union ihead *ih = ihead;
|
|
|
|
for (i = INOHSZ; --i >= 0; ih++) {
|
|
ih->ih_head[0] = ih;
|
|
ih->ih_head[1] = ih;
|
|
}
|
|
ifreeh = ip;
|
|
ifreet = &ip->i_freef;
|
|
ip->i_freeb = &ifreeh;
|
|
ip->i_forw = ip;
|
|
ip->i_back = ip;
|
|
ip->i_vnode.v_data = (caddr_t)ip;
|
|
ip->i_vnode.v_op = &ufs_vnodeops;
|
|
for (i = ninode; --i > 0; ) {
|
|
++ip;
|
|
ip->i_forw = ip;
|
|
ip->i_back = ip;
|
|
*ifreet = ip;
|
|
ip->i_freeb = ifreet;
|
|
ifreet = &ip->i_freef;
|
|
ip->i_vnode.v_data = (caddr_t)ip;
|
|
ip->i_vnode.v_op = &ufs_vnodeops;
|
|
}
|
|
ip->i_freef = NULL;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Look up an inode by device,inumber.
|
|
* If it is in core (in the inode structure),
|
|
* honor the locking protocol.
|
|
* If it is not in core, read it in from the
|
|
* specified device.
|
|
* If the inode is mounted on, perform
|
|
* the indicated indirection.
|
|
* In all cases, a pointer to a locked
|
|
* inode structure is returned.
|
|
*
|
|
* panic: no imt -- if the mounted file
|
|
* system is not in the mount table.
|
|
* "cannot happen"
|
|
*/
|
|
struct inode *
|
|
iget(dev, fs, ino)
|
|
dev_t dev;
|
|
register struct fs *fs;
|
|
ino_t ino;
|
|
{
|
|
register struct inode *ip;
|
|
register union ihead *ih;
|
|
register struct buf *bp;
|
|
register struct dinode *dp;
|
|
register struct inode *iq;
|
|
register struct vnode *vp;
|
|
struct mount *mp;
|
|
|
|
/*
|
|
* lookup inode in cache
|
|
*/
|
|
loop:
|
|
mp = getmp(dev);
|
|
if (mp == NULL) {
|
|
panic("iget: bad dev\n");
|
|
}
|
|
if (mp->m_bufp->b_un.b_fs != fs)
|
|
panic("iget: bad fs");
|
|
ih = &ihead[INOHASH(dev, ino)];
|
|
for (ip = ih->ih_chain[0]; ip != (struct inode *)ih; ip = ip->i_forw)
|
|
if (ino == ip->i_number && dev == ip->i_dev) {
|
|
/*
|
|
* found it. check for locks
|
|
*/
|
|
if ((ip->i_flag & ILOCKED) != 0) {
|
|
ip->i_flag |= IWANT;
|
|
(void) sleep((caddr_t)ip, PINOD);
|
|
goto loop;
|
|
}
|
|
/*
|
|
* If inode is on free list, remove it.
|
|
*/
|
|
if ((ip->i_flag & IREF) == 0) {
|
|
if (iq = ip->i_freef)
|
|
iq->i_freeb = ip->i_freeb;
|
|
else
|
|
ifreet = ip->i_freeb;
|
|
*ip->i_freeb = iq;
|
|
ip->i_freef = NULL;
|
|
ip->i_freeb = NULL;
|
|
}
|
|
/*
|
|
* mark inode locked and referenced and return it.
|
|
*/
|
|
ip->i_flag |= ILOCKED | IREF;
|
|
VN_HOLD(ITOV(ip));
|
|
return(ip);
|
|
}
|
|
|
|
/*
|
|
* Inode was not in cache. Get free inode slot for new inode.
|
|
*/
|
|
while (ifreeh == NULL) {
|
|
}
|
|
if ((ip = ifreeh) == NULL) {
|
|
tablefull("inode");
|
|
u.u_error = ENFILE;
|
|
return(NULL);
|
|
}
|
|
if (iq = ip->i_freef)
|
|
iq->i_freeb = &ifreeh;
|
|
ifreeh = iq;
|
|
ip->i_freef = NULL;
|
|
ip->i_freeb = NULL;
|
|
/*
|
|
* Now to take inode off the hash chain it was on
|
|
* (initially, or after an iflush, it is on a "hash chain"
|
|
* consisting entirely of itself, and pointed to by no-one,
|
|
* but that doesn't matter), and put it on the chain for
|
|
* its new (ino, dev) pair
|
|
*/
|
|
remque(ip);
|
|
insque(ip, ih);
|
|
#ifdef QUOTA
|
|
dqrele(ip->i_dquot);
|
|
ip->i_dquot = NULL;
|
|
#endif
|
|
ip->i_flag = ILOCKED | IREF;
|
|
ip->i_dev = dev;
|
|
ip->i_devvp = mp->m_devvp;
|
|
ip->i_number = ino;
|
|
ip->i_diroff = 0;
|
|
ip->i_fs = fs;
|
|
ip->i_lastr = 0;
|
|
bp = bread(ip->i_devvp, (daddr_t)fsbtodb(fs, itod(fs, ino)),
|
|
(int)fs->fs_bsize);
|
|
/*
|
|
* Check I/O errors
|
|
*/
|
|
if ((bp->b_flags & B_ERROR) != 0) {
|
|
brelse(bp);
|
|
/*
|
|
* the inode doesn't contain anything useful, so it would
|
|
* be misleading to leave it on its hash chain.
|
|
* 'iput' will take care of putting it back on the free list.
|
|
*/
|
|
remque(ip);
|
|
ip->i_forw = ip;
|
|
ip->i_back = ip;
|
|
/*
|
|
* we also loose its inumber, just in case (as iput
|
|
* doesn't do that any more) - but as it isn't on its
|
|
* hash chain, I doubt if this is really necessary .. kre
|
|
* (probably the two methods are interchangable)
|
|
*/
|
|
ip->i_number = 0;
|
|
iunlock(ip);
|
|
iinactive(ip);
|
|
return(NULL);
|
|
}
|
|
dp = bp->b_un.b_dino;
|
|
dp += itoo(fs, ino);
|
|
ip->i_ic = dp->di_ic;
|
|
vp = ITOV(ip);
|
|
VN_INIT(vp, mp->m_vfsp, IFTOVT(ip->i_mode), ip->i_rdev);
|
|
if (ino == (ino_t)ROOTINO) {
|
|
vp->v_flag |= VROOT;
|
|
}
|
|
brelse(bp);
|
|
#ifdef QUOTA
|
|
if (ip->i_mode != 0)
|
|
ip->i_dquot = getinoquota(ip);
|
|
#endif
|
|
return (ip);
|
|
}
|
|
|
|
|
|
/*
|
|
* Vnode is no loger referenced, write the inode out and if necessary,
|
|
* truncate and deallocate the file.
|
|
*/
|
|
iinactive(ip)
|
|
register struct inode *ip;
|
|
{
|
|
|
|
if (ip->i_flag & ILOCKED)
|
|
panic("ufs_inactive");
|
|
if (ip->i_fs->fs_ronly == 0) {
|
|
#ifdef NFS_BOOT
|
|
/*
|
|
* When booting all inodes are read only.
|
|
*/
|
|
#else
|
|
ip->i_flag |= ILOCKED;
|
|
if (ip->i_nlink <= 0) {
|
|
ip->i_gen++;
|
|
itrunc(ip, (u_long)0);
|
|
mode = ip->i_mode;
|
|
ip->i_mode = 0;
|
|
ip->i_rdev = 0;
|
|
imark(ip, IUPD|ICHG);
|
|
ifree(ip, ip->i_number, mode);
|
|
#ifdef QUOTA
|
|
(void)chkiq(VFSTOM(ip->i_vnode.v_vfsp),
|
|
ip, ip->i_uid, 0);
|
|
dqrele(ip->i_dquot);
|
|
ip->i_dquot = NULL;
|
|
#endif
|
|
}
|
|
if (ip->i_flag & (IUPD|IACC|ICHG))
|
|
iupdat(ip, 0);
|
|
iunlock(ip);
|
|
#endif /* NFS_BOOT */
|
|
}
|
|
ip->i_flag = 0;
|
|
/*
|
|
* Put the inode on the end of the free list.
|
|
* Possibly in some cases it would be better to
|
|
* put the inode at the head of the free list,
|
|
* (eg: where i_mode == 0 || i_number == 0)
|
|
* but I will think about that later .. kre
|
|
* (i_number is rarely 0 - only after an i/o error in iget,
|
|
* where i_mode == 0, the inode will probably be wanted
|
|
* again soon for an ialloc, so possibly we should keep it)
|
|
*/
|
|
if (ifreeh) {
|
|
*ifreet = ip;
|
|
ip->i_freeb = ifreet;
|
|
} else {
|
|
ifreeh = ip;
|
|
ip->i_freeb = &ifreeh;
|
|
}
|
|
ip->i_freef = NULL;
|
|
ifreet = &ip->i_freef;
|
|
}
|
|
|
|
|
|
/*
|
|
* Mark the accessed, updated, or changed times in an inode
|
|
* with the current (unique) time
|
|
*/
|
|
imark(ip, flag)
|
|
register struct inode *ip;
|
|
register int flag;
|
|
{
|
|
struct timeval ut;
|
|
|
|
#ifdef NFS_BOOT
|
|
ut.tv_sec = 0;
|
|
ut.tv_usec = 0;
|
|
#else
|
|
uniqtime(&ut);
|
|
#endif /* NFS_BOOT */
|
|
ip->i_flag |= flag;
|
|
if (flag & IACC)
|
|
ip->i_atime = ut;
|
|
if (flag & IUPD)
|
|
ip->i_mtime = ut;
|
|
if (flag & ICHG) {
|
|
ip->i_diroff = 0;
|
|
ip->i_ctime = ut;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Lock an inode. If its already locked, set the WANT bit and sleep.
|
|
*/
|
|
ilock(ip)
|
|
register struct inode *ip;
|
|
{
|
|
|
|
ILOCK(ip);
|
|
}
|
|
|
|
/*
|
|
* Unlock an inode. If WANT bit is on, wakeup.
|
|
*/
|
|
iunlock(ip)
|
|
register struct inode *ip;
|
|
{
|
|
|
|
if (!(ip->i_flag & ILOCKED)) {
|
|
panic("iunlock");
|
|
}
|
|
IUNLOCK(ip);
|
|
}
|
|
|
|
|
|
/*
|
|
* Check mode permission on inode.
|
|
* Mode is READ, WRITE or EXEC.
|
|
* In the case of WRITE, the
|
|
* read-only status of the file
|
|
* system is checked.
|
|
* Also in WRITE, prototype text
|
|
* segments cannot be written.
|
|
* The mode is shifted to select
|
|
* the owner/group/other fields.
|
|
* The super user is granted all
|
|
* permissions.
|
|
*/
|
|
iaccess(ip, m)
|
|
register struct inode *ip;
|
|
register int m;
|
|
{
|
|
register int *gp;
|
|
|
|
if (m & IWRITE) {
|
|
register struct vnode *vp;
|
|
|
|
vp = ITOV(ip);
|
|
/*
|
|
* Disallow write attempts on read-only
|
|
* file systems; unless the file is a block
|
|
* or character device resident on the
|
|
* file system.
|
|
*/
|
|
if (ip->i_fs->fs_ronly != 0) {
|
|
if ((ip->i_mode & IFMT) != IFCHR &&
|
|
(ip->i_mode & IFMT) != IFBLK) {
|
|
u.u_error = EROFS;
|
|
return (EROFS);
|
|
}
|
|
}
|
|
/*
|
|
* If there's shared text associated with
|
|
* the inode, try to free it up once. If
|
|
* we fail, we can't allow writing.
|
|
*/
|
|
if (vp->v_flag & VTEXT)
|
|
xrele(vp);
|
|
if (vp->v_flag & VTEXT) {
|
|
u.u_error = ETXTBSY;
|
|
return (ETXTBSY);
|
|
}
|
|
}
|
|
/*
|
|
* If you're the super-user,
|
|
* you always get access.
|
|
*/
|
|
if (u.u_uid == 0)
|
|
return (0);
|
|
/*
|
|
* Access check is based on only
|
|
* one of owner, group, public.
|
|
* If not owner, then check group.
|
|
* If not a member of the group, then
|
|
* check public access.
|
|
*/
|
|
if (u.u_uid != ip->i_uid) {
|
|
m >>= 3;
|
|
if (u.u_gid == ip->i_gid)
|
|
goto found;
|
|
gp = u.u_groups;
|
|
for (; gp < &u.u_groups[NGROUPS] && *gp != NOGROUP; gp++)
|
|
if (ip->i_gid == *gp)
|
|
goto found;
|
|
m >>= 3;
|
|
}
|
|
found:
|
|
if ((ip->i_mode & m) == m)
|
|
return (0);
|
|
u.u_error = EACCES;
|
|
return (EACCES);
|
|
}
|
|
|