123405Smckusick /* 237737Smckusick * Copyright (c) 1982, 1986, 1989 Regents of the University of California. 337737Smckusick * All rights reserved. 423405Smckusick * 537737Smckusick * Redistribution and use in source and binary forms are permitted 637737Smckusick * provided that the above copyright notice and this paragraph are 737737Smckusick * duplicated in all such forms and that any documentation, 837737Smckusick * advertising materials, and other materials related to such 937737Smckusick * distribution and use acknowledge that the software was developed 1037737Smckusick * by the University of California, Berkeley. The name of the 1137737Smckusick * University may not be used to endorse or promote products derived 1237737Smckusick * from this software without specific prior written permission. 1337737Smckusick * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1437737Smckusick * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1537737Smckusick * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1637737Smckusick * 17*39909Smckusick * @(#)lfs_vnops.c 7.29 (Berkeley) 01/15/90 1823405Smckusick */ 1937Sbill 2017101Sbloom #include "param.h" 2117101Sbloom #include "systm.h" 2217101Sbloom #include "user.h" 2317101Sbloom #include "kernel.h" 2417101Sbloom #include "file.h" 2517101Sbloom #include "stat.h" 2617101Sbloom #include "buf.h" 2717101Sbloom #include "proc.h" 2817101Sbloom #include "uio.h" 2917101Sbloom #include "socket.h" 3017101Sbloom #include "socketvar.h" 3137737Smckusick #include "conf.h" 3217101Sbloom #include "mount.h" 3337737Smckusick #include "vnode.h" 3437737Smckusick #include "../ufs/inode.h" 3537737Smckusick #include "../ufs/fs.h" 3637737Smckusick #include "../ufs/quota.h" 3737Sbill 389167Ssam /* 3937737Smckusick * Global vfs data structures for ufs 409167Ssam */ 416254Sroot 4237737Smckusick int ufs_lookup(), 4337737Smckusick ufs_create(), 4437737Smckusick ufs_mknod(), 4537737Smckusick ufs_open(), 4637737Smckusick ufs_close(), 4737737Smckusick ufs_access(), 4837737Smckusick ufs_getattr(), 4937737Smckusick ufs_setattr(), 5037737Smckusick ufs_read(), 5137737Smckusick ufs_write(), 5237737Smckusick ufs_ioctl(), 5337737Smckusick ufs_select(), 5437737Smckusick ufs_mmap(), 5537737Smckusick ufs_fsync(), 5637737Smckusick ufs_seek(), 5737737Smckusick ufs_remove(), 5837737Smckusick ufs_link(), 5937737Smckusick ufs_rename(), 6037737Smckusick ufs_mkdir(), 6137737Smckusick ufs_rmdir(), 6237737Smckusick ufs_symlink(), 6337737Smckusick ufs_readdir(), 6437737Smckusick ufs_readlink(), 6537737Smckusick ufs_abortop(), 6637737Smckusick ufs_inactive(), 6739391Smckusick ufs_reclaim(), 6837737Smckusick ufs_lock(), 6937737Smckusick ufs_unlock(), 7037737Smckusick ufs_bmap(), 7139674Smckusick ufs_strategy(), 72*39909Smckusick ufs_print(), 73*39909Smckusick ufs_islocked(); 746254Sroot 7537737Smckusick struct vnodeops ufs_vnodeops = { 7639674Smckusick ufs_lookup, /* lookup */ 7739674Smckusick ufs_create, /* create */ 7839674Smckusick ufs_mknod, /* mknod */ 7939674Smckusick ufs_open, /* open */ 8039674Smckusick ufs_close, /* close */ 8139674Smckusick ufs_access, /* access */ 8239674Smckusick ufs_getattr, /* getattr */ 8339674Smckusick ufs_setattr, /* setattr */ 8439674Smckusick ufs_read, /* read */ 8539674Smckusick ufs_write, /* write */ 8639674Smckusick ufs_ioctl, /* ioctl */ 8739674Smckusick ufs_select, /* select */ 8839674Smckusick ufs_mmap, /* mmap */ 8939674Smckusick ufs_fsync, /* fsync */ 9039674Smckusick ufs_seek, /* seek */ 9139674Smckusick ufs_remove, /* remove */ 9239674Smckusick ufs_link, /* link */ 9339674Smckusick ufs_rename, /* rename */ 9439674Smckusick ufs_mkdir, /* mkdir */ 9539674Smckusick ufs_rmdir, /* rmdir */ 9639674Smckusick ufs_symlink, /* symlink */ 9739674Smckusick ufs_readdir, /* readdir */ 9839674Smckusick ufs_readlink, /* readlink */ 9939674Smckusick ufs_abortop, /* abortop */ 10039674Smckusick ufs_inactive, /* inactive */ 10139674Smckusick ufs_reclaim, /* reclaim */ 10239674Smckusick ufs_lock, /* lock */ 10339674Smckusick ufs_unlock, /* unlock */ 10439674Smckusick ufs_bmap, /* bmap */ 10539674Smckusick ufs_strategy, /* strategy */ 10639674Smckusick ufs_print, /* print */ 107*39909Smckusick ufs_islocked, /* islocked */ 10837737Smckusick }; 1096254Sroot 11039435Smckusick int spec_lookup(), 11139435Smckusick spec_open(), 11239628Smckusick ufsspec_read(), 11339628Smckusick ufsspec_write(), 11439435Smckusick spec_strategy(), 11539674Smckusick spec_bmap(), 11639435Smckusick spec_ioctl(), 11739435Smckusick spec_select(), 11839628Smckusick ufsspec_close(), 11939435Smckusick spec_badop(), 12039435Smckusick spec_nullop(); 12139435Smckusick 12239435Smckusick struct vnodeops spec_inodeops = { 12339597Smckusick spec_lookup, /* lookup */ 12439597Smckusick spec_badop, /* create */ 12539597Smckusick spec_badop, /* mknod */ 12639597Smckusick spec_open, /* open */ 12739628Smckusick ufsspec_close, /* close */ 12839597Smckusick ufs_access, /* access */ 12939597Smckusick ufs_getattr, /* getattr */ 13039597Smckusick ufs_setattr, /* setattr */ 13139628Smckusick ufsspec_read, /* read */ 13239628Smckusick ufsspec_write, /* write */ 13339597Smckusick spec_ioctl, /* ioctl */ 13439597Smckusick spec_select, /* select */ 13539597Smckusick spec_badop, /* mmap */ 13639597Smckusick spec_nullop, /* fsync */ 13739597Smckusick spec_badop, /* seek */ 13839597Smckusick spec_badop, /* remove */ 13939597Smckusick spec_badop, /* link */ 14039597Smckusick spec_badop, /* rename */ 14139597Smckusick spec_badop, /* mkdir */ 14239597Smckusick spec_badop, /* rmdir */ 14339597Smckusick spec_badop, /* symlink */ 14439597Smckusick spec_badop, /* readdir */ 14539597Smckusick spec_badop, /* readlink */ 14639597Smckusick spec_badop, /* abortop */ 14739597Smckusick ufs_inactive, /* inactive */ 14839597Smckusick ufs_reclaim, /* reclaim */ 14939597Smckusick ufs_lock, /* lock */ 15039597Smckusick ufs_unlock, /* unlock */ 15139674Smckusick spec_bmap, /* bmap */ 15239597Smckusick spec_strategy, /* strategy */ 15339674Smckusick ufs_print, /* print */ 154*39909Smckusick ufs_islocked, /* islocked */ 15539435Smckusick }; 15639435Smckusick 15737737Smckusick enum vtype iftovt_tab[8] = { 15837737Smckusick VNON, VCHR, VDIR, VBLK, VREG, VLNK, VSOCK, VBAD, 15937737Smckusick }; 16037737Smckusick int vttoif_tab[8] = { 16137737Smckusick 0, IFREG, IFDIR, IFBLK, IFCHR, IFLNK, IFSOCK, IFMT, 16237737Smckusick }; 1636254Sroot 1649167Ssam /* 16537737Smckusick * Create a regular file 1669167Ssam */ 16737737Smckusick ufs_create(ndp, vap) 16837737Smckusick struct nameidata *ndp; 16937737Smckusick struct vattr *vap; 1706254Sroot { 17137737Smckusick struct inode *ip; 17237737Smckusick int error; 1736254Sroot 17437737Smckusick if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip)) 17537737Smckusick return (error); 17637737Smckusick ndp->ni_vp = ITOV(ip); 17737737Smckusick return (0); 1786254Sroot } 1796254Sroot 18037Sbill /* 18137737Smckusick * Mknod vnode call 1826254Sroot */ 18337737Smckusick /* ARGSUSED */ 18437737Smckusick ufs_mknod(ndp, vap, cred) 18537737Smckusick struct nameidata *ndp; 18637737Smckusick struct ucred *cred; 18737737Smckusick struct vattr *vap; 1886254Sroot { 18939435Smckusick register struct vnode *vp; 19037737Smckusick struct inode *ip; 19137737Smckusick int error; 1926254Sroot 19337737Smckusick if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip)) 19437737Smckusick return (error); 19539435Smckusick vp = ITOV(ip); 19637737Smckusick if (vap->va_rdev) { 19737737Smckusick /* 19837737Smckusick * Want to be able to use this to make badblock 19937737Smckusick * inodes, so don't truncate the dev number. 20037737Smckusick */ 20139608Smckusick ip->i_rdev = vap->va_rdev; 20237737Smckusick ip->i_flag |= IACC|IUPD|ICHG; 20312756Ssam } 20437737Smckusick /* 20537737Smckusick * Remove inode so that it will be reloaded by iget and 20637737Smckusick * checked to see if it is an alias of an existing entry 20737737Smckusick * in the inode cache. 20837737Smckusick */ 20938809Smckusick iput(ip); 21039435Smckusick vp->v_type = VNON; 21139435Smckusick vgone(vp); 21237737Smckusick return (0); 2136254Sroot } 2146254Sroot 2156254Sroot /* 21637737Smckusick * Open called. 21737737Smckusick * 21837737Smckusick * Nothing to do. 2196254Sroot */ 22037737Smckusick /* ARGSUSED */ 22137737Smckusick ufs_open(vp, mode, cred) 22237737Smckusick struct vnode *vp; 22337737Smckusick int mode; 22437737Smckusick struct ucred *cred; 2256254Sroot { 2266254Sroot 22737737Smckusick return (0); 2286254Sroot } 2296254Sroot 2306254Sroot /* 23137737Smckusick * Close called 23237737Smckusick * 23337737Smckusick * Update the times on the inode. 2346254Sroot */ 23537737Smckusick /* ARGSUSED */ 23637737Smckusick ufs_close(vp, fflag, cred) 23737737Smckusick struct vnode *vp; 23837737Smckusick int fflag; 23937737Smckusick struct ucred *cred; 2406254Sroot { 24137737Smckusick register struct inode *ip = VTOI(vp); 2426254Sroot 24339815Smckusick if (vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) 24437737Smckusick ITIMES(ip, &time, &time); 24537737Smckusick return (0); 2466254Sroot } 2476254Sroot 24837737Smckusick ufs_access(vp, mode, cred) 24937737Smckusick struct vnode *vp; 25037737Smckusick int mode; 25137737Smckusick struct ucred *cred; 2526254Sroot { 2536254Sroot 25437737Smckusick return (iaccess(VTOI(vp), mode, cred)); 2556254Sroot } 2566254Sroot 25737737Smckusick /* ARGSUSED */ 25837737Smckusick ufs_getattr(vp, vap, cred) 25937737Smckusick struct vnode *vp; 26037737Smckusick register struct vattr *vap; 26137737Smckusick struct ucred *cred; 2626254Sroot { 26337737Smckusick register struct inode *ip = VTOI(vp); 2646254Sroot 26537737Smckusick ITIMES(ip, &time, &time); 2666254Sroot /* 26737737Smckusick * Copy from inode table 2686254Sroot */ 26937737Smckusick vap->va_fsid = ip->i_dev; 27037737Smckusick vap->va_fileid = ip->i_number; 27137737Smckusick vap->va_mode = ip->i_mode & ~IFMT; 27237737Smckusick vap->va_nlink = ip->i_nlink; 27337737Smckusick vap->va_uid = ip->i_uid; 27437737Smckusick vap->va_gid = ip->i_gid; 27537737Smckusick vap->va_rdev = (dev_t)ip->i_rdev; 27639391Smckusick vap->va_size = ip->i_din.di_qsize.val[0]; 27739391Smckusick vap->va_size1 = ip->i_din.di_qsize.val[1]; 27837737Smckusick vap->va_atime.tv_sec = ip->i_atime; 27938578Smckusick vap->va_atime.tv_usec = 0; 28037737Smckusick vap->va_mtime.tv_sec = ip->i_mtime; 28138578Smckusick vap->va_mtime.tv_usec = 0; 28237737Smckusick vap->va_ctime.tv_sec = ip->i_ctime; 28338578Smckusick vap->va_ctime.tv_usec = 0; 28438254Smckusick vap->va_flags = ip->i_flags; 28538254Smckusick vap->va_gen = ip->i_gen; 28637737Smckusick /* this doesn't belong here */ 28737737Smckusick if (vp->v_type == VBLK) 28837737Smckusick vap->va_blocksize = BLKDEV_IOSIZE; 28937737Smckusick else if (vp->v_type == VCHR) 29037737Smckusick vap->va_blocksize = MAXBSIZE; 2917142Smckusick else 29237737Smckusick vap->va_blocksize = ip->i_fs->fs_bsize; 29338657Smckusick vap->va_bytes = dbtob(ip->i_blocks); 29437737Smckusick vap->va_bytes1 = -1; 29537737Smckusick vap->va_type = vp->v_type; 29637737Smckusick return (0); 2976254Sroot } 2986254Sroot 2996254Sroot /* 30037737Smckusick * Set attribute vnode op. called from several syscalls 3016254Sroot */ 30237737Smckusick ufs_setattr(vp, vap, cred) 30337737Smckusick register struct vnode *vp; 30437737Smckusick register struct vattr *vap; 30537737Smckusick register struct ucred *cred; 3066254Sroot { 30737737Smckusick register struct inode *ip = VTOI(vp); 30837737Smckusick int error = 0; 3096254Sroot 31037737Smckusick /* 31137737Smckusick * Check for unsetable attributes. 31237737Smckusick */ 31337737Smckusick if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || 31437737Smckusick (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || 31537737Smckusick (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || 31638254Smckusick ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { 31737737Smckusick return (EINVAL); 31816540Ssam } 31937737Smckusick /* 32037737Smckusick * Go through the fields and update iff not VNOVAL. 32137737Smckusick */ 32237737Smckusick if (vap->va_uid != (u_short)VNOVAL || vap->va_gid != (u_short)VNOVAL) 32337737Smckusick if (error = chown1(vp, vap->va_uid, vap->va_gid, cred)) 32437737Smckusick return (error); 32537737Smckusick if (vap->va_size != VNOVAL) { 32637737Smckusick if (vp->v_type == VDIR) 32737737Smckusick return (EISDIR); 32839674Smckusick if (error = itrunc(ip, vap->va_size, 0)) /* XXX IO_SYNC? */ 32937737Smckusick return (error); 33013878Ssam } 33137737Smckusick if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { 33237773Smckusick if (cred->cr_uid != ip->i_uid && 33337773Smckusick (error = suser(cred, &u.u_acflag))) 33437773Smckusick return (error); 33537737Smckusick if (vap->va_atime.tv_sec != VNOVAL) 33637737Smckusick ip->i_flag |= IACC; 33737737Smckusick if (vap->va_mtime.tv_sec != VNOVAL) 33837737Smckusick ip->i_flag |= IUPD; 33937737Smckusick ip->i_flag |= ICHG; 34037737Smckusick if (error = iupdat(ip, &vap->va_atime, &vap->va_mtime, 1)) 34137737Smckusick return (error); 3426254Sroot } 34337737Smckusick if (vap->va_mode != (u_short)VNOVAL) 34437737Smckusick error = chmod1(vp, (int)vap->va_mode, cred); 34538254Smckusick if (vap->va_flags != VNOVAL) { 34638254Smckusick if (cred->cr_uid != ip->i_uid && 34738254Smckusick (error = suser(cred, &u.u_acflag))) 34838254Smckusick return (error); 34938254Smckusick if (cred->cr_uid == 0) { 35038254Smckusick ip->i_flags = vap->va_flags; 35138254Smckusick } else { 35238254Smckusick ip->i_flags &= 0xffff0000; 35338254Smckusick ip->i_flags |= (vap->va_flags & 0xffff); 35438254Smckusick } 35538254Smckusick ip->i_flag |= ICHG; 35638254Smckusick } 35737737Smckusick return (error); 3586254Sroot } 3596254Sroot 3606254Sroot /* 3619167Ssam * Change the mode on a file. 3629167Ssam * Inode must be locked before calling. 3639167Ssam */ 36437737Smckusick chmod1(vp, mode, cred) 36537737Smckusick register struct vnode *vp; 3667701Ssam register int mode; 36737737Smckusick struct ucred *cred; 3687701Ssam { 36937737Smckusick register struct inode *ip = VTOI(vp); 37037773Smckusick int error; 3717868Sroot 37237773Smckusick if (cred->cr_uid != ip->i_uid && 37337773Smckusick (error = suser(cred, &u.u_acflag))) 37437773Smckusick return (error); 3756254Sroot ip->i_mode &= ~07777; 37637737Smckusick if (cred->cr_uid) { 37737737Smckusick if (vp->v_type != VDIR) 37821015Smckusick mode &= ~ISVTX; 37937737Smckusick if (!groupmember(ip->i_gid, cred)) 38011811Ssam mode &= ~ISGID; 3817439Sroot } 38237737Smckusick ip->i_mode |= mode & 07777; 3836254Sroot ip->i_flag |= ICHG; 38437737Smckusick if ((vp->v_flag & VTEXT) && (ip->i_mode & ISVTX) == 0) 38537737Smckusick xrele(vp); 38621015Smckusick return (0); 3875992Swnj } 3885992Swnj 3899167Ssam /* 3907701Ssam * Perform chown operation on inode ip; 3917701Ssam * inode must be locked prior to call. 3927701Ssam */ 39337737Smckusick chown1(vp, uid, gid, cred) 39437737Smckusick register struct vnode *vp; 39537737Smckusick uid_t uid; 39637737Smckusick gid_t gid; 39737737Smckusick struct ucred *cred; 3987701Ssam { 39937737Smckusick register struct inode *ip = VTOI(vp); 4007701Ssam #ifdef QUOTA 4017701Ssam register long change; 40211811Ssam #endif 40337737Smckusick int error; 4047701Ssam 40537737Smckusick if (uid == (u_short)VNOVAL) 40611811Ssam uid = ip->i_uid; 40737737Smckusick if (gid == (u_short)VNOVAL) 40811811Ssam gid = ip->i_gid; 40936614Sbostic /* 41036614Sbostic * If we don't own the file, are trying to change the owner 41136614Sbostic * of the file, or are not a member of the target group, 41236614Sbostic * the caller must be superuser or the call fails. 41336614Sbostic */ 41437737Smckusick if ((cred->cr_uid != ip->i_uid || uid != ip->i_uid || 41537737Smckusick !groupmember((gid_t)gid, cred)) && 41637737Smckusick (error = suser(cred, &u.u_acflag))) 41737737Smckusick return (error); 41811811Ssam #ifdef QUOTA 41914385Ssam if (ip->i_uid == uid) /* this just speeds things a little */ 4207482Skre change = 0; 42112646Ssam else 42212646Ssam change = ip->i_blocks; 42312646Ssam (void) chkdq(ip, -change, 1); 42412646Ssam (void) chkiq(ip->i_dev, ip, ip->i_uid, 1); 4257482Skre dqrele(ip->i_dquot); 4267482Skre #endif 42739306Smckusick if (ip->i_uid != uid && cred->cr_uid != 0) 42839306Smckusick ip->i_mode &= ~ISUID; 42939306Smckusick if (ip->i_gid != gid && cred->cr_uid != 0) 43039306Smckusick ip->i_mode &= ~ISGID; 43111811Ssam ip->i_uid = uid; 43211811Ssam ip->i_gid = gid; 4336254Sroot ip->i_flag |= ICHG; 4347701Ssam #ifdef QUOTA 4357482Skre ip->i_dquot = inoquota(ip); 43612646Ssam (void) chkdq(ip, change, 1); 43726361Skarels (void) chkiq(ip->i_dev, (struct inode *)NULL, (uid_t)uid, 1); 43812646Ssam return (u.u_error); /* should == 0 ALWAYS !! */ 43912646Ssam #else 44012646Ssam return (0); 4417482Skre #endif 44237Sbill } 44337Sbill 44439608Smckusick /* 44539608Smckusick * Vnode op for reading. 44639608Smckusick */ 44737737Smckusick /* ARGSUSED */ 44839608Smckusick ufs_read(vp, uio, ioflag, cred) 44939608Smckusick struct vnode *vp; 45039608Smckusick register struct uio *uio; 45139608Smckusick int ioflag; 45239608Smckusick struct ucred *cred; 45339608Smckusick { 45439608Smckusick register struct inode *ip = VTOI(vp); 45539608Smckusick register struct fs *fs; 45639608Smckusick struct buf *bp; 45739608Smckusick daddr_t lbn, bn, rablock; 45839896Smckusick int size, diff, error = 0; 45939608Smckusick long n, on, type; 46039608Smckusick 46139608Smckusick if (uio->uio_rw != UIO_READ) 46239608Smckusick panic("ufs_read mode"); 46339608Smckusick type = ip->i_mode & IFMT; 46439608Smckusick if (type != IFDIR && type != IFREG && type != IFLNK) 46539608Smckusick panic("ufs_read type"); 46639608Smckusick if (uio->uio_resid == 0) 46739608Smckusick return (0); 46839608Smckusick if (uio->uio_offset < 0) 46939608Smckusick return (EINVAL); 47039608Smckusick ip->i_flag |= IACC; 47139608Smckusick fs = ip->i_fs; 47239608Smckusick do { 47339608Smckusick lbn = lblkno(fs, uio->uio_offset); 47439608Smckusick on = blkoff(fs, uio->uio_offset); 47539608Smckusick n = MIN((unsigned)(fs->fs_bsize - on), uio->uio_resid); 47639608Smckusick diff = ip->i_size - uio->uio_offset; 47739608Smckusick if (diff <= 0) 47839608Smckusick return (0); 47939608Smckusick if (diff < n) 48039608Smckusick n = diff; 48139608Smckusick size = blksize(fs, ip, lbn); 48239674Smckusick rablock = lbn + 1; 48339896Smckusick if (vp->v_lastr + 1 == lbn && 48439896Smckusick lblktosize(fs, rablock) < ip->i_size) 48539896Smckusick error = breada(ITOV(ip), lbn, size, rablock, 48639896Smckusick blksize(fs, ip, rablock), NOCRED, &bp); 48739608Smckusick else 48839674Smckusick error = bread(ITOV(ip), lbn, size, NOCRED, &bp); 48939815Smckusick vp->v_lastr = lbn; 49039608Smckusick n = MIN(n, size - bp->b_resid); 49139608Smckusick if (error) { 49239608Smckusick brelse(bp); 49339608Smckusick return (error); 49439608Smckusick } 49539608Smckusick error = uiomove(bp->b_un.b_addr + on, (int)n, uio); 49639608Smckusick if (n + on == fs->fs_bsize || uio->uio_offset == ip->i_size) 49739608Smckusick bp->b_flags |= B_AGE; 49839608Smckusick brelse(bp); 49939608Smckusick } while (error == 0 && uio->uio_resid > 0 && n != 0); 50039608Smckusick return (error); 50139608Smckusick } 50239608Smckusick 50339608Smckusick /* 50439608Smckusick * Vnode op for writing. 50539608Smckusick */ 50639608Smckusick ufs_write(vp, uio, ioflag, cred) 50739608Smckusick register struct vnode *vp; 50839608Smckusick struct uio *uio; 50939608Smckusick int ioflag; 51039608Smckusick struct ucred *cred; 51139608Smckusick { 51239608Smckusick register struct inode *ip = VTOI(vp); 51339608Smckusick register struct fs *fs; 51439608Smckusick struct buf *bp; 51539608Smckusick daddr_t lbn, bn; 51639608Smckusick u_long osize; 51739608Smckusick int i, n, on, flags; 51839608Smckusick int count, size, resid, error = 0; 51939608Smckusick 52039608Smckusick if (uio->uio_rw != UIO_WRITE) 52139608Smckusick panic("ufs_write mode"); 52239608Smckusick switch (vp->v_type) { 52339608Smckusick case VREG: 52439608Smckusick if (ioflag & IO_APPEND) 52539608Smckusick uio->uio_offset = ip->i_size; 52639608Smckusick /* fall through */ 52739608Smckusick case VLNK: 52839608Smckusick break; 52939608Smckusick 53039608Smckusick case VDIR: 53139608Smckusick if ((ioflag & IO_SYNC) == 0) 53239608Smckusick panic("ufs_write nonsync dir write"); 53339608Smckusick break; 53439608Smckusick 53539608Smckusick default: 53639608Smckusick panic("ufs_write type"); 53739608Smckusick } 53839608Smckusick if (uio->uio_offset < 0) 53939608Smckusick return (EINVAL); 54039608Smckusick if (uio->uio_resid == 0) 54139608Smckusick return (0); 54239608Smckusick /* 54339608Smckusick * Maybe this should be above the vnode op call, but so long as 54439608Smckusick * file servers have no limits, i don't think it matters 54539608Smckusick */ 54639608Smckusick if (vp->v_type == VREG && 54739608Smckusick uio->uio_offset + uio->uio_resid > 54839608Smckusick u.u_rlimit[RLIMIT_FSIZE].rlim_cur) { 54939608Smckusick psignal(u.u_procp, SIGXFSZ); 55039608Smckusick return (EFBIG); 55139608Smckusick } 55239608Smckusick resid = uio->uio_resid; 55339608Smckusick osize = ip->i_size; 55439608Smckusick fs = ip->i_fs; 55539674Smckusick flags = 0; 55639674Smckusick if (ioflag & IO_SYNC) 55739674Smckusick flags = B_SYNC; 55839608Smckusick do { 55939608Smckusick lbn = lblkno(fs, uio->uio_offset); 56039608Smckusick on = blkoff(fs, uio->uio_offset); 56139608Smckusick n = MIN((unsigned)(fs->fs_bsize - on), uio->uio_resid); 56239608Smckusick if (n < fs->fs_bsize) 56339674Smckusick flags |= B_CLRBUF; 56439608Smckusick else 56539674Smckusick flags &= ~B_CLRBUF; 56639674Smckusick if (error = balloc(ip, lbn, (int)(on + n), &bp, flags)) 56739608Smckusick break; 56839674Smckusick bn = bp->b_blkno; 56939608Smckusick if (uio->uio_offset + n > ip->i_size) 57039608Smckusick ip->i_size = uio->uio_offset + n; 57139608Smckusick size = blksize(fs, ip, lbn); 57239608Smckusick count = howmany(size, CLBYTES); 57339608Smckusick for (i = 0; i < count; i++) 57439608Smckusick munhash(ip->i_devvp, bn + i * CLBYTES / DEV_BSIZE); 57539608Smckusick n = MIN(n, size - bp->b_resid); 57639608Smckusick error = uiomove(bp->b_un.b_addr + on, n, uio); 57739608Smckusick if (ioflag & IO_SYNC) 57839608Smckusick (void) bwrite(bp); 57939608Smckusick else if (n + on == fs->fs_bsize) { 58039608Smckusick bp->b_flags |= B_AGE; 58139608Smckusick bawrite(bp); 58239608Smckusick } else 58339608Smckusick bdwrite(bp); 58439608Smckusick ip->i_flag |= IUPD|ICHG; 58539608Smckusick if (cred->cr_uid != 0) 58639608Smckusick ip->i_mode &= ~(ISUID|ISGID); 58739608Smckusick } while (error == 0 && uio->uio_resid > 0 && n != 0); 58839608Smckusick if (error && (ioflag & IO_UNIT)) { 58939674Smckusick (void) itrunc(ip, osize, ioflag & IO_SYNC); 59039608Smckusick uio->uio_offset -= resid - uio->uio_resid; 59139608Smckusick uio->uio_resid = resid; 59239608Smckusick } 59339608Smckusick return (error); 59439608Smckusick } 59539608Smckusick 59639608Smckusick /* ARGSUSED */ 59737737Smckusick ufs_ioctl(vp, com, data, fflag, cred) 59837737Smckusick struct vnode *vp; 59937737Smckusick int com; 60037737Smckusick caddr_t data; 60137737Smckusick int fflag; 60237737Smckusick struct ucred *cred; 60311811Ssam { 60411811Ssam 60537737Smckusick return (ENOTTY); 60611811Ssam } 60711811Ssam 60837737Smckusick /* ARGSUSED */ 60937737Smckusick ufs_select(vp, which, cred) 61037737Smckusick struct vnode *vp; 61137737Smckusick int which; 61237737Smckusick struct ucred *cred; 61337737Smckusick { 61437737Smckusick 61537737Smckusick return (1); /* XXX */ 61637737Smckusick } 61737737Smckusick 6189167Ssam /* 61937737Smckusick * Mmap a file 62037737Smckusick * 62137737Smckusick * NB Currently unsupported. 6229167Ssam */ 62337737Smckusick /* ARGSUSED */ 62437737Smckusick ufs_mmap(vp, fflags, cred) 62537737Smckusick struct vnode *vp; 62637737Smckusick int fflags; 62737737Smckusick struct ucred *cred; 62837Sbill { 62937Sbill 63037737Smckusick return (EINVAL); 63137Sbill } 6327535Sroot 6339167Ssam /* 63437737Smckusick * Synch an open file. 6359167Ssam */ 63637737Smckusick /* ARGSUSED */ 63739597Smckusick ufs_fsync(vp, fflags, cred, waitfor) 63837737Smckusick struct vnode *vp; 63937737Smckusick int fflags; 64037737Smckusick struct ucred *cred; 64139597Smckusick int waitfor; 6427701Ssam { 64339597Smckusick struct inode *ip = VTOI(vp); 6447701Ssam 64537737Smckusick if (fflags&FWRITE) 64637737Smckusick ip->i_flag |= ICHG; 64739674Smckusick vflushbuf(vp, waitfor == MNT_WAIT ? B_SYNC : 0); 64839674Smckusick return (iupdat(ip, &time, &time, waitfor == MNT_WAIT)); 6497701Ssam } 6507701Ssam 6519167Ssam /* 65237737Smckusick * Seek on a file 65337737Smckusick * 65437737Smckusick * Nothing to do, so just return. 6559167Ssam */ 65637737Smckusick /* ARGSUSED */ 65737737Smckusick ufs_seek(vp, oldoff, newoff, cred) 65837737Smckusick struct vnode *vp; 65937737Smckusick off_t oldoff, newoff; 66037737Smckusick struct ucred *cred; 6617701Ssam { 6627701Ssam 66337737Smckusick return (0); 66437737Smckusick } 66537737Smckusick 66637737Smckusick /* 66737737Smckusick * ufs remove 66837737Smckusick * Hard to avoid races here, especially 66937737Smckusick * in unlinking directories. 67037737Smckusick */ 67137737Smckusick ufs_remove(ndp) 67237737Smckusick struct nameidata *ndp; 67337737Smckusick { 67437737Smckusick register struct inode *ip, *dp; 67537737Smckusick int error; 67637737Smckusick 67737737Smckusick ip = VTOI(ndp->ni_vp); 67837737Smckusick dp = VTOI(ndp->ni_dvp); 67937737Smckusick error = dirremove(ndp); 68037737Smckusick if (!error) { 68137737Smckusick ip->i_nlink--; 68237737Smckusick ip->i_flag |= ICHG; 6837701Ssam } 68437737Smckusick if (dp == ip) 68537737Smckusick vrele(ITOV(ip)); 68637737Smckusick else 68737737Smckusick iput(ip); 68837737Smckusick iput(dp); 68937737Smckusick return (error); 6907701Ssam } 6917701Ssam 6929167Ssam /* 69337737Smckusick * link vnode call 6949167Ssam */ 69537737Smckusick ufs_link(vp, ndp) 69637737Smckusick register struct vnode *vp; 69737737Smckusick register struct nameidata *ndp; 6989167Ssam { 69937737Smckusick register struct inode *ip = VTOI(vp); 70037737Smckusick int error; 7019167Ssam 70237737Smckusick if (ndp->ni_dvp != vp) 70337737Smckusick ILOCK(ip); 70437737Smckusick if (ip->i_nlink == LINK_MAX - 1) { 70537737Smckusick error = EMLINK; 70637737Smckusick goto out; 70737737Smckusick } 70837737Smckusick ip->i_nlink++; 70937737Smckusick ip->i_flag |= ICHG; 71037737Smckusick error = iupdat(ip, &time, &time, 1); 71137737Smckusick if (!error) 71237737Smckusick error = direnter(ip, ndp); 71337737Smckusick out: 71437737Smckusick if (ndp->ni_dvp != vp) 71537737Smckusick IUNLOCK(ip); 71637737Smckusick if (error) { 71737737Smckusick ip->i_nlink--; 71830598Smckusick ip->i_flag |= ICHG; 71937737Smckusick } 72037737Smckusick return (error); 7219167Ssam } 7229167Ssam 7239167Ssam /* 7249167Ssam * Rename system call. 7259167Ssam * rename("foo", "bar"); 7269167Ssam * is essentially 7279167Ssam * unlink("bar"); 7289167Ssam * link("foo", "bar"); 7299167Ssam * unlink("foo"); 7309167Ssam * but ``atomically''. Can't do full commit without saving state in the 7319167Ssam * inode on disk which isn't feasible at this time. Best we can do is 7329167Ssam * always guarantee the target exists. 7339167Ssam * 7349167Ssam * Basic algorithm is: 7359167Ssam * 7369167Ssam * 1) Bump link count on source while we're linking it to the 73737737Smckusick * target. This also ensure the inode won't be deleted out 73816776Smckusick * from underneath us while we work (it may be truncated by 73916776Smckusick * a concurrent `trunc' or `open' for creation). 7409167Ssam * 2) Link source to destination. If destination already exists, 7419167Ssam * delete it first. 74216776Smckusick * 3) Unlink source reference to inode if still around. If a 74316776Smckusick * directory was moved and the parent of the destination 7449167Ssam * is different from the source, patch the ".." entry in the 7459167Ssam * directory. 7469167Ssam */ 74737737Smckusick ufs_rename(fndp, tndp) 74837737Smckusick register struct nameidata *fndp, *tndp; 7497701Ssam { 7509167Ssam register struct inode *ip, *xp, *dp; 75116776Smckusick struct dirtemplate dirbuf; 75216776Smckusick int doingdirectory = 0, oldparent = 0, newparent = 0; 75310051Ssam int error = 0; 7547701Ssam 75537737Smckusick dp = VTOI(fndp->ni_dvp); 75637737Smckusick ip = VTOI(fndp->ni_vp); 75737737Smckusick ILOCK(ip); 7589167Ssam if ((ip->i_mode&IFMT) == IFDIR) { 75937737Smckusick register struct direct *d = &fndp->ni_dent; 7609167Ssam 7619167Ssam /* 76211641Ssam * Avoid ".", "..", and aliases of "." for obvious reasons. 7639167Ssam */ 76437737Smckusick if ((d->d_namlen == 1 && d->d_name[0] == '.') || dp == ip || 76537737Smckusick fndp->ni_isdotdot || (ip->i_flag & IRENAME)) { 76637737Smckusick IUNLOCK(ip); 76737737Smckusick ufs_abortop(fndp); 76837737Smckusick ufs_abortop(tndp); 76937737Smckusick return (EINVAL); 7709167Ssam } 77116776Smckusick ip->i_flag |= IRENAME; 7729167Ssam oldparent = dp->i_number; 7739167Ssam doingdirectory++; 7749167Ssam } 77537737Smckusick vrele(fndp->ni_dvp); 7769167Ssam 7779167Ssam /* 7789167Ssam * 1) Bump link count while we're moving stuff 7799167Ssam * around. If we crash somewhere before 7809167Ssam * completing our work, the link count 7819167Ssam * may be wrong, but correctable. 7829167Ssam */ 7839167Ssam ip->i_nlink++; 7849167Ssam ip->i_flag |= ICHG; 78537737Smckusick error = iupdat(ip, &time, &time, 1); 78616664Smckusick IUNLOCK(ip); 7879167Ssam 7889167Ssam /* 7899167Ssam * When the target exists, both the directory 79037737Smckusick * and target vnodes are returned locked. 7919167Ssam */ 79237737Smckusick dp = VTOI(tndp->ni_dvp); 79337737Smckusick xp = NULL; 79437737Smckusick if (tndp->ni_vp) 79537737Smckusick xp = VTOI(tndp->ni_vp); 7969167Ssam /* 79711641Ssam * If ".." must be changed (ie the directory gets a new 79812816Smckusick * parent) then the source directory must not be in the 79912816Smckusick * directory heirarchy above the target, as this would 80012816Smckusick * orphan everything below the source directory. Also 80112816Smckusick * the user must have write permission in the source so 80212816Smckusick * as to be able to change "..". We must repeat the call 80312816Smckusick * to namei, as the parent directory is unlocked by the 80412816Smckusick * call to checkpath(). 80511641Ssam */ 80616776Smckusick if (oldparent != dp->i_number) 80716776Smckusick newparent = dp->i_number; 80816776Smckusick if (doingdirectory && newparent) { 80937737Smckusick if (error = iaccess(ip, IWRITE, tndp->ni_cred)) 81012816Smckusick goto bad; 81137737Smckusick tndp->ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE; 81212816Smckusick do { 81337737Smckusick dp = VTOI(tndp->ni_dvp); 81412816Smckusick if (xp != NULL) 81538069Smckusick iput(xp); 81637737Smckusick if (error = checkpath(ip, dp, tndp->ni_cred)) 81712816Smckusick goto out; 81837737Smckusick if (error = namei(tndp)) 81912816Smckusick goto out; 82037737Smckusick xp = NULL; 82137737Smckusick if (tndp->ni_vp) 82237737Smckusick xp = VTOI(tndp->ni_vp); 82337737Smckusick } while (dp != VTOI(tndp->ni_dvp)); 82412816Smckusick } 82511641Ssam /* 8269167Ssam * 2) If target doesn't exist, link the target 8279167Ssam * to the source and unlink the source. 8289167Ssam * Otherwise, rewrite the target directory 8299167Ssam * entry to reference the source inode and 8309167Ssam * expunge the original entry's existence. 8319167Ssam */ 8329167Ssam if (xp == NULL) { 83337737Smckusick if (dp->i_dev != ip->i_dev) 83437737Smckusick panic("rename: EXDEV"); 8359167Ssam /* 83616776Smckusick * Account for ".." in new directory. 83716776Smckusick * When source and destination have the same 83816776Smckusick * parent we don't fool with the link count. 8399167Ssam */ 84016776Smckusick if (doingdirectory && newparent) { 8419167Ssam dp->i_nlink++; 8429167Ssam dp->i_flag |= ICHG; 84337737Smckusick error = iupdat(dp, &time, &time, 1); 8449167Ssam } 84537737Smckusick if (error = direnter(ip, tndp)) 8469167Ssam goto out; 8479167Ssam } else { 84837737Smckusick if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) 84937737Smckusick panic("rename: EXDEV"); 8509167Ssam /* 85110590Ssam * Short circuit rename(foo, foo). 85210590Ssam */ 85310590Ssam if (xp->i_number == ip->i_number) 85437737Smckusick panic("rename: same file"); 85510590Ssam /* 85624433Sbloom * If the parent directory is "sticky", then the user must 85724433Sbloom * own the parent directory, or the destination of the rename, 85824433Sbloom * otherwise the destination may not be changed (except by 85924433Sbloom * root). This implements append-only directories. 86024433Sbloom */ 86137737Smckusick if ((dp->i_mode & ISVTX) && tndp->ni_cred->cr_uid != 0 && 86237737Smckusick tndp->ni_cred->cr_uid != dp->i_uid && 86337737Smckusick xp->i_uid != tndp->ni_cred->cr_uid) { 86424433Sbloom error = EPERM; 86524433Sbloom goto bad; 86624433Sbloom } 86724433Sbloom /* 86810051Ssam * Target must be empty if a directory 86910051Ssam * and have no links to it. 8709167Ssam * Also, insure source and target are 8719167Ssam * compatible (both directories, or both 8729167Ssam * not directories). 8739167Ssam */ 8749167Ssam if ((xp->i_mode&IFMT) == IFDIR) { 87537737Smckusick if (!dirempty(xp, dp->i_number, tndp->ni_cred) || 87637737Smckusick xp->i_nlink > 2) { 87710051Ssam error = ENOTEMPTY; 8789167Ssam goto bad; 8799167Ssam } 8809167Ssam if (!doingdirectory) { 88110051Ssam error = ENOTDIR; 8829167Ssam goto bad; 8839167Ssam } 88437737Smckusick cache_purge(ITOV(dp)); 8859167Ssam } else if (doingdirectory) { 88610051Ssam error = EISDIR; 8879167Ssam goto bad; 8889167Ssam } 88937737Smckusick if (error = dirrewrite(dp, ip, tndp)) 89037737Smckusick goto bad; 89137737Smckusick vput(ITOV(dp)); 8929167Ssam /* 89310051Ssam * Adjust the link count of the target to 89410051Ssam * reflect the dirrewrite above. If this is 89510051Ssam * a directory it is empty and there are 89610051Ssam * no links to it, so we can squash the inode and 89710051Ssam * any space associated with it. We disallowed 89810051Ssam * renaming over top of a directory with links to 89916776Smckusick * it above, as the remaining link would point to 90016776Smckusick * a directory without "." or ".." entries. 9019167Ssam */ 90210051Ssam xp->i_nlink--; 9039167Ssam if (doingdirectory) { 90410051Ssam if (--xp->i_nlink != 0) 90510051Ssam panic("rename: linked directory"); 90639674Smckusick error = itrunc(xp, (u_long)0, IO_SYNC); 90710051Ssam } 9089167Ssam xp->i_flag |= ICHG; 90938398Smckusick iput(xp); 91010246Ssam xp = NULL; 9119167Ssam } 9129167Ssam 9139167Ssam /* 9149167Ssam * 3) Unlink the source. 9159167Ssam */ 91637737Smckusick fndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF; 91737737Smckusick (void)namei(fndp); 91837737Smckusick if (fndp->ni_vp != NULL) { 91937737Smckusick xp = VTOI(fndp->ni_vp); 92037737Smckusick dp = VTOI(fndp->ni_dvp); 92137737Smckusick } else { 92238069Smckusick if (fndp->ni_dvp != NULL) 92338069Smckusick vput(fndp->ni_dvp); 92437737Smckusick xp = NULL; 92517758Smckusick dp = NULL; 92637737Smckusick } 9279167Ssam /* 92837737Smckusick * Ensure that the directory entry still exists and has not 92916776Smckusick * changed while the new name has been entered. If the source is 93016776Smckusick * a file then the entry may have been unlinked or renamed. In 93116776Smckusick * either case there is no further work to be done. If the source 93216776Smckusick * is a directory then it cannot have been rmdir'ed; its link 93316776Smckusick * count of three would cause a rmdir to fail with ENOTEMPTY. 93437737Smckusick * The IRENAME flag ensures that it cannot be moved by another 93516776Smckusick * rename. 9369167Ssam */ 93717758Smckusick if (xp != ip) { 93816776Smckusick if (doingdirectory) 93917758Smckusick panic("rename: lost dir entry"); 94016776Smckusick } else { 9419167Ssam /* 94216776Smckusick * If the source is a directory with a 94316776Smckusick * new parent, the link count of the old 94416776Smckusick * parent directory must be decremented 94516776Smckusick * and ".." set to point to the new parent. 9469167Ssam */ 94716776Smckusick if (doingdirectory && newparent) { 9489167Ssam dp->i_nlink--; 9499167Ssam dp->i_flag |= ICHG; 95039597Smckusick error = vn_rdwr(UIO_READ, ITOV(xp), (caddr_t)&dirbuf, 95137737Smckusick sizeof (struct dirtemplate), (off_t)0, 95239597Smckusick UIO_SYSSPACE, IO_NODELOCKED, 95339597Smckusick tndp->ni_cred, (int *)0); 95416776Smckusick if (error == 0) { 95516776Smckusick if (dirbuf.dotdot_namlen != 2 || 95616776Smckusick dirbuf.dotdot_name[0] != '.' || 95716776Smckusick dirbuf.dotdot_name[1] != '.') { 95839610Smckusick dirbad(xp, 12, "rename: mangled dir"); 95916776Smckusick } else { 96016776Smckusick dirbuf.dotdot_ino = newparent; 96139597Smckusick (void) vn_rdwr(UIO_WRITE, ITOV(xp), 96216776Smckusick (caddr_t)&dirbuf, 96316776Smckusick sizeof (struct dirtemplate), 96437740Smckusick (off_t)0, UIO_SYSSPACE, 96539597Smckusick IO_NODELOCKED|IO_SYNC, 96637737Smckusick tndp->ni_cred, (int *)0); 96737737Smckusick cache_purge(ITOV(dp)); 96816776Smckusick } 96916776Smckusick } 9709167Ssam } 97137737Smckusick error = dirremove(fndp); 97237737Smckusick if (!error) { 97316776Smckusick xp->i_nlink--; 97416776Smckusick xp->i_flag |= ICHG; 9759167Ssam } 97616776Smckusick xp->i_flag &= ~IRENAME; 9779167Ssam } 9789167Ssam if (dp) 97937737Smckusick vput(ITOV(dp)); 98016776Smckusick if (xp) 98137737Smckusick vput(ITOV(xp)); 98237737Smckusick vrele(ITOV(ip)); 98337737Smckusick return (error); 9849167Ssam 9859167Ssam bad: 9869167Ssam if (xp) 98737737Smckusick vput(ITOV(xp)); 98837737Smckusick vput(ITOV(dp)); 9899167Ssam out: 9909167Ssam ip->i_nlink--; 9919167Ssam ip->i_flag |= ICHG; 99237737Smckusick vrele(ITOV(ip)); 99337737Smckusick return (error); 9947701Ssam } 9957701Ssam 9967535Sroot /* 99712756Ssam * A virgin directory (no blushing please). 99812756Ssam */ 99912756Ssam struct dirtemplate mastertemplate = { 100012756Ssam 0, 12, 1, ".", 100112756Ssam 0, DIRBLKSIZ - 12, 2, ".." 100212756Ssam }; 100312756Ssam 100412756Ssam /* 100512756Ssam * Mkdir system call 100612756Ssam */ 100737737Smckusick ufs_mkdir(ndp, vap) 100837737Smckusick struct nameidata *ndp; 100937737Smckusick struct vattr *vap; 101012756Ssam { 101112756Ssam register struct inode *ip, *dp; 101237737Smckusick struct inode *tip; 101337737Smckusick struct vnode *dvp; 101412756Ssam struct dirtemplate dirtemplate; 101537737Smckusick int error; 101637737Smckusick int dmode; 101712756Ssam 101837737Smckusick dvp = ndp->ni_dvp; 101937737Smckusick dp = VTOI(dvp); 102037737Smckusick dmode = vap->va_mode&0777; 102137737Smckusick dmode |= IFDIR; 102212756Ssam /* 102312756Ssam * Must simulate part of maknode here 102412756Ssam * in order to acquire the inode, but 102512756Ssam * not have it entered in the parent 102612756Ssam * directory. The entry is made later 102712756Ssam * after writing "." and ".." entries out. 102812756Ssam */ 102937737Smckusick error = ialloc(dp, dirpref(dp->i_fs), dmode, &tip); 103037737Smckusick if (error) { 103112756Ssam iput(dp); 103237737Smckusick return (error); 103312756Ssam } 103437737Smckusick ip = tip; 103512756Ssam #ifdef QUOTA 103612756Ssam if (ip->i_dquot != NODQUOT) 103712756Ssam panic("mkdir: dquot"); 103812756Ssam #endif 103912756Ssam ip->i_flag |= IACC|IUPD|ICHG; 104037737Smckusick ip->i_mode = dmode; 104137737Smckusick ITOV(ip)->v_type = VDIR; /* Rest init'd in iget() */ 104212756Ssam ip->i_nlink = 2; 104337737Smckusick ip->i_uid = ndp->ni_cred->cr_uid; 104412756Ssam ip->i_gid = dp->i_gid; 104512756Ssam #ifdef QUOTA 104612756Ssam ip->i_dquot = inoquota(ip); 104712756Ssam #endif 104837737Smckusick error = iupdat(ip, &time, &time, 1); 104912756Ssam 105012756Ssam /* 105112756Ssam * Bump link count in parent directory 105212756Ssam * to reflect work done below. Should 105312756Ssam * be done before reference is created 105412756Ssam * so reparation is possible if we crash. 105512756Ssam */ 105612756Ssam dp->i_nlink++; 105712756Ssam dp->i_flag |= ICHG; 105837737Smckusick error = iupdat(dp, &time, &time, 1); 105912756Ssam 106012756Ssam /* 106112756Ssam * Initialize directory with "." 106212756Ssam * and ".." from static template. 106312756Ssam */ 106412756Ssam dirtemplate = mastertemplate; 106512756Ssam dirtemplate.dot_ino = ip->i_number; 106612756Ssam dirtemplate.dotdot_ino = dp->i_number; 106739597Smckusick error = vn_rdwr(UIO_WRITE, ITOV(ip), (caddr_t)&dirtemplate, 106837737Smckusick sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE, 106939597Smckusick IO_NODELOCKED|IO_SYNC, ndp->ni_cred, (int *)0); 107037737Smckusick if (error) { 107112756Ssam dp->i_nlink--; 107212756Ssam dp->i_flag |= ICHG; 107312756Ssam goto bad; 107412756Ssam } 107537737Smckusick if (DIRBLKSIZ > dp->i_fs->fs_fsize) 107637737Smckusick panic("mkdir: blksize"); /* XXX - should grow w/balloc() */ 107718103Smckusick else 107818103Smckusick ip->i_size = DIRBLKSIZ; 107912756Ssam /* 108012756Ssam * Directory all set up, now 108112756Ssam * install the entry for it in 108212756Ssam * the parent directory. 108312756Ssam */ 108437737Smckusick error = direnter(ip, ndp); 108512756Ssam dp = NULL; 108637737Smckusick if (error) { 108716694Smckusick ndp->ni_nameiop = LOOKUP | NOCACHE; 108837737Smckusick error = namei(ndp); 108937737Smckusick if (!error) { 109037737Smckusick dp = VTOI(ndp->ni_vp); 109112756Ssam dp->i_nlink--; 109212756Ssam dp->i_flag |= ICHG; 109312756Ssam } 109412756Ssam } 109512756Ssam bad: 109612756Ssam /* 109712756Ssam * No need to do an explicit itrunc here, 109837737Smckusick * vrele will do this for us because we set 109912756Ssam * the link count to 0. 110012756Ssam */ 110137737Smckusick if (error) { 110212756Ssam ip->i_nlink = 0; 110312756Ssam ip->i_flag |= ICHG; 110438144Smckusick iput(ip); 110538144Smckusick } else 110638144Smckusick ndp->ni_vp = ITOV(ip); 110712756Ssam if (dp) 110812756Ssam iput(dp); 110937737Smckusick return (error); 111012756Ssam } 111112756Ssam 111212756Ssam /* 111312756Ssam * Rmdir system call. 111412756Ssam */ 111537737Smckusick ufs_rmdir(ndp) 111637737Smckusick register struct nameidata *ndp; 111712756Ssam { 111812756Ssam register struct inode *ip, *dp; 111937737Smckusick int error = 0; 112012756Ssam 112137737Smckusick ip = VTOI(ndp->ni_vp); 112237737Smckusick dp = VTOI(ndp->ni_dvp); 112312756Ssam /* 112412756Ssam * No rmdir "." please. 112512756Ssam */ 112612756Ssam if (dp == ip) { 112737737Smckusick vrele(ITOV(dp)); 112812756Ssam iput(ip); 112937737Smckusick return (EINVAL); 113012756Ssam } 113112756Ssam /* 113212756Ssam * Verify the directory is empty (and valid). 113312756Ssam * (Rmdir ".." won't be valid since 113412756Ssam * ".." will contain a reference to 113512756Ssam * the current directory and thus be 113612756Ssam * non-empty.) 113712756Ssam */ 113837737Smckusick if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number, ndp->ni_cred)) { 113937737Smckusick error = ENOTEMPTY; 114012756Ssam goto out; 114112756Ssam } 114212756Ssam /* 114312756Ssam * Delete reference to directory before purging 114412756Ssam * inode. If we crash in between, the directory 114512756Ssam * will be reattached to lost+found, 114612756Ssam */ 114737737Smckusick if (error = dirremove(ndp)) 114812756Ssam goto out; 114912756Ssam dp->i_nlink--; 115012756Ssam dp->i_flag |= ICHG; 115137737Smckusick cache_purge(ITOV(dp)); 115212756Ssam iput(dp); 115337737Smckusick ndp->ni_dvp = NULL; 115412756Ssam /* 115512756Ssam * Truncate inode. The only stuff left 115612756Ssam * in the directory is "." and "..". The 115712756Ssam * "." reference is inconsequential since 115812756Ssam * we're quashing it. The ".." reference 115912756Ssam * has already been adjusted above. We've 116012756Ssam * removed the "." reference and the reference 116112756Ssam * in the parent directory, but there may be 116212756Ssam * other hard links so decrement by 2 and 116312756Ssam * worry about them later. 116412756Ssam */ 116512756Ssam ip->i_nlink -= 2; 116639674Smckusick error = itrunc(ip, (u_long)0, IO_SYNC); 116737737Smckusick cache_purge(ITOV(ip)); 116812756Ssam out: 116937737Smckusick if (ndp->ni_dvp) 117012756Ssam iput(dp); 117112756Ssam iput(ip); 117237737Smckusick return (error); 117312756Ssam } 117412756Ssam 117537737Smckusick /* 117637737Smckusick * symlink -- make a symbolic link 117737737Smckusick */ 117837737Smckusick ufs_symlink(ndp, vap, target) 117937737Smckusick struct nameidata *ndp; 118037737Smckusick struct vattr *vap; 118137737Smckusick char *target; 118212756Ssam { 118337737Smckusick struct inode *ip; 118437737Smckusick int error; 118512756Ssam 118637737Smckusick error = maknode(IFLNK | vap->va_mode, ndp, &ip); 118737737Smckusick if (error) 118837737Smckusick return (error); 118939597Smckusick error = vn_rdwr(UIO_WRITE, ITOV(ip), target, strlen(target), (off_t)0, 119039597Smckusick UIO_SYSSPACE, IO_NODELOCKED, ndp->ni_cred, (int *)0); 119137737Smckusick iput(ip); 119237737Smckusick return (error); 119337737Smckusick } 119437737Smckusick 119537737Smckusick /* 119637737Smckusick * Vnode op for read and write 119737737Smckusick */ 119839597Smckusick ufs_readdir(vp, uio, cred) 119937737Smckusick struct vnode *vp; 120037737Smckusick register struct uio *uio; 120137737Smckusick struct ucred *cred; 120237737Smckusick { 120339597Smckusick int count, lost, error; 120437737Smckusick 120537737Smckusick count = uio->uio_resid; 120637737Smckusick count &= ~(DIRBLKSIZ - 1); 120739597Smckusick lost = uio->uio_resid - count; 120839597Smckusick if (count < DIRBLKSIZ || (uio->uio_offset & (DIRBLKSIZ -1))) 120937737Smckusick return (EINVAL); 121037737Smckusick uio->uio_resid = count; 121137737Smckusick uio->uio_iov->iov_len = count; 121239597Smckusick error = ufs_read(vp, uio, 0, cred); 121339597Smckusick uio->uio_resid += lost; 121437737Smckusick return (error); 121537737Smckusick } 121637737Smckusick 121737737Smckusick /* 121837737Smckusick * Return target name of a symbolic link 121937737Smckusick */ 122037737Smckusick ufs_readlink(vp, uiop, cred) 122137737Smckusick struct vnode *vp; 122237737Smckusick struct uio *uiop; 122337737Smckusick struct ucred *cred; 122437737Smckusick { 122537737Smckusick 122639597Smckusick return (ufs_read(vp, uiop, 0, cred)); 122737737Smckusick } 122837737Smckusick 122937737Smckusick /* 123037737Smckusick * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually 123137737Smckusick * done. Iff ni_vp/ni_dvp not null and locked, unlock. 123237737Smckusick */ 123337737Smckusick ufs_abortop(ndp) 123437737Smckusick register struct nameidata *ndp; 123537737Smckusick { 123637737Smckusick register struct inode *ip; 123737737Smckusick 123837737Smckusick if (ndp->ni_vp) { 123937737Smckusick ip = VTOI(ndp->ni_vp); 124037737Smckusick if (ip->i_flag & ILOCKED) 124137737Smckusick IUNLOCK(ip); 124237737Smckusick vrele(ndp->ni_vp); 124312756Ssam } 124437737Smckusick if (ndp->ni_dvp) { 124537737Smckusick ip = VTOI(ndp->ni_dvp); 124637737Smckusick if (ip->i_flag & ILOCKED) 124737737Smckusick IUNLOCK(ip); 124837737Smckusick vrele(ndp->ni_dvp); 124937737Smckusick } 125037737Smckusick return; 125112756Ssam } 125212756Ssam 1253*39909Smckusick /* 1254*39909Smckusick * Lock an inode. 1255*39909Smckusick */ 125637737Smckusick ufs_lock(vp) 125737737Smckusick struct vnode *vp; 125837737Smckusick { 125937737Smckusick register struct inode *ip = VTOI(vp); 126037737Smckusick 126137737Smckusick ILOCK(ip); 126237737Smckusick return (0); 126337737Smckusick } 126437737Smckusick 1265*39909Smckusick /* 1266*39909Smckusick * Unlock an inode. 1267*39909Smckusick */ 126837737Smckusick ufs_unlock(vp) 126937737Smckusick struct vnode *vp; 127037737Smckusick { 127137737Smckusick register struct inode *ip = VTOI(vp); 127237737Smckusick 127337737Smckusick if (!(ip->i_flag & ILOCKED)) 127437737Smckusick panic("ufs_unlock NOT LOCKED"); 127537737Smckusick IUNLOCK(ip); 127637737Smckusick return (0); 127737737Smckusick } 127837737Smckusick 127912756Ssam /* 1280*39909Smckusick * Check for a locked inode. 1281*39909Smckusick */ 1282*39909Smckusick ufs_islocked(vp) 1283*39909Smckusick struct vnode *vp; 1284*39909Smckusick { 1285*39909Smckusick 1286*39909Smckusick if (VTOI(vp)->i_flag & ILOCKED) 1287*39909Smckusick return (1); 1288*39909Smckusick return (0); 1289*39909Smckusick } 1290*39909Smckusick 1291*39909Smckusick /* 129237737Smckusick * Get access to bmap 129312756Ssam */ 129437737Smckusick ufs_bmap(vp, bn, vpp, bnp) 129537737Smckusick struct vnode *vp; 129637737Smckusick daddr_t bn; 129737737Smckusick struct vnode **vpp; 129837737Smckusick daddr_t *bnp; 129912756Ssam { 130037737Smckusick struct inode *ip = VTOI(vp); 130112756Ssam 130237737Smckusick if (vpp != NULL) 130337737Smckusick *vpp = ip->i_devvp; 130437737Smckusick if (bnp == NULL) 130537737Smckusick return (0); 130637737Smckusick return (bmap(ip, bn, bnp, (daddr_t *)0, (int *)0)); 130712756Ssam } 130837737Smckusick 130937737Smckusick /* 131037737Smckusick * Just call the device strategy routine 131137737Smckusick */ 131239674Smckusick int checkoverlap = 1; 131339674Smckusick 131437737Smckusick ufs_strategy(bp) 131537737Smckusick register struct buf *bp; 131637737Smckusick { 131739674Smckusick register struct inode *ip = VTOI(bp->b_vp); 131839674Smckusick register struct buf *ep; 131939674Smckusick struct vnode *vp; 132039674Smckusick struct buf *ebp; 132139674Smckusick daddr_t start, last; 132239674Smckusick int error; 132339674Smckusick 132439674Smckusick if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR) 132539674Smckusick panic("ufs_strategy: spec"); 132639674Smckusick if (bp->b_blkno == bp->b_lblkno) { 132739674Smckusick if (error = bmap(ip, bp->b_lblkno, &bp->b_blkno)) 132839674Smckusick return (error); 132939896Smckusick if ((long)bp->b_blkno == -1) 133039674Smckusick clrbuf(bp); 133139674Smckusick } 133239896Smckusick if ((long)bp->b_blkno == -1) { 133339896Smckusick biodone(bp); 133439674Smckusick return (0); 133539896Smckusick } 133639674Smckusick if (checkoverlap) { 133739674Smckusick ebp = &buf[nbuf]; 133839674Smckusick start = bp->b_blkno; 133939674Smckusick last = start + btodb(bp->b_bcount) - 1; 134039674Smckusick for (ep = buf; ep < ebp; ep++) { 134139674Smckusick if (ep == bp || (ep->b_flags & B_INVAL) || 134239674Smckusick ep->b_vp == (struct vnode *)0) 134339674Smckusick continue; 134439674Smckusick if (VOP_BMAP(ep->b_vp, (daddr_t)0, &vp, (daddr_t)0)) 134539674Smckusick continue; 134639674Smckusick if (vp != ip->i_devvp) 134739674Smckusick continue; 134839674Smckusick /* look for overlap */ 134939674Smckusick if (ep->b_bcount == 0 || ep->b_blkno > last || 135039674Smckusick ep->b_blkno + btodb(ep->b_bcount) <= start) 135139674Smckusick continue; 135239896Smckusick vprint("Disk overlap", vp); 135339896Smckusick printf("\tstart %d, end %d overlap start %d, end %d\n", 135439896Smckusick start, last, ep->b_blkno, 135539896Smckusick ep->b_blkno + btodb(ep->b_bcount) - 1); 135639674Smckusick } 135739674Smckusick } 135839674Smckusick vp = ip->i_devvp; 135939674Smckusick bp->b_dev = vp->v_rdev; 136039674Smckusick (*(vp->v_op->vn_strategy))(bp); 136137737Smckusick return (0); 136237737Smckusick } 136337737Smckusick 136437737Smckusick /* 136539674Smckusick * Print out the contents of an inode. 136639674Smckusick */ 136739674Smckusick ufs_print(vp) 136839674Smckusick struct vnode *vp; 136939674Smckusick { 137039674Smckusick register struct inode *ip = VTOI(vp); 137139674Smckusick 137239674Smckusick printf("tag VT_UFS, ino %d, on dev %d, %d%s\n", ip->i_number, 137339674Smckusick major(ip->i_dev), minor(ip->i_dev), 137439674Smckusick (ip->i_flag & ILOCKED) ? " (LOCKED)" : ""); 137539900Smckusick if (ip->i_spare0 == 0) 137639900Smckusick return; 137739900Smckusick printf("\towner pid %d", ip->i_spare0); 137839900Smckusick if (ip->i_spare1) 137939900Smckusick printf(" waiting pid %d", ip->i_spare1); 138039900Smckusick printf("\n"); 138139674Smckusick } 138239674Smckusick 138339674Smckusick /* 138439628Smckusick * Read wrapper for special devices. 138539628Smckusick */ 138639628Smckusick ufsspec_read(vp, uio, ioflag, cred) 138739628Smckusick struct vnode *vp; 138839628Smckusick struct uio *uio; 138939628Smckusick int ioflag; 139039628Smckusick struct ucred *cred; 139139628Smckusick { 139239628Smckusick 139339628Smckusick /* 139439628Smckusick * Set access flag. 139539628Smckusick */ 139639628Smckusick VTOI(vp)->i_flag |= IACC; 139739628Smckusick return (spec_read(vp, uio, ioflag, cred)); 139839628Smckusick } 139939628Smckusick 140039628Smckusick /* 140139628Smckusick * Write wrapper for special devices. 140239628Smckusick */ 140339628Smckusick ufsspec_write(vp, uio, ioflag, cred) 140439628Smckusick struct vnode *vp; 140539628Smckusick struct uio *uio; 140639628Smckusick int ioflag; 140739628Smckusick struct ucred *cred; 140839628Smckusick { 140939628Smckusick 141039628Smckusick /* 141139628Smckusick * Set update and change flags. 141239628Smckusick */ 141339628Smckusick VTOI(vp)->i_flag |= IUPD|ICHG; 141439628Smckusick return (spec_write(vp, uio, ioflag, cred)); 141539628Smckusick } 141639628Smckusick 141739628Smckusick /* 141839628Smckusick * Close wrapper for special devices. 141939628Smckusick * 142039628Smckusick * Update the times on the inode then do device close. 142139628Smckusick */ 142239628Smckusick ufsspec_close(vp, fflag, cred) 142339628Smckusick struct vnode *vp; 142439628Smckusick int fflag; 142539628Smckusick struct ucred *cred; 142639628Smckusick { 142739628Smckusick register struct inode *ip = VTOI(vp); 142839628Smckusick 142939815Smckusick if (vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) 143039628Smckusick ITIMES(ip, &time, &time); 143139628Smckusick return (spec_close(vp, fflag, cred)); 143239628Smckusick } 143339628Smckusick 143439628Smckusick /* 143537737Smckusick * Make a new file. 143637737Smckusick */ 143737737Smckusick maknode(mode, ndp, ipp) 143837737Smckusick int mode; 143937737Smckusick register struct nameidata *ndp; 144037737Smckusick struct inode **ipp; 144137737Smckusick { 144237737Smckusick register struct inode *ip; 144337737Smckusick struct inode *tip; 144437737Smckusick register struct inode *pdir = VTOI(ndp->ni_dvp); 144537737Smckusick ino_t ipref; 144637737Smckusick int error; 144737737Smckusick 144837737Smckusick *ipp = 0; 144937737Smckusick if ((mode & IFMT) == IFDIR) 145037737Smckusick ipref = dirpref(pdir->i_fs); 145137737Smckusick else 145237737Smckusick ipref = pdir->i_number; 145337737Smckusick error = ialloc(pdir, ipref, mode, &tip); 145437737Smckusick if (error) { 145537737Smckusick iput(pdir); 145637737Smckusick return (error); 145737737Smckusick } 145837737Smckusick ip = tip; 145937737Smckusick #ifdef QUOTA 146037737Smckusick if (ip->i_dquot != NODQUOT) 146137737Smckusick panic("maknode: dquot"); 146237737Smckusick #endif 146337737Smckusick ip->i_flag |= IACC|IUPD|ICHG; 146437737Smckusick if ((mode & IFMT) == 0) 146537737Smckusick mode |= IFREG; 146637737Smckusick ip->i_mode = mode; 146737737Smckusick ITOV(ip)->v_type = IFTOVT(mode); /* Rest init'd in iget() */ 146837737Smckusick ip->i_nlink = 1; 146937737Smckusick ip->i_uid = ndp->ni_cred->cr_uid; 147037737Smckusick ip->i_gid = pdir->i_gid; 147137737Smckusick if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, ndp->ni_cred) && 147237737Smckusick suser(ndp->ni_cred, NULL)) 147337737Smckusick ip->i_mode &= ~ISGID; 147437737Smckusick #ifdef QUOTA 147537737Smckusick ip->i_dquot = inoquota(ip); 147637737Smckusick #endif 147737737Smckusick 147837737Smckusick /* 147937737Smckusick * Make sure inode goes to disk before directory entry. 148037737Smckusick */ 148137737Smckusick if ((error = iupdat(ip, &time, &time, 1)) || 148237737Smckusick (error = direnter(ip, ndp))) { 148337737Smckusick /* 148437737Smckusick * Write error occurred trying to update the inode 148537737Smckusick * or the directory so must deallocate the inode. 148637737Smckusick */ 148737737Smckusick ip->i_nlink = 0; 148837737Smckusick ip->i_flag |= ICHG; 148937737Smckusick iput(ip); 149037737Smckusick return (error); 149137737Smckusick } 149237737Smckusick *ipp = ip; 149337737Smckusick return (0); 149437737Smckusick } 1495