* Copyright (c) 1982, 1986, 1989 Regents of the University of California.
* %sccs.include.redist.c%
* @(#)ufs_vnops.c 7.109 (Berkeley) %G%
#include <sys/resourcevar.h>
#include <miscfs/specfs/specdev.h>
#include <ufs/ufs/lockf.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
static int ufs_chmod
__P((struct vnode
*, int, struct ucred
*, struct proc
*));
__P((struct vnode
*, uid_t
, gid_t
, struct ucred
*, struct proc
*));
#define SETHIGH(q, h) { \
tmp.val[_QUAD_HIGHWORD] = (h); \
tmp.val[_QUAD_LOWWORD] = (l); \
struct vop_create_args
/* {
struct componentname *a_cnp;
ufs_makeinode(MAKEIMODE(ap
->a_vap
->va_type
, ap
->a_vap
->va_mode
),
ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
))
struct vop_mknod_args
/* {
struct componentname *a_cnp;
register struct vattr
*vap
= ap
->a_vap
;
register struct vnode
**vpp
= ap
->a_vpp
;
register struct inode
*ip
;
ufs_makeinode(MAKEIMODE(vap
->va_type
, vap
->va_mode
),
ap
->a_dvp
, vpp
, ap
->a_cnp
))
ip
->i_flag
|= IACC
|IUPD
|ICHG
;
if (vap
->va_rdev
!= VNOVAL
) {
* Want to be able to use this to make badblock
* inodes, so don't truncate the dev number.
ip
->i_rdev
= vap
->va_rdev
;
* Remove inode so that it will be reloaded by iget and
* checked to see if it is an alias of an existing entry
struct vop_open_args
/* {
* Update the times on the inode.
struct vop_close_args
/* {
register struct vnode
*vp
= ap
->a_vp
;
register struct inode
*ip
= VTOI(vp
);
if (vp
->v_usecount
> 1 && !(ip
->i_flag
& ILOCKED
))
ITIMES(ip
, &time
, &time
);
* Check mode permission on inode pointer. Mode is READ, WRITE or EXEC.
* The mode is shifted to select the owner/group/other fields. The
* super user is granted all permissions.
struct vop_access_args
/* {
register struct vnode
*vp
= ap
->a_vp
;
register struct inode
*ip
= VTOI(vp
);
register struct ucred
*cred
= ap
->a_cred
;
mode_t mode
= ap
->a_mode
;
vprint("ufs_access: not locked", vp
);
panic("ufs_access: not locked");
case VREG
: case VDIR
: case VLNK
:
if (error
= getinoquota(ip
))
* If you're the super-user, you always get access.
* 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 (cred
->cr_uid
!= ip
->i_uid
) {
for (i
= 0; i
< cred
->cr_ngroups
; i
++, gp
++)
if ((ip
->i_mode
& mode
) != 0)
struct vop_getattr_args
/* {
register struct vnode
*vp
= ap
->a_vp
;
register struct inode
*ip
= VTOI(vp
);
register struct vattr
*vap
= ap
->a_vap
;
ITIMES(ip
, &time
, &time
);
vap
->va_fsid
= ip
->i_dev
;
vap
->va_fileid
= ip
->i_number
;
vap
->va_mode
= ip
->i_mode
& ~IFMT
;
vap
->va_nlink
= ip
->i_nlink
;
vap
->va_rdev
= (dev_t
)ip
->i_rdev
;
vap
->va_size
= ip
->i_din
.di_size
;
vap
->va_atime
= ip
->i_atime
;
vap
->va_mtime
= ip
->i_mtime
;
vap
->va_ctime
= ip
->i_ctime
;
vap
->va_flags
= ip
->i_flags
;
/* this doesn't belong here */
vap
->va_blocksize
= BLKDEV_IOSIZE
;
else if (vp
->v_type
== VCHR
)
vap
->va_blocksize
= MAXBSIZE
;
vap
->va_blocksize
= vp
->v_mount
->mnt_stat
.f_iosize
;
vap
->va_bytes
= dbtob(ip
->i_blocks
);
vap
->va_type
= vp
->v_type
;
vap
->va_filerev
= ip
->i_modrev
;
* Set attribute vnode op. called from several syscalls
struct vop_setattr_args
/* {
register struct vattr
*vap
= ap
->a_vap
;
register struct vnode
*vp
= ap
->a_vp
;
register struct inode
*ip
= VTOI(vp
);
register struct ucred
*cred
= ap
->a_cred
;
register struct proc
*p
= ap
->a_p
;
struct timeval atimeval
, mtimeval
;
* Check for unsettable attributes.
if ((vap
->va_type
!= VNON
) || (vap
->va_nlink
!= VNOVAL
) ||
(vap
->va_fsid
!= VNOVAL
) || (vap
->va_fileid
!= VNOVAL
) ||
(vap
->va_blocksize
!= VNOVAL
) || (vap
->va_rdev
!= VNOVAL
) ||
((int)vap
->va_bytes
!= VNOVAL
) || (vap
->va_gen
!= VNOVAL
)) {
* Go through the fields and update iff not VNOVAL.
if (vap
->va_uid
!= (uid_t
)VNOVAL
|| vap
->va_gid
!= (gid_t
)VNOVAL
)
if (error
= ufs_chown(vp
, vap
->va_uid
, vap
->va_gid
, cred
, p
))
if (vap
->va_size
!= VNOVAL
) {
if (error
= VOP_TRUNCATE(vp
, vap
->va_size
, 0, cred
, p
))
if (vap
->va_atime
.ts_sec
!= VNOVAL
|| vap
->va_mtime
.ts_sec
!= VNOVAL
) {
if (cred
->cr_uid
!= ip
->i_uid
&&
(error
= suser(cred
, &p
->p_acflag
)))
if (vap
->va_atime
.ts_sec
!= VNOVAL
)
if (vap
->va_mtime
.ts_sec
!= VNOVAL
)
ip
->i_flag
|= IUPD
| ICHG
;
atimeval
.tv_sec
= vap
->va_atime
.ts_sec
;
atimeval
.tv_usec
= vap
->va_atime
.ts_nsec
/ 1000;
mtimeval
.tv_sec
= vap
->va_mtime
.ts_sec
;
mtimeval
.tv_usec
= vap
->va_mtime
.ts_nsec
/ 1000;
if (error
= VOP_UPDATE(vp
, &atimeval
, &mtimeval
, 1))
if (vap
->va_mode
!= (mode_t
)VNOVAL
)
error
= ufs_chmod(vp
, (int)vap
->va_mode
, cred
, p
);
if (vap
->va_flags
!= VNOVAL
) {
if (cred
->cr_uid
!= ip
->i_uid
&&
(error
= suser(cred
, &p
->p_acflag
)))
ip
->i_flags
= vap
->va_flags
;
ip
->i_flags
&= 0xffff0000;
ip
->i_flags
|= (vap
->va_flags
& 0xffff);
* Change the mode on a file.
* Inode must be locked before calling.
ufs_chmod(vp
, mode
, cred
, p
)
register struct vnode
*vp
;
register struct ucred
*cred
;
register struct inode
*ip
= VTOI(vp
);
if (cred
->cr_uid
!= ip
->i_uid
&&
(error
= suser(cred
, &p
->p_acflag
)))
if (vp
->v_type
!= VDIR
&& (mode
& ISVTX
))
if (!groupmember(ip
->i_gid
, cred
) && (mode
& ISGID
))
ip
->i_mode
|= mode
& 07777;
if ((vp
->v_flag
& VTEXT
) && (ip
->i_mode
& ISVTX
) == 0)
(void) vnode_pager_uncache(vp
);
* Perform chown operation on inode ip;
* inode must be locked prior to call.
ufs_chown(vp
, uid
, gid
, cred
, p
)
register struct vnode
*vp
;
register struct inode
*ip
= VTOI(vp
);
if (uid
== (uid_t
)VNOVAL
)
if (gid
== (gid_t
)VNOVAL
)
* If we don't own the file, are trying to change the owner
* of the file, or are not a member of the target group,
* the caller must be superuser or the call fails.
if ((cred
->cr_uid
!= ip
->i_uid
|| uid
!= ip
->i_uid
||
!groupmember((gid_t
)gid
, cred
)) &&
(error
= suser(cred
, &p
->p_acflag
)))
if (error
= getinoquota(ip
))
dqrele(vp
, ip
->i_dquot
[USRQUOTA
]);
ip
->i_dquot
[USRQUOTA
] = NODQUOT
;
dqrele(vp
, ip
->i_dquot
[GRPQUOTA
]);
ip
->i_dquot
[GRPQUOTA
] = NODQUOT
;
(void) chkdq(ip
, -change
, cred
, CHOWN
);
(void) chkiq(ip
, -1, cred
, CHOWN
);
for (i
= 0; i
< MAXQUOTAS
; i
++) {
dqrele(vp
, ip
->i_dquot
[i
]);
ip
->i_dquot
[i
] = NODQUOT
;
if ((error
= getinoquota(ip
)) == 0) {
dqrele(vp
, ip
->i_dquot
[USRQUOTA
]);
ip
->i_dquot
[USRQUOTA
] = NODQUOT
;
dqrele(vp
, ip
->i_dquot
[GRPQUOTA
]);
ip
->i_dquot
[GRPQUOTA
] = NODQUOT
;
if ((error
= chkdq(ip
, change
, cred
, CHOWN
)) == 0) {
if ((error
= chkiq(ip
, 1, cred
, CHOWN
)) == 0)
(void) chkdq(ip
, -change
, cred
, CHOWN
|FORCE
);
for (i
= 0; i
< MAXQUOTAS
; i
++) {
dqrele(vp
, ip
->i_dquot
[i
]);
ip
->i_dquot
[i
] = NODQUOT
;
if (getinoquota(ip
) == 0) {
dqrele(vp
, ip
->i_dquot
[USRQUOTA
]);
ip
->i_dquot
[USRQUOTA
] = NODQUOT
;
dqrele(vp
, ip
->i_dquot
[GRPQUOTA
]);
ip
->i_dquot
[GRPQUOTA
] = NODQUOT
;
(void) chkdq(ip
, change
, cred
, FORCE
|CHOWN
);
(void) chkiq(ip
, 1, cred
, FORCE
|CHOWN
);
panic("chown: lost quota");
if (ouid
!= uid
|| ogid
!= gid
)
if (ouid
!= uid
&& cred
->cr_uid
!= 0)
if (ogid
!= gid
&& cred
->cr_uid
!= 0)
struct vop_ioctl_args
/* {
struct vop_select_args
/* {
* We should really check to see if I/O is possible.
* NB Currently unsupported.
struct vop_mmap_args
/* {
* Nothing to do, so just return.
struct vop_seek_args
/* {
* Hard to avoid races here, especially
* in unlinking directories.
struct vop_remove_args
/* {
struct componentname *a_cnp;
register struct inode
*ip
, *dp
;
error
= ufs_dirremove(ap
->a_dvp
, ap
->a_cnp
);
struct vop_link_args
/* {
struct componentname *a_cnp;
register struct vnode
*vp
= ap
->a_vp
;
register struct vnode
*tdvp
= ap
->a_tdvp
;
register struct componentname
*cnp
= ap
->a_cnp
;
register struct inode
*ip
;
if (vp
->v_mount
!= tdvp
->v_mount
) {
if ((cnp
->cn_flags
& HASBUF
) == 0)
panic("ufs_link: no name");
if ((nlink_t
)ip
->i_nlink
>= LINK_MAX
) {
free(cnp
->cn_pnbuf
, M_NAMEI
);
error
= VOP_UPDATE(tdvp
, &tv
, &tv
, 1);
error
= ufs_direnter(ip
, vp
, cnp
);
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
* relookup - lookup a path name component
* Used by lookup to re-aquire things.
struct vnode
*dvp
, **vpp
;
struct componentname
*cnp
;
register struct vnode
*dp
= 0; /* the directory we are searching */
struct vnode
*tdp
; /* saved dp */
struct mount
*mp
; /* mount table entry */
int docache
; /* == 0 do not cache last component */
int wantparent
; /* 1 => wantparent or lockparent flag */
int rdonly
; /* lookup read-only flag bit */
char *cp
; /* DEBUG: check name ptr/len */
int newhash
; /* DEBUG: check name hash */
* Setup: break out flag bits into variables.
wantparent
= cnp
->cn_flags
& (LOCKPARENT
|WANTPARENT
);
docache
= (cnp
->cn_flags
& NOCACHE
) ^ NOCACHE
;
if (cnp
->cn_nameiop
== DELETE
||
(wantparent
&& cnp
->cn_nameiop
!= CREATE
))
rdonly
= cnp
->cn_flags
& RDONLY
;
cnp
->cn_flags
&= ~ISSYMLINK
;
* Search a new directory.
* The cn_hash value is for use by vfs_cache.
* The last component of the filename is left accessible via
* cnp->cn_nameptr for callers that need the name. Callers needing
* the name set the SAVENAME flag. When done, they assume
* responsibility for freeing the pathname buffer.
for (newhash
= 0, cp
= cnp
->cn_nameptr
; *cp
!= 0 && *cp
!= '/'; cp
++)
newhash
+= (unsigned char)*cp
;
if (newhash
!= cnp
->cn_hash
)
panic("relookup: bad hash");
if (cnp
->cn_namelen
!= cp
- cnp
->cn_nameptr
)
panic ("relookup: bad len");
panic("relookup: not last component");
printf("{%s}: ", cnp
->cn_nameptr
);
* Check for degenerate name (e.g. / or "")
* which is a way of talking about a directory,
if (cnp
->cn_nameptr
[0] == '\0') {
if (cnp
->cn_nameiop
!= LOOKUP
|| wantparent
) {
if (dp
->v_type
!= VDIR
) {
if (!(cnp
->cn_flags
& LOCKLEAF
))
if (cnp
->cn_flags
& SAVESTART
)
panic("lookup: SAVESTART");
if (cnp
->cn_flags
& ISDOTDOT
)
panic ("relookup: lookup on dot-dot");
* We now have a segment name to search for, and a directory to search.
if (error
= VOP_LOOKUP(dp
, vpp
, cnp
)) {
panic("leaf should be empty");
if (error
!= EJUSTRETURN
)
* If creating and at end of pathname, then can consider
* allowing file to be created.
if (rdonly
|| (dvp
->v_mount
->mnt_flag
& MNT_RDONLY
)) {
/* ASSERT(dvp == ndp->ni_startdir) */
if (cnp
->cn_flags
& SAVESTART
)
* We return with ni_vp NULL to indicate that the entry
* doesn't currently exist, leaving a pointer to the
* (possibly locked) directory inode in ndp->ni_dvp.
* Check for symbolic link
if (dp
->v_type
== VLNK
&& (cnp
->cn_flags
& FOLLOW
))
panic ("relookup: symlink found.\n");
* Check for read-only file systems.
if (cnp
->cn_nameiop
== DELETE
|| cnp
->cn_nameiop
== RENAME
) {
* Disallow directory write attempts on read-only
if (rdonly
|| (dp
->v_mount
->mnt_flag
& MNT_RDONLY
) ||
(dvp
->v_mount
->mnt_flag
& MNT_RDONLY
))) {
/* ASSERT(dvp == ndp->ni_startdir) */
if (cnp
->cn_flags
& SAVESTART
)
if ((cnp
->cn_flags
& LOCKLEAF
) == 0)
if ((cnp
->cn_flags
& LOCKPARENT
) && (cnp
->cn_flags
& ISLASTCN
))
* but ``atomically''. Can't do full commit without saving state in the
* inode on disk which isn't feasible at this time. Best we can do is
* always guarantee the target exists.
* 1) Bump link count on source while we're linking it to the
* target. This also ensure the inode won't be deleted out
* from underneath us while we work (it may be truncated by
* a concurrent `trunc' or `open' for creation).
* 2) Link source to destination. If destination already exists,
* 3) Unlink source reference to inode if still around. If a
* directory was moved and the parent of the destination
* is different from the source, patch the ".." entry in the
struct vop_rename_args
/* {
struct componentname *a_fcnp;
struct componentname *a_tcnp;
struct vnode
*tvp
= ap
->a_tvp
;
register struct vnode
*tdvp
= ap
->a_tdvp
;
struct vnode
*fvp
= ap
->a_fvp
;
register struct vnode
*fdvp
= ap
->a_fdvp
;
register struct componentname
*tcnp
= ap
->a_tcnp
;
register struct componentname
*fcnp
= ap
->a_fcnp
;
register struct inode
*ip
, *xp
, *dp
;
struct dirtemplate dirbuf
;
int doingdirectory
= 0, oldparent
= 0, newparent
= 0;
int fdvpneedsrele
= 1, tdvpneedsrele
= 1;
/* Check for cross-device rename */
if ((fvp
->v_mount
!= tdvp
->v_mount
) ||
(tvp
&& (fvp
->v_mount
!= tvp
->v_mount
))) {
VOP_ABORTOP(tdvp
, tcnp
); /* XXX, why not in NFS? */
VOP_ABORTOP(fdvp
, fcnp
); /* XXX, why not in NFS? */
if ((tcnp
->cn_flags
& HASBUF
) == 0 ||
(fcnp
->cn_flags
& HASBUF
) == 0)
panic("ufs_rename: no name");
* Check if just deleting a link name.
if ((ip
->i_mode
&IFMT
) == IFDIR
) {
if ((ip
->i_mode
&IFMT
) == IFDIR
) {
* Avoid ".", "..", and aliases of "." for obvious reasons.
if ((fcnp
->cn_namelen
== 1 && fcnp
->cn_nameptr
[0] == '.') ||
dp
== ip
|| (fcnp
->cn_flags
&ISDOTDOT
) ||
(ip
->i_flag
& IRENAME
)) {
oldparent
= dp
->i_number
;
* 1) Bump link count while we're moving stuff
* around. If we crash somewhere before
* completing our work, the link count
* may be wrong, but correctable.
error
= VOP_UPDATE(fvp
, &tv
, &tv
, 1);
* When the target exists, both the directory
* and target vnodes are returned locked.
* If ".." must be changed (ie the directory gets a new
* parent) then the source directory must not be in the
* directory heirarchy above the target, as this would
* orphan everything below the source directory. Also
* the user must have write permission in the source so
* as to be able to change "..". We must repeat the call
* to namei, as the parent directory is unlocked by the
if (oldparent
!= dp
->i_number
)
newparent
= dp
->i_number
;
if (doingdirectory
&& newparent
) {
error
= VOP_ACCESS(fvp
, VWRITE
, tcnp
->cn_cred
, tcnp
->cn_proc
);
if (error
= ufs_checkpath(ip
, dp
, tcnp
->cn_cred
))
if ((tcnp
->cn_flags
& SAVESTART
) == 0)
panic("ufs_rename: lost to startdir");
if (error
= relookup(tdvp
, &tvp
, tcnp
))
* 2) If target doesn't exist, link the target
* to the source and unlink the source.
* Otherwise, rewrite the target directory
* entry to reference the source inode and
* expunge the original entry's existence.
if (dp
->i_dev
!= ip
->i_dev
)
* Account for ".." in new directory.
* When source and destination have the same
* parent we don't fool with the link count.
if (doingdirectory
&& newparent
) {
if ((nlink_t
)dp
->i_nlink
>= LINK_MAX
) {
if (error
= VOP_UPDATE(ITOV(dp
), &tv
, &tv
, 1))
if (error
= ufs_direnter(ip
, tdvp
, tcnp
)) {
if (doingdirectory
&& newparent
) {
(void)VOP_UPDATE(ITOV(dp
), &tv
, &tv
, 1);
if (xp
->i_dev
!= dp
->i_dev
|| xp
->i_dev
!= ip
->i_dev
)
* Short circuit rename(foo, foo).
if (xp
->i_number
== ip
->i_number
)
panic("rename: same file");
* If the parent directory is "sticky", then the user must
* own the parent directory, or the destination of the rename,
* otherwise the destination may not be changed (except by
* root). This implements append-only directories.
if ((dp
->i_mode
& ISVTX
) && tcnp
->cn_cred
->cr_uid
!= 0 &&
tcnp
->cn_cred
->cr_uid
!= dp
->i_uid
&&
xp
->i_uid
!= tcnp
->cn_cred
->cr_uid
) {
* Target must be empty if a directory and have no links
* to it. Also, ensure source and target are compatible
* (both directories, or both not directories).
if ((xp
->i_mode
&IFMT
) == IFDIR
) {
if (!ufs_dirempty(xp
, dp
->i_number
, tcnp
->cn_cred
) ||
} else if (doingdirectory
) {
if (error
= ufs_dirrewrite(dp
, ip
, tcnp
))
* If the target directory is in the same
* directory as the source directory,
* decrement the link count on the parent
* of the target directory.
if (doingdirectory
&& !newparent
) {
* Adjust the link count of the target to
* reflect the dirrewrite above. If this is
* a directory it is empty and there are
* no links to it, so we can squash the inode and
* any space associated with it. We disallowed
* renaming over top of a directory with links to
* it above, as the remaining link would point to
* a directory without "." or ".." entries.
panic("rename: linked directory");
error
= VOP_TRUNCATE(ITOV(xp
), (off_t
)0, IO_SYNC
,
tcnp
->cn_cred
, tcnp
->cn_proc
);
fcnp
->cn_flags
&= ~MODMASK
;
fcnp
->cn_flags
|= LOCKPARENT
| LOCKLEAF
;
if ((fcnp
->cn_flags
& SAVESTART
) == 0)
panic("ufs_rename: lost from startdir");
(void) relookup(fdvp
, &fvp
, fcnp
);
* From name has disappeared.
panic("rename: lost dir entry");
* Ensure that the directory entry still exists and has not
* changed while the new name has been entered. If the source is
* a file then the entry may have been unlinked or renamed. In
* either case there is no further work to be done. If the source
* is a directory then it cannot have been rmdir'ed; its link
* count of three would cause a rmdir to fail with ENOTEMPTY.
* The IRENAME flag ensures that it cannot be moved by another
panic("rename: lost dir entry");
* If the source is a directory with a
* new parent, the link count of the old
* parent directory must be decremented
* and ".." set to point to the new parent.
if (doingdirectory
&& newparent
) {
error
= vn_rdwr(UIO_READ
, ITOV(xp
), (caddr_t
)&dirbuf
,
sizeof (struct dirtemplate
), (off_t
)0,
UIO_SYSSPACE
, IO_NODELOCKED
,
tcnp
->cn_cred
, (int *)0, (struct proc
*)0);
# if (BYTE_ORDER == LITTLE_ENDIAN)
if (fvp
->v_mount
->mnt_maxsymlinklen
<= 0)
namlen
= dirbuf
.dotdot_type
;
namlen
= dirbuf
.dotdot_namlen
;
namlen
= dirbuf
.dotdot_namlen
;
dirbuf
.dotdot_name
[0] != '.' ||
dirbuf
.dotdot_name
[1] != '.') {
ufs_dirbad(xp
, (doff_t
)12,
dirbuf
.dotdot_ino
= newparent
;
(void) vn_rdwr(UIO_WRITE
, ITOV(xp
),
sizeof (struct dirtemplate
),
error
= ufs_dirremove(fdvp
, fcnp
);
* A virgin directory (no blushing please).
static struct dirtemplate mastertemplate
= {
0, DIRBLKSIZ
- 12, DT_DIR
, 2, ".."
static struct odirtemplate omastertemplate
= {
0, DIRBLKSIZ
- 12, 2, ".."
struct vop_mkdir_args
/* {
struct componentname *a_cnp;
register struct vnode
*dvp
= ap
->a_dvp
;
register struct vattr
*vap
= ap
->a_vap
;
register struct componentname
*cnp
= ap
->a_cnp
;
register struct inode
*ip
, *dp
;
struct dirtemplate dirtemplate
, *dtp
;
if ((cnp
->cn_flags
& HASBUF
) == 0)
panic("ufs_mkdir: no name");
if ((nlink_t
)dp
->i_nlink
>= LINK_MAX
) {
free(cnp
->cn_pnbuf
, M_NAMEI
);
dmode
= vap
->va_mode
&0777;
* Must simulate part of maknode here to acquire the inode, but
* not have it entered in the parent directory. The entry is made
* later after writing "." and ".." entries.
if (error
= VOP_VALLOC(dvp
, dmode
, cnp
->cn_cred
, &tvp
)) {
free(cnp
->cn_pnbuf
, M_NAMEI
);
ip
->i_uid
= cnp
->cn_cred
->cr_uid
;
if ((error
= getinoquota(ip
)) ||
(error
= chkiq(ip
, 1, cnp
->cn_cred
, 0))) {
free(cnp
->cn_pnbuf
, M_NAMEI
);
VOP_VFREE(tvp
, ip
->i_number
, dmode
);
ip
->i_flag
|= IACC
|IUPD
|ICHG
;
ITOV(ip
)->v_type
= VDIR
; /* Rest init'd in iget() */
error
= VOP_UPDATE(ITOV(ip
), &tv
, &tv
, 1);
* Bump link count in parent directory
* to reflect work done below. Should
* be done before reference is created
* so reparation is possible if we crash.
if (error
= VOP_UPDATE(ITOV(dp
), &tv
, &tv
, 1))
/* Initialize directory with "." and ".." from static template. */
if (dvp
->v_mount
->mnt_maxsymlinklen
> 0)
dtp
= (struct dirtemplate
*)&omastertemplate
;
dirtemplate
.dot_ino
= ip
->i_number
;
dirtemplate
.dotdot_ino
= dp
->i_number
;
error
= vn_rdwr(UIO_WRITE
, ITOV(ip
), (caddr_t
)&dirtemplate
,
sizeof (dirtemplate
), (off_t
)0, UIO_SYSSPACE
,
IO_NODELOCKED
|IO_SYNC
, cnp
->cn_cred
, (int *)0, (struct proc
*)0);
if (DIRBLKSIZ
> VFSTOUFS(dvp
->v_mount
)->um_mountp
->mnt_stat
.f_bsize
)
panic("ufs_mkdir: blksize"); /* XXX should grow with balloc() */
/* Directory set up, now install it's entry in the parent directory. */
if (error
= ufs_direnter(ip
, dvp
, cnp
)) {
* No need to do an explicit VOP_TRUNCATE here, vrele will do this
* for us because we set the link count to 0.
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
struct vop_rmdir_args
/* {
struct componentname *a_cnp;
register struct vnode
*dvp
= ap
->a_dvp
;
register struct componentname
*cnp
= ap
->a_cnp
;
register struct inode
*ip
, *dp
;
* Verify the directory is empty (and valid).
* (Rmdir ".." won't be valid since
* ".." will contain a reference to
* the current directory and thus be
!ufs_dirempty(ip
, dp
->i_number
, cnp
->cn_cred
)) {
* Delete reference to directory before purging
* inode. If we crash in between, the directory
* will be reattached to lost+found,
if (error
= ufs_dirremove(dvp
, cnp
))
* Truncate inode. The only stuff left
* in the directory is "." and "..". The
* "." reference is inconsequential since
* we're quashing it. The ".." reference
* has already been adjusted above. We've
* removed the "." reference and the reference
* in the parent directory, but there may be
* other hard links so decrement by 2 and
* worry about them later.
error
= VOP_TRUNCATE(ap
->a_vp
, (off_t
)0, IO_SYNC
, cnp
->cn_cred
,
* symlink -- make a symbolic link
struct vop_symlink_args
/* {
struct componentname *a_cnp;
register struct vnode
*vp
, **vpp
= ap
->a_vpp
;
register struct inode
*ip
;
if (error
= ufs_makeinode(IFLNK
| ap
->a_vap
->va_mode
, ap
->a_dvp
,
len
= strlen(ap
->a_target
);
if (len
< vp
->v_mount
->mnt_maxsymlinklen
) {
bcopy(ap
->a_target
, (char *)ip
->i_shortlink
, len
);
error
= vn_rdwr(UIO_WRITE
, vp
, ap
->a_target
, len
, (off_t
)0,
UIO_SYSSPACE
, IO_NODELOCKED
, ap
->a_cnp
->cn_cred
, (int *)0,
* Vnode op for reading directories.
* The routine below assumes that the on-disk format of a directory
* is the same as that defined by <sys/dirent.h>. If the on-disk
* format changes, then it will be necessary to do a conversion
* from the on-disk format that read returns to the format defined
struct vop_readdir_args
/* {
register struct uio
*uio
= ap
->a_uio
;
count
&= ~(DIRBLKSIZ
- 1);
lost
= uio
->uio_resid
- count
;
if (count
< DIRBLKSIZ
|| (uio
->uio_offset
& (DIRBLKSIZ
-1)))
uio
->uio_iov
->iov_len
= count
;
# if (BYTE_ORDER == LITTLE_ENDIAN)
if (ap
->a_vp
->v_mount
->mnt_maxsymlinklen
> 0) {
error
= VOP_READ(ap
->a_vp
, uio
, 0, ap
->a_cred
);
auio
.uio_segflg
= UIO_SYSSPACE
;
MALLOC(dirbuf
, caddr_t
, count
, M_TEMP
, M_WAITOK
);
error
= VOP_READ(ap
->a_vp
, &auio
, 0, ap
->a_cred
);
readcnt
= count
- auio
.uio_resid
;
edp
= (struct dirent
*)&dirbuf
[readcnt
];
for (dp
= (struct dirent
*)dirbuf
; dp
< edp
; ) {
dp
->d_namlen
= dp
->d_type
;
((char *)dp
+ dp
->d_reclen
);
error
= uiomove(dirbuf
, readcnt
, uio
);
error
= VOP_READ(ap
->a_vp
, uio
, 0, ap
->a_cred
);
* Return target name of a symbolic link
struct vop_readlink_args
/* {
register struct vnode
*vp
= ap
->a_vp
;
register struct inode
*ip
= VTOI(vp
);
if (isize
< vp
->v_mount
->mnt_maxsymlinklen
) {
uiomove((char *)ip
->i_shortlink
, isize
, ap
->a_uio
);
return (VOP_READ(vp
, ap
->a_uio
, 0, ap
->a_cred
));
* Ufs abort op, called after namei() when a CREATE/DELETE isn't actually
* done. If a buffer has been saved in anticipation of a CREATE, delete it.
struct vop_abortop_args
/* {
struct componentname *a_cnp;
if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
)
FREE(ap
->a_cnp
->cn_pnbuf
, M_NAMEI
);
struct vop_lock_args
/* {
register struct inode
*ip
= VTOI(ap
->a_vp
);
struct vop_unlock_args
/* {
register struct inode
*ip
= VTOI(ap
->a_vp
);
if (!(ip
->i_flag
& ILOCKED
))
panic("ufs_unlock NOT LOCKED");
* Check for a locked inode.
struct vop_islocked_args
/* {
if (VTOI(ap
->a_vp
)->i_flag
& ILOCKED
)
* Calculate the logical to physical mapping if not done already,
* then call the device strategy routine.
struct vop_strategy_args
/* {
register struct buf
*bp
= ap
->a_bp
;
register struct vnode
*vp
= bp
->b_vp
;
register struct inode
*ip
;
if (vp
->v_type
== VBLK
|| vp
->v_type
== VCHR
)
panic("ufs_strategy: spec");
if (bp
->b_blkno
== bp
->b_lblkno
) {
VOP_BMAP(vp
, bp
->b_lblkno
, NULL
, &bp
->b_blkno
, NULL
)) {
if ((long)bp
->b_blkno
== -1)
if ((long)bp
->b_blkno
== -1) {
VOCALL (vp
->v_op
, VOFFSET(vop_strategy
), ap
);
* Print out the contents of an inode.
struct vop_print_args
/* {
register struct vnode
*vp
= ap
->a_vp
;
register struct inode
*ip
= VTOI(vp
);
printf("tag VT_UFS, ino %d, on dev %d, %d", ip
->i_number
,
major(ip
->i_dev
), minor(ip
->i_dev
));
printf("%s\n", (ip
->i_flag
& ILOCKED
) ? " (LOCKED)" : "");
if (ip
->i_lockholder
== 0)
printf("\towner pid %d", ip
->i_lockholder
);
printf(" waiting pid %d", ip
->i_lockwaiter
);
* Read wrapper for special devices.
struct vop_read_args
/* {
VTOI(ap
->a_vp
)->i_flag
|= IACC
;
return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_read
), ap
));
* Write wrapper for special devices.
struct vop_write_args
/* {
* Set update and change flags.
VTOI(ap
->a_vp
)->i_flag
|= IUPD
|ICHG
;
return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_write
), ap
));
* Close wrapper for special devices.
* Update the times on the inode then do device close.
struct vop_close_args
/* {
register struct inode
*ip
= VTOI(ap
->a_vp
);
if (ap
->a_vp
->v_usecount
> 1 && !(ip
->i_flag
& ILOCKED
))
ITIMES(ip
, &time
, &time
);
return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_close
), ap
));
* Read wrapper for fifo's
struct vop_read_args
/* {
extern int (**fifo_vnodeop_p
)();
VTOI(ap
->a_vp
)->i_flag
|= IACC
;
return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_read
), ap
));
* Write wrapper for fifo's.
struct vop_write_args
/* {
extern int (**fifo_vnodeop_p
)();
* Set update and change flags.
VTOI(ap
->a_vp
)->i_flag
|= IUPD
|ICHG
;
return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_write
), ap
));
* Close wrapper for fifo's.
* Update the times on the inode then do device close.
struct vop_close_args
/* {
extern int (**fifo_vnodeop_p
)();
register struct inode
*ip
= VTOI(ap
->a_vp
);
if (ap
->a_vp
->v_usecount
> 1 && !(ip
->i_flag
& ILOCKED
))
ITIMES(ip
, &time
, &time
);
return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_close
), ap
));
* Advisory record locking support
struct vop_advlock_args
/* {
register struct inode
*ip
= VTOI(ap
->a_vp
);
register struct flock
*fl
= ap
->a_fl
;
register struct lockf
*lock
;
* Avoid the common case of unlocking when inode has no locks.
if (ip
->i_lockf
== (struct lockf
*)0) {
if (ap
->a_op
!= F_SETLK
) {
* Convert the flock structure into a start and end.
* Caller is responsible for adding any necessary offset
start
= ip
->i_size
+ fl
->l_start
;
end
= start
+ fl
->l_len
- 1;
* Create the lockf structure
MALLOC(lock
, struct lockf
*, sizeof *lock
, M_LOCKF
, M_WAITOK
);
lock
->lf_type
= fl
->l_type
;
lock
->lf_next
= (struct lockf
*)0;
lock
->lf_block
= (struct lockf
*)0;
lock
->lf_flags
= ap
->a_flags
;
* Do the requested operation.
return (lf_setlock(lock
));
error
= lf_clearlock(lock
);
error
= lf_getlock(lock
, fl
);
* Initialize the vnode associated with a new inode, handle aliased
ufs_vinit(mntp
, specops
, fifoops
, vpp
)
switch(vp
->v_type
= IFTOVT(ip
->i_mode
)) {
if (nvp
= checkalias(vp
, ip
->i_rdev
, mntp
)) {
* Discard unneeded vnode, but save its inode.
nvp
->v_data
= vp
->v_data
;
vp
->v_op
= spec_vnodeop_p
;
* Reinitialize aliased inode.
if (ip
->i_number
== ROOTINO
)
* Initialize modrev times
SETHIGH(ip
->i_modrev
, mono_time
.tv_sec
);
SETLOW(ip
->i_modrev
, mono_time
.tv_usec
* 4294);
ufs_makeinode(mode
, dvp
, vpp
, cnp
)
struct componentname
*cnp
;
register struct inode
*ip
, *pdir
;
if ((cnp
->cn_flags
& HASBUF
) == 0)
panic("ufs_makeinode: no name");
if (error
= VOP_VALLOC(dvp
, mode
, cnp
->cn_cred
, &tvp
)) {
free(cnp
->cn_pnbuf
, M_NAMEI
);
ip
->i_uid
= cnp
->cn_cred
->cr_uid
;
if ((error
= getinoquota(ip
)) ||
(error
= chkiq(ip
, 1, cnp
->cn_cred
, 0))) {
free(cnp
->cn_pnbuf
, M_NAMEI
);
VOP_VFREE(tvp
, ip
->i_number
, mode
);
ip
->i_flag
|= IACC
|IUPD
|ICHG
;
tvp
->v_type
= IFTOVT(mode
); /* Rest init'd in iget() */
if ((ip
->i_mode
& ISGID
) && !groupmember(ip
->i_gid
, cnp
->cn_cred
) &&
suser(cnp
->cn_cred
, NULL
))
* Make sure inode goes to disk before directory entry.
if (error
= VOP_UPDATE(tvp
, &tv
, &tv
, 1))
if (error
= ufs_direnter(ip
, dvp
, cnp
))
if ((cnp
->cn_flags
& SAVESTART
) == 0)
FREE(cnp
->cn_pnbuf
, M_NAMEI
);
* Write error occurred trying to update the inode
* or the directory so must deallocate the inode.
free(cnp
->cn_pnbuf
, M_NAMEI
);