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*39674Smckusick * @(#)lfs_vnops.c 7.25 (Berkeley) 11/30/89 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(), 71*39674Smckusick ufs_strategy(), 72*39674Smckusick ufs_print(); 736254Sroot 7437737Smckusick struct vnodeops ufs_vnodeops = { 75*39674Smckusick ufs_lookup, /* lookup */ 76*39674Smckusick ufs_create, /* create */ 77*39674Smckusick ufs_mknod, /* mknod */ 78*39674Smckusick ufs_open, /* open */ 79*39674Smckusick ufs_close, /* close */ 80*39674Smckusick ufs_access, /* access */ 81*39674Smckusick ufs_getattr, /* getattr */ 82*39674Smckusick ufs_setattr, /* setattr */ 83*39674Smckusick ufs_read, /* read */ 84*39674Smckusick ufs_write, /* write */ 85*39674Smckusick ufs_ioctl, /* ioctl */ 86*39674Smckusick ufs_select, /* select */ 87*39674Smckusick ufs_mmap, /* mmap */ 88*39674Smckusick ufs_fsync, /* fsync */ 89*39674Smckusick ufs_seek, /* seek */ 90*39674Smckusick ufs_remove, /* remove */ 91*39674Smckusick ufs_link, /* link */ 92*39674Smckusick ufs_rename, /* rename */ 93*39674Smckusick ufs_mkdir, /* mkdir */ 94*39674Smckusick ufs_rmdir, /* rmdir */ 95*39674Smckusick ufs_symlink, /* symlink */ 96*39674Smckusick ufs_readdir, /* readdir */ 97*39674Smckusick ufs_readlink, /* readlink */ 98*39674Smckusick ufs_abortop, /* abortop */ 99*39674Smckusick ufs_inactive, /* inactive */ 100*39674Smckusick ufs_reclaim, /* reclaim */ 101*39674Smckusick ufs_lock, /* lock */ 102*39674Smckusick ufs_unlock, /* unlock */ 103*39674Smckusick ufs_bmap, /* bmap */ 104*39674Smckusick ufs_strategy, /* strategy */ 105*39674Smckusick ufs_print, /* print */ 10637737Smckusick }; 1076254Sroot 10839435Smckusick int spec_lookup(), 10939435Smckusick spec_open(), 11039628Smckusick ufsspec_read(), 11139628Smckusick ufsspec_write(), 11239435Smckusick spec_strategy(), 113*39674Smckusick spec_bmap(), 11439435Smckusick spec_ioctl(), 11539435Smckusick spec_select(), 11639628Smckusick ufsspec_close(), 11739435Smckusick spec_badop(), 11839435Smckusick spec_nullop(); 11939435Smckusick 12039435Smckusick struct vnodeops spec_inodeops = { 12139597Smckusick spec_lookup, /* lookup */ 12239597Smckusick spec_badop, /* create */ 12339597Smckusick spec_badop, /* mknod */ 12439597Smckusick spec_open, /* open */ 12539628Smckusick ufsspec_close, /* close */ 12639597Smckusick ufs_access, /* access */ 12739597Smckusick ufs_getattr, /* getattr */ 12839597Smckusick ufs_setattr, /* setattr */ 12939628Smckusick ufsspec_read, /* read */ 13039628Smckusick ufsspec_write, /* write */ 13139597Smckusick spec_ioctl, /* ioctl */ 13239597Smckusick spec_select, /* select */ 13339597Smckusick spec_badop, /* mmap */ 13439597Smckusick spec_nullop, /* fsync */ 13539597Smckusick spec_badop, /* seek */ 13639597Smckusick spec_badop, /* remove */ 13739597Smckusick spec_badop, /* link */ 13839597Smckusick spec_badop, /* rename */ 13939597Smckusick spec_badop, /* mkdir */ 14039597Smckusick spec_badop, /* rmdir */ 14139597Smckusick spec_badop, /* symlink */ 14239597Smckusick spec_badop, /* readdir */ 14339597Smckusick spec_badop, /* readlink */ 14439597Smckusick spec_badop, /* abortop */ 14539597Smckusick ufs_inactive, /* inactive */ 14639597Smckusick ufs_reclaim, /* reclaim */ 14739597Smckusick ufs_lock, /* lock */ 14839597Smckusick ufs_unlock, /* unlock */ 149*39674Smckusick spec_bmap, /* bmap */ 15039597Smckusick spec_strategy, /* strategy */ 151*39674Smckusick ufs_print, /* print */ 15239435Smckusick }; 15339435Smckusick 15437737Smckusick enum vtype iftovt_tab[8] = { 15537737Smckusick VNON, VCHR, VDIR, VBLK, VREG, VLNK, VSOCK, VBAD, 15637737Smckusick }; 15737737Smckusick int vttoif_tab[8] = { 15837737Smckusick 0, IFREG, IFDIR, IFBLK, IFCHR, IFLNK, IFSOCK, IFMT, 15937737Smckusick }; 1606254Sroot 1619167Ssam /* 16237737Smckusick * Create a regular file 1639167Ssam */ 16437737Smckusick ufs_create(ndp, vap) 16537737Smckusick struct nameidata *ndp; 16637737Smckusick struct vattr *vap; 1676254Sroot { 16837737Smckusick struct inode *ip; 16937737Smckusick int error; 1706254Sroot 17137737Smckusick if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip)) 17237737Smckusick return (error); 17337737Smckusick ndp->ni_vp = ITOV(ip); 17437737Smckusick return (0); 1756254Sroot } 1766254Sroot 17737Sbill /* 17837737Smckusick * Mknod vnode call 1796254Sroot */ 18037737Smckusick /* ARGSUSED */ 18137737Smckusick ufs_mknod(ndp, vap, cred) 18237737Smckusick struct nameidata *ndp; 18337737Smckusick struct ucred *cred; 18437737Smckusick struct vattr *vap; 1856254Sroot { 18639435Smckusick register struct vnode *vp; 18737737Smckusick struct inode *ip; 18837737Smckusick int error; 1896254Sroot 19037737Smckusick if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip)) 19137737Smckusick return (error); 19239435Smckusick vp = ITOV(ip); 19337737Smckusick if (vap->va_rdev) { 19437737Smckusick /* 19537737Smckusick * Want to be able to use this to make badblock 19637737Smckusick * inodes, so don't truncate the dev number. 19737737Smckusick */ 19839608Smckusick ip->i_rdev = vap->va_rdev; 19937737Smckusick ip->i_flag |= IACC|IUPD|ICHG; 20012756Ssam } 20137737Smckusick /* 20237737Smckusick * Remove inode so that it will be reloaded by iget and 20337737Smckusick * checked to see if it is an alias of an existing entry 20437737Smckusick * in the inode cache. 20537737Smckusick */ 20638809Smckusick iput(ip); 20739435Smckusick vp->v_type = VNON; 20839435Smckusick vgone(vp); 20937737Smckusick return (0); 2106254Sroot } 2116254Sroot 2126254Sroot /* 21337737Smckusick * Open called. 21437737Smckusick * 21537737Smckusick * Nothing to do. 2166254Sroot */ 21737737Smckusick /* ARGSUSED */ 21837737Smckusick ufs_open(vp, mode, cred) 21937737Smckusick struct vnode *vp; 22037737Smckusick int mode; 22137737Smckusick struct ucred *cred; 2226254Sroot { 2236254Sroot 22437737Smckusick return (0); 2256254Sroot } 2266254Sroot 2276254Sroot /* 22837737Smckusick * Close called 22937737Smckusick * 23037737Smckusick * Update the times on the inode. 2316254Sroot */ 23237737Smckusick /* ARGSUSED */ 23337737Smckusick ufs_close(vp, fflag, cred) 23437737Smckusick struct vnode *vp; 23537737Smckusick int fflag; 23637737Smckusick struct ucred *cred; 2376254Sroot { 23837737Smckusick register struct inode *ip = VTOI(vp); 2396254Sroot 24037737Smckusick if (vp->v_count > 1 && !(ip->i_flag & ILOCKED)) 24137737Smckusick ITIMES(ip, &time, &time); 24237737Smckusick return (0); 2436254Sroot } 2446254Sroot 24537737Smckusick ufs_access(vp, mode, cred) 24637737Smckusick struct vnode *vp; 24737737Smckusick int mode; 24837737Smckusick struct ucred *cred; 2496254Sroot { 2506254Sroot 25137737Smckusick return (iaccess(VTOI(vp), mode, cred)); 2526254Sroot } 2536254Sroot 25437737Smckusick /* ARGSUSED */ 25537737Smckusick ufs_getattr(vp, vap, cred) 25637737Smckusick struct vnode *vp; 25737737Smckusick register struct vattr *vap; 25837737Smckusick struct ucred *cred; 2596254Sroot { 26037737Smckusick register struct inode *ip = VTOI(vp); 2616254Sroot 26237737Smckusick ITIMES(ip, &time, &time); 2636254Sroot /* 26437737Smckusick * Copy from inode table 2656254Sroot */ 26637737Smckusick vap->va_fsid = ip->i_dev; 26737737Smckusick vap->va_fileid = ip->i_number; 26837737Smckusick vap->va_mode = ip->i_mode & ~IFMT; 26937737Smckusick vap->va_nlink = ip->i_nlink; 27037737Smckusick vap->va_uid = ip->i_uid; 27137737Smckusick vap->va_gid = ip->i_gid; 27237737Smckusick vap->va_rdev = (dev_t)ip->i_rdev; 27339391Smckusick vap->va_size = ip->i_din.di_qsize.val[0]; 27439391Smckusick vap->va_size1 = ip->i_din.di_qsize.val[1]; 27537737Smckusick vap->va_atime.tv_sec = ip->i_atime; 27638578Smckusick vap->va_atime.tv_usec = 0; 27737737Smckusick vap->va_mtime.tv_sec = ip->i_mtime; 27838578Smckusick vap->va_mtime.tv_usec = 0; 27937737Smckusick vap->va_ctime.tv_sec = ip->i_ctime; 28038578Smckusick vap->va_ctime.tv_usec = 0; 28138254Smckusick vap->va_flags = ip->i_flags; 28238254Smckusick vap->va_gen = ip->i_gen; 28337737Smckusick /* this doesn't belong here */ 28437737Smckusick if (vp->v_type == VBLK) 28537737Smckusick vap->va_blocksize = BLKDEV_IOSIZE; 28637737Smckusick else if (vp->v_type == VCHR) 28737737Smckusick vap->va_blocksize = MAXBSIZE; 2887142Smckusick else 28937737Smckusick vap->va_blocksize = ip->i_fs->fs_bsize; 29038657Smckusick vap->va_bytes = dbtob(ip->i_blocks); 29137737Smckusick vap->va_bytes1 = -1; 29237737Smckusick vap->va_type = vp->v_type; 29337737Smckusick return (0); 2946254Sroot } 2956254Sroot 2966254Sroot /* 29737737Smckusick * Set attribute vnode op. called from several syscalls 2986254Sroot */ 29937737Smckusick ufs_setattr(vp, vap, cred) 30037737Smckusick register struct vnode *vp; 30137737Smckusick register struct vattr *vap; 30237737Smckusick register struct ucred *cred; 3036254Sroot { 30437737Smckusick register struct inode *ip = VTOI(vp); 30537737Smckusick int error = 0; 3066254Sroot 30737737Smckusick /* 30837737Smckusick * Check for unsetable attributes. 30937737Smckusick */ 31037737Smckusick if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || 31137737Smckusick (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || 31237737Smckusick (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || 31338254Smckusick ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { 31437737Smckusick return (EINVAL); 31516540Ssam } 31637737Smckusick /* 31737737Smckusick * Go through the fields and update iff not VNOVAL. 31837737Smckusick */ 31937737Smckusick if (vap->va_uid != (u_short)VNOVAL || vap->va_gid != (u_short)VNOVAL) 32037737Smckusick if (error = chown1(vp, vap->va_uid, vap->va_gid, cred)) 32137737Smckusick return (error); 32237737Smckusick if (vap->va_size != VNOVAL) { 32337737Smckusick if (vp->v_type == VDIR) 32437737Smckusick return (EISDIR); 325*39674Smckusick if (error = itrunc(ip, vap->va_size, 0)) /* XXX IO_SYNC? */ 32637737Smckusick return (error); 32713878Ssam } 32837737Smckusick if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { 32937773Smckusick if (cred->cr_uid != ip->i_uid && 33037773Smckusick (error = suser(cred, &u.u_acflag))) 33137773Smckusick return (error); 33237737Smckusick if (vap->va_atime.tv_sec != VNOVAL) 33337737Smckusick ip->i_flag |= IACC; 33437737Smckusick if (vap->va_mtime.tv_sec != VNOVAL) 33537737Smckusick ip->i_flag |= IUPD; 33637737Smckusick ip->i_flag |= ICHG; 33737737Smckusick if (error = iupdat(ip, &vap->va_atime, &vap->va_mtime, 1)) 33837737Smckusick return (error); 3396254Sroot } 34037737Smckusick if (vap->va_mode != (u_short)VNOVAL) 34137737Smckusick error = chmod1(vp, (int)vap->va_mode, cred); 34238254Smckusick if (vap->va_flags != VNOVAL) { 34338254Smckusick if (cred->cr_uid != ip->i_uid && 34438254Smckusick (error = suser(cred, &u.u_acflag))) 34538254Smckusick return (error); 34638254Smckusick if (cred->cr_uid == 0) { 34738254Smckusick ip->i_flags = vap->va_flags; 34838254Smckusick } else { 34938254Smckusick ip->i_flags &= 0xffff0000; 35038254Smckusick ip->i_flags |= (vap->va_flags & 0xffff); 35138254Smckusick } 35238254Smckusick ip->i_flag |= ICHG; 35338254Smckusick } 35437737Smckusick return (error); 3556254Sroot } 3566254Sroot 3576254Sroot /* 3589167Ssam * Change the mode on a file. 3599167Ssam * Inode must be locked before calling. 3609167Ssam */ 36137737Smckusick chmod1(vp, mode, cred) 36237737Smckusick register struct vnode *vp; 3637701Ssam register int mode; 36437737Smckusick struct ucred *cred; 3657701Ssam { 36637737Smckusick register struct inode *ip = VTOI(vp); 36737773Smckusick int error; 3687868Sroot 36937773Smckusick if (cred->cr_uid != ip->i_uid && 37037773Smckusick (error = suser(cred, &u.u_acflag))) 37137773Smckusick return (error); 3726254Sroot ip->i_mode &= ~07777; 37337737Smckusick if (cred->cr_uid) { 37437737Smckusick if (vp->v_type != VDIR) 37521015Smckusick mode &= ~ISVTX; 37637737Smckusick if (!groupmember(ip->i_gid, cred)) 37711811Ssam mode &= ~ISGID; 3787439Sroot } 37937737Smckusick ip->i_mode |= mode & 07777; 3806254Sroot ip->i_flag |= ICHG; 38137737Smckusick if ((vp->v_flag & VTEXT) && (ip->i_mode & ISVTX) == 0) 38237737Smckusick xrele(vp); 38321015Smckusick return (0); 3845992Swnj } 3855992Swnj 3869167Ssam /* 3877701Ssam * Perform chown operation on inode ip; 3887701Ssam * inode must be locked prior to call. 3897701Ssam */ 39037737Smckusick chown1(vp, uid, gid, cred) 39137737Smckusick register struct vnode *vp; 39237737Smckusick uid_t uid; 39337737Smckusick gid_t gid; 39437737Smckusick struct ucred *cred; 3957701Ssam { 39637737Smckusick register struct inode *ip = VTOI(vp); 3977701Ssam #ifdef QUOTA 3987701Ssam register long change; 39911811Ssam #endif 40037737Smckusick int error; 4017701Ssam 40237737Smckusick if (uid == (u_short)VNOVAL) 40311811Ssam uid = ip->i_uid; 40437737Smckusick if (gid == (u_short)VNOVAL) 40511811Ssam gid = ip->i_gid; 40636614Sbostic /* 40736614Sbostic * If we don't own the file, are trying to change the owner 40836614Sbostic * of the file, or are not a member of the target group, 40936614Sbostic * the caller must be superuser or the call fails. 41036614Sbostic */ 41137737Smckusick if ((cred->cr_uid != ip->i_uid || uid != ip->i_uid || 41237737Smckusick !groupmember((gid_t)gid, cred)) && 41337737Smckusick (error = suser(cred, &u.u_acflag))) 41437737Smckusick return (error); 41511811Ssam #ifdef QUOTA 41614385Ssam if (ip->i_uid == uid) /* this just speeds things a little */ 4177482Skre change = 0; 41812646Ssam else 41912646Ssam change = ip->i_blocks; 42012646Ssam (void) chkdq(ip, -change, 1); 42112646Ssam (void) chkiq(ip->i_dev, ip, ip->i_uid, 1); 4227482Skre dqrele(ip->i_dquot); 4237482Skre #endif 42439306Smckusick if (ip->i_uid != uid && cred->cr_uid != 0) 42539306Smckusick ip->i_mode &= ~ISUID; 42639306Smckusick if (ip->i_gid != gid && cred->cr_uid != 0) 42739306Smckusick ip->i_mode &= ~ISGID; 42811811Ssam ip->i_uid = uid; 42911811Ssam ip->i_gid = gid; 4306254Sroot ip->i_flag |= ICHG; 4317701Ssam #ifdef QUOTA 4327482Skre ip->i_dquot = inoquota(ip); 43312646Ssam (void) chkdq(ip, change, 1); 43426361Skarels (void) chkiq(ip->i_dev, (struct inode *)NULL, (uid_t)uid, 1); 43512646Ssam return (u.u_error); /* should == 0 ALWAYS !! */ 43612646Ssam #else 43712646Ssam return (0); 4387482Skre #endif 43937Sbill } 44037Sbill 44139608Smckusick /* 44239608Smckusick * Vnode op for reading. 44339608Smckusick */ 44437737Smckusick /* ARGSUSED */ 44539608Smckusick ufs_read(vp, uio, ioflag, cred) 44639608Smckusick struct vnode *vp; 44739608Smckusick register struct uio *uio; 44839608Smckusick int ioflag; 44939608Smckusick struct ucred *cred; 45039608Smckusick { 45139608Smckusick register struct inode *ip = VTOI(vp); 45239608Smckusick register struct fs *fs; 45339608Smckusick struct buf *bp; 45439608Smckusick daddr_t lbn, bn, rablock; 45539608Smckusick int size, rasize, diff, error = 0; 45639608Smckusick long n, on, type; 45739608Smckusick 45839608Smckusick if (uio->uio_rw != UIO_READ) 45939608Smckusick panic("ufs_read mode"); 46039608Smckusick type = ip->i_mode & IFMT; 46139608Smckusick if (type != IFDIR && type != IFREG && type != IFLNK) 46239608Smckusick panic("ufs_read type"); 46339608Smckusick if (uio->uio_resid == 0) 46439608Smckusick return (0); 46539608Smckusick if (uio->uio_offset < 0) 46639608Smckusick return (EINVAL); 46739608Smckusick ip->i_flag |= IACC; 46839608Smckusick fs = ip->i_fs; 46939608Smckusick do { 47039608Smckusick lbn = lblkno(fs, uio->uio_offset); 47139608Smckusick on = blkoff(fs, uio->uio_offset); 47239608Smckusick n = MIN((unsigned)(fs->fs_bsize - on), uio->uio_resid); 47339608Smckusick diff = ip->i_size - uio->uio_offset; 47439608Smckusick if (diff <= 0) 47539608Smckusick return (0); 47639608Smckusick if (diff < n) 47739608Smckusick n = diff; 47839608Smckusick size = blksize(fs, ip, lbn); 479*39674Smckusick rablock = lbn + 1; 480*39674Smckusick rasize = blksize(fs, ip, rablock); 481*39674Smckusick if (ip->i_lastr + 1 == lbn) 482*39674Smckusick error = breada(ITOV(ip), lbn, size, rablock, rasize, 48339608Smckusick NOCRED, &bp); 48439608Smckusick else 485*39674Smckusick error = bread(ITOV(ip), lbn, size, NOCRED, &bp); 48639608Smckusick ip->i_lastr = lbn; 48739608Smckusick n = MIN(n, size - bp->b_resid); 48839608Smckusick if (error) { 48939608Smckusick brelse(bp); 49039608Smckusick return (error); 49139608Smckusick } 49239608Smckusick error = uiomove(bp->b_un.b_addr + on, (int)n, uio); 49339608Smckusick if (n + on == fs->fs_bsize || uio->uio_offset == ip->i_size) 49439608Smckusick bp->b_flags |= B_AGE; 49539608Smckusick brelse(bp); 49639608Smckusick } while (error == 0 && uio->uio_resid > 0 && n != 0); 49739608Smckusick return (error); 49839608Smckusick } 49939608Smckusick 50039608Smckusick /* 50139608Smckusick * Vnode op for writing. 50239608Smckusick */ 50339608Smckusick ufs_write(vp, uio, ioflag, cred) 50439608Smckusick register struct vnode *vp; 50539608Smckusick struct uio *uio; 50639608Smckusick int ioflag; 50739608Smckusick struct ucred *cred; 50839608Smckusick { 50939608Smckusick register struct inode *ip = VTOI(vp); 51039608Smckusick register struct fs *fs; 51139608Smckusick struct buf *bp; 51239608Smckusick daddr_t lbn, bn; 51339608Smckusick u_long osize; 51439608Smckusick int i, n, on, flags; 51539608Smckusick int count, size, resid, error = 0; 51639608Smckusick 51739608Smckusick if (uio->uio_rw != UIO_WRITE) 51839608Smckusick panic("ufs_write mode"); 51939608Smckusick switch (vp->v_type) { 52039608Smckusick case VREG: 52139608Smckusick if (ioflag & IO_APPEND) 52239608Smckusick uio->uio_offset = ip->i_size; 52339608Smckusick /* fall through */ 52439608Smckusick case VLNK: 52539608Smckusick break; 52639608Smckusick 52739608Smckusick case VDIR: 52839608Smckusick if ((ioflag & IO_SYNC) == 0) 52939608Smckusick panic("ufs_write nonsync dir write"); 53039608Smckusick break; 53139608Smckusick 53239608Smckusick default: 53339608Smckusick panic("ufs_write type"); 53439608Smckusick } 53539608Smckusick if (uio->uio_offset < 0) 53639608Smckusick return (EINVAL); 53739608Smckusick if (uio->uio_resid == 0) 53839608Smckusick return (0); 53939608Smckusick /* 54039608Smckusick * Maybe this should be above the vnode op call, but so long as 54139608Smckusick * file servers have no limits, i don't think it matters 54239608Smckusick */ 54339608Smckusick if (vp->v_type == VREG && 54439608Smckusick uio->uio_offset + uio->uio_resid > 54539608Smckusick u.u_rlimit[RLIMIT_FSIZE].rlim_cur) { 54639608Smckusick psignal(u.u_procp, SIGXFSZ); 54739608Smckusick return (EFBIG); 54839608Smckusick } 54939608Smckusick resid = uio->uio_resid; 55039608Smckusick osize = ip->i_size; 55139608Smckusick fs = ip->i_fs; 552*39674Smckusick flags = 0; 553*39674Smckusick if (ioflag & IO_SYNC) 554*39674Smckusick flags = B_SYNC; 55539608Smckusick do { 55639608Smckusick lbn = lblkno(fs, uio->uio_offset); 55739608Smckusick on = blkoff(fs, uio->uio_offset); 55839608Smckusick n = MIN((unsigned)(fs->fs_bsize - on), uio->uio_resid); 55939608Smckusick if (n < fs->fs_bsize) 560*39674Smckusick flags |= B_CLRBUF; 56139608Smckusick else 562*39674Smckusick flags &= ~B_CLRBUF; 563*39674Smckusick if (error = balloc(ip, lbn, (int)(on + n), &bp, flags)) 56439608Smckusick break; 565*39674Smckusick bn = bp->b_blkno; 56639608Smckusick if (uio->uio_offset + n > ip->i_size) 56739608Smckusick ip->i_size = uio->uio_offset + n; 56839608Smckusick size = blksize(fs, ip, lbn); 56939608Smckusick count = howmany(size, CLBYTES); 57039608Smckusick for (i = 0; i < count; i++) 57139608Smckusick munhash(ip->i_devvp, bn + i * CLBYTES / DEV_BSIZE); 57239608Smckusick n = MIN(n, size - bp->b_resid); 57339608Smckusick error = uiomove(bp->b_un.b_addr + on, n, uio); 57439608Smckusick if (ioflag & IO_SYNC) 57539608Smckusick (void) bwrite(bp); 57639608Smckusick else if (n + on == fs->fs_bsize) { 57739608Smckusick bp->b_flags |= B_AGE; 57839608Smckusick bawrite(bp); 57939608Smckusick } else 58039608Smckusick bdwrite(bp); 58139608Smckusick ip->i_flag |= IUPD|ICHG; 58239608Smckusick if (cred->cr_uid != 0) 58339608Smckusick ip->i_mode &= ~(ISUID|ISGID); 58439608Smckusick } while (error == 0 && uio->uio_resid > 0 && n != 0); 58539608Smckusick if (error && (ioflag & IO_UNIT)) { 586*39674Smckusick (void) itrunc(ip, osize, ioflag & IO_SYNC); 58739608Smckusick uio->uio_offset -= resid - uio->uio_resid; 58839608Smckusick uio->uio_resid = resid; 58939608Smckusick } 59039608Smckusick return (error); 59139608Smckusick } 59239608Smckusick 59339608Smckusick /* ARGSUSED */ 59437737Smckusick ufs_ioctl(vp, com, data, fflag, cred) 59537737Smckusick struct vnode *vp; 59637737Smckusick int com; 59737737Smckusick caddr_t data; 59837737Smckusick int fflag; 59937737Smckusick struct ucred *cred; 60011811Ssam { 60111811Ssam 60237737Smckusick return (ENOTTY); 60311811Ssam } 60411811Ssam 60537737Smckusick /* ARGSUSED */ 60637737Smckusick ufs_select(vp, which, cred) 60737737Smckusick struct vnode *vp; 60837737Smckusick int which; 60937737Smckusick struct ucred *cred; 61037737Smckusick { 61137737Smckusick 61237737Smckusick return (1); /* XXX */ 61337737Smckusick } 61437737Smckusick 6159167Ssam /* 61637737Smckusick * Mmap a file 61737737Smckusick * 61837737Smckusick * NB Currently unsupported. 6199167Ssam */ 62037737Smckusick /* ARGSUSED */ 62137737Smckusick ufs_mmap(vp, fflags, cred) 62237737Smckusick struct vnode *vp; 62337737Smckusick int fflags; 62437737Smckusick struct ucred *cred; 62537Sbill { 62637Sbill 62737737Smckusick return (EINVAL); 62837Sbill } 6297535Sroot 6309167Ssam /* 63137737Smckusick * Synch an open file. 6329167Ssam */ 63337737Smckusick /* ARGSUSED */ 63439597Smckusick ufs_fsync(vp, fflags, cred, waitfor) 63537737Smckusick struct vnode *vp; 63637737Smckusick int fflags; 63737737Smckusick struct ucred *cred; 63839597Smckusick int waitfor; 6397701Ssam { 64039597Smckusick struct inode *ip = VTOI(vp); 6417701Ssam 64237737Smckusick if (fflags&FWRITE) 64337737Smckusick ip->i_flag |= ICHG; 644*39674Smckusick vflushbuf(vp, waitfor == MNT_WAIT ? B_SYNC : 0); 645*39674Smckusick return (iupdat(ip, &time, &time, waitfor == MNT_WAIT)); 6467701Ssam } 6477701Ssam 6489167Ssam /* 64937737Smckusick * Seek on a file 65037737Smckusick * 65137737Smckusick * Nothing to do, so just return. 6529167Ssam */ 65337737Smckusick /* ARGSUSED */ 65437737Smckusick ufs_seek(vp, oldoff, newoff, cred) 65537737Smckusick struct vnode *vp; 65637737Smckusick off_t oldoff, newoff; 65737737Smckusick struct ucred *cred; 6587701Ssam { 6597701Ssam 66037737Smckusick return (0); 66137737Smckusick } 66237737Smckusick 66337737Smckusick /* 66437737Smckusick * ufs remove 66537737Smckusick * Hard to avoid races here, especially 66637737Smckusick * in unlinking directories. 66737737Smckusick */ 66837737Smckusick ufs_remove(ndp) 66937737Smckusick struct nameidata *ndp; 67037737Smckusick { 67137737Smckusick register struct inode *ip, *dp; 67237737Smckusick int error; 67337737Smckusick 67437737Smckusick ip = VTOI(ndp->ni_vp); 67537737Smckusick dp = VTOI(ndp->ni_dvp); 67637737Smckusick error = dirremove(ndp); 67737737Smckusick if (!error) { 67837737Smckusick ip->i_nlink--; 67937737Smckusick ip->i_flag |= ICHG; 6807701Ssam } 68137737Smckusick if (dp == ip) 68237737Smckusick vrele(ITOV(ip)); 68337737Smckusick else 68437737Smckusick iput(ip); 68537737Smckusick iput(dp); 68637737Smckusick return (error); 6877701Ssam } 6887701Ssam 6899167Ssam /* 69037737Smckusick * link vnode call 6919167Ssam */ 69237737Smckusick ufs_link(vp, ndp) 69337737Smckusick register struct vnode *vp; 69437737Smckusick register struct nameidata *ndp; 6959167Ssam { 69637737Smckusick register struct inode *ip = VTOI(vp); 69737737Smckusick int error; 6989167Ssam 69937737Smckusick if (ndp->ni_dvp != vp) 70037737Smckusick ILOCK(ip); 70137737Smckusick if (ip->i_nlink == LINK_MAX - 1) { 70237737Smckusick error = EMLINK; 70337737Smckusick goto out; 70437737Smckusick } 70537737Smckusick ip->i_nlink++; 70637737Smckusick ip->i_flag |= ICHG; 70737737Smckusick error = iupdat(ip, &time, &time, 1); 70837737Smckusick if (!error) 70937737Smckusick error = direnter(ip, ndp); 71037737Smckusick out: 71137737Smckusick if (ndp->ni_dvp != vp) 71237737Smckusick IUNLOCK(ip); 71337737Smckusick if (error) { 71437737Smckusick ip->i_nlink--; 71530598Smckusick ip->i_flag |= ICHG; 71637737Smckusick } 71737737Smckusick return (error); 7189167Ssam } 7199167Ssam 7209167Ssam /* 7219167Ssam * Rename system call. 7229167Ssam * rename("foo", "bar"); 7239167Ssam * is essentially 7249167Ssam * unlink("bar"); 7259167Ssam * link("foo", "bar"); 7269167Ssam * unlink("foo"); 7279167Ssam * but ``atomically''. Can't do full commit without saving state in the 7289167Ssam * inode on disk which isn't feasible at this time. Best we can do is 7299167Ssam * always guarantee the target exists. 7309167Ssam * 7319167Ssam * Basic algorithm is: 7329167Ssam * 7339167Ssam * 1) Bump link count on source while we're linking it to the 73437737Smckusick * target. This also ensure the inode won't be deleted out 73516776Smckusick * from underneath us while we work (it may be truncated by 73616776Smckusick * a concurrent `trunc' or `open' for creation). 7379167Ssam * 2) Link source to destination. If destination already exists, 7389167Ssam * delete it first. 73916776Smckusick * 3) Unlink source reference to inode if still around. If a 74016776Smckusick * directory was moved and the parent of the destination 7419167Ssam * is different from the source, patch the ".." entry in the 7429167Ssam * directory. 7439167Ssam */ 74437737Smckusick ufs_rename(fndp, tndp) 74537737Smckusick register struct nameidata *fndp, *tndp; 7467701Ssam { 7479167Ssam register struct inode *ip, *xp, *dp; 74816776Smckusick struct dirtemplate dirbuf; 74916776Smckusick int doingdirectory = 0, oldparent = 0, newparent = 0; 75010051Ssam int error = 0; 7517701Ssam 75237737Smckusick dp = VTOI(fndp->ni_dvp); 75337737Smckusick ip = VTOI(fndp->ni_vp); 75437737Smckusick ILOCK(ip); 7559167Ssam if ((ip->i_mode&IFMT) == IFDIR) { 75637737Smckusick register struct direct *d = &fndp->ni_dent; 7579167Ssam 7589167Ssam /* 75911641Ssam * Avoid ".", "..", and aliases of "." for obvious reasons. 7609167Ssam */ 76137737Smckusick if ((d->d_namlen == 1 && d->d_name[0] == '.') || dp == ip || 76237737Smckusick fndp->ni_isdotdot || (ip->i_flag & IRENAME)) { 76337737Smckusick IUNLOCK(ip); 76437737Smckusick ufs_abortop(fndp); 76537737Smckusick ufs_abortop(tndp); 76637737Smckusick return (EINVAL); 7679167Ssam } 76816776Smckusick ip->i_flag |= IRENAME; 7699167Ssam oldparent = dp->i_number; 7709167Ssam doingdirectory++; 7719167Ssam } 77237737Smckusick vrele(fndp->ni_dvp); 7739167Ssam 7749167Ssam /* 7759167Ssam * 1) Bump link count while we're moving stuff 7769167Ssam * around. If we crash somewhere before 7779167Ssam * completing our work, the link count 7789167Ssam * may be wrong, but correctable. 7799167Ssam */ 7809167Ssam ip->i_nlink++; 7819167Ssam ip->i_flag |= ICHG; 78237737Smckusick error = iupdat(ip, &time, &time, 1); 78316664Smckusick IUNLOCK(ip); 7849167Ssam 7859167Ssam /* 7869167Ssam * When the target exists, both the directory 78737737Smckusick * and target vnodes are returned locked. 7889167Ssam */ 78937737Smckusick dp = VTOI(tndp->ni_dvp); 79037737Smckusick xp = NULL; 79137737Smckusick if (tndp->ni_vp) 79237737Smckusick xp = VTOI(tndp->ni_vp); 7939167Ssam /* 79411641Ssam * If ".." must be changed (ie the directory gets a new 79512816Smckusick * parent) then the source directory must not be in the 79612816Smckusick * directory heirarchy above the target, as this would 79712816Smckusick * orphan everything below the source directory. Also 79812816Smckusick * the user must have write permission in the source so 79912816Smckusick * as to be able to change "..". We must repeat the call 80012816Smckusick * to namei, as the parent directory is unlocked by the 80112816Smckusick * call to checkpath(). 80211641Ssam */ 80316776Smckusick if (oldparent != dp->i_number) 80416776Smckusick newparent = dp->i_number; 80516776Smckusick if (doingdirectory && newparent) { 80637737Smckusick if (error = iaccess(ip, IWRITE, tndp->ni_cred)) 80712816Smckusick goto bad; 80837737Smckusick tndp->ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE; 80912816Smckusick do { 81037737Smckusick dp = VTOI(tndp->ni_dvp); 81112816Smckusick if (xp != NULL) 81238069Smckusick iput(xp); 81337737Smckusick if (error = checkpath(ip, dp, tndp->ni_cred)) 81412816Smckusick goto out; 81537737Smckusick if (error = namei(tndp)) 81612816Smckusick goto out; 81737737Smckusick xp = NULL; 81837737Smckusick if (tndp->ni_vp) 81937737Smckusick xp = VTOI(tndp->ni_vp); 82037737Smckusick } while (dp != VTOI(tndp->ni_dvp)); 82112816Smckusick } 82211641Ssam /* 8239167Ssam * 2) If target doesn't exist, link the target 8249167Ssam * to the source and unlink the source. 8259167Ssam * Otherwise, rewrite the target directory 8269167Ssam * entry to reference the source inode and 8279167Ssam * expunge the original entry's existence. 8289167Ssam */ 8299167Ssam if (xp == NULL) { 83037737Smckusick if (dp->i_dev != ip->i_dev) 83137737Smckusick panic("rename: EXDEV"); 8329167Ssam /* 83316776Smckusick * Account for ".." in new directory. 83416776Smckusick * When source and destination have the same 83516776Smckusick * parent we don't fool with the link count. 8369167Ssam */ 83716776Smckusick if (doingdirectory && newparent) { 8389167Ssam dp->i_nlink++; 8399167Ssam dp->i_flag |= ICHG; 84037737Smckusick error = iupdat(dp, &time, &time, 1); 8419167Ssam } 84237737Smckusick if (error = direnter(ip, tndp)) 8439167Ssam goto out; 8449167Ssam } else { 84537737Smckusick if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) 84637737Smckusick panic("rename: EXDEV"); 8479167Ssam /* 84810590Ssam * Short circuit rename(foo, foo). 84910590Ssam */ 85010590Ssam if (xp->i_number == ip->i_number) 85137737Smckusick panic("rename: same file"); 85210590Ssam /* 85324433Sbloom * If the parent directory is "sticky", then the user must 85424433Sbloom * own the parent directory, or the destination of the rename, 85524433Sbloom * otherwise the destination may not be changed (except by 85624433Sbloom * root). This implements append-only directories. 85724433Sbloom */ 85837737Smckusick if ((dp->i_mode & ISVTX) && tndp->ni_cred->cr_uid != 0 && 85937737Smckusick tndp->ni_cred->cr_uid != dp->i_uid && 86037737Smckusick xp->i_uid != tndp->ni_cred->cr_uid) { 86124433Sbloom error = EPERM; 86224433Sbloom goto bad; 86324433Sbloom } 86424433Sbloom /* 86510051Ssam * Target must be empty if a directory 86610051Ssam * and have no links to it. 8679167Ssam * Also, insure source and target are 8689167Ssam * compatible (both directories, or both 8699167Ssam * not directories). 8709167Ssam */ 8719167Ssam if ((xp->i_mode&IFMT) == IFDIR) { 87237737Smckusick if (!dirempty(xp, dp->i_number, tndp->ni_cred) || 87337737Smckusick xp->i_nlink > 2) { 87410051Ssam error = ENOTEMPTY; 8759167Ssam goto bad; 8769167Ssam } 8779167Ssam if (!doingdirectory) { 87810051Ssam error = ENOTDIR; 8799167Ssam goto bad; 8809167Ssam } 88137737Smckusick cache_purge(ITOV(dp)); 8829167Ssam } else if (doingdirectory) { 88310051Ssam error = EISDIR; 8849167Ssam goto bad; 8859167Ssam } 88637737Smckusick if (error = dirrewrite(dp, ip, tndp)) 88737737Smckusick goto bad; 88837737Smckusick vput(ITOV(dp)); 8899167Ssam /* 89010051Ssam * Adjust the link count of the target to 89110051Ssam * reflect the dirrewrite above. If this is 89210051Ssam * a directory it is empty and there are 89310051Ssam * no links to it, so we can squash the inode and 89410051Ssam * any space associated with it. We disallowed 89510051Ssam * renaming over top of a directory with links to 89616776Smckusick * it above, as the remaining link would point to 89716776Smckusick * a directory without "." or ".." entries. 8989167Ssam */ 89910051Ssam xp->i_nlink--; 9009167Ssam if (doingdirectory) { 90110051Ssam if (--xp->i_nlink != 0) 90210051Ssam panic("rename: linked directory"); 903*39674Smckusick error = itrunc(xp, (u_long)0, IO_SYNC); 90410051Ssam } 9059167Ssam xp->i_flag |= ICHG; 90638398Smckusick iput(xp); 90710246Ssam xp = NULL; 9089167Ssam } 9099167Ssam 9109167Ssam /* 9119167Ssam * 3) Unlink the source. 9129167Ssam */ 91337737Smckusick fndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF; 91437737Smckusick (void)namei(fndp); 91537737Smckusick if (fndp->ni_vp != NULL) { 91637737Smckusick xp = VTOI(fndp->ni_vp); 91737737Smckusick dp = VTOI(fndp->ni_dvp); 91837737Smckusick } else { 91938069Smckusick if (fndp->ni_dvp != NULL) 92038069Smckusick vput(fndp->ni_dvp); 92137737Smckusick xp = NULL; 92217758Smckusick dp = NULL; 92337737Smckusick } 9249167Ssam /* 92537737Smckusick * Ensure that the directory entry still exists and has not 92616776Smckusick * changed while the new name has been entered. If the source is 92716776Smckusick * a file then the entry may have been unlinked or renamed. In 92816776Smckusick * either case there is no further work to be done. If the source 92916776Smckusick * is a directory then it cannot have been rmdir'ed; its link 93016776Smckusick * count of three would cause a rmdir to fail with ENOTEMPTY. 93137737Smckusick * The IRENAME flag ensures that it cannot be moved by another 93216776Smckusick * rename. 9339167Ssam */ 93417758Smckusick if (xp != ip) { 93516776Smckusick if (doingdirectory) 93617758Smckusick panic("rename: lost dir entry"); 93716776Smckusick } else { 9389167Ssam /* 93916776Smckusick * If the source is a directory with a 94016776Smckusick * new parent, the link count of the old 94116776Smckusick * parent directory must be decremented 94216776Smckusick * and ".." set to point to the new parent. 9439167Ssam */ 94416776Smckusick if (doingdirectory && newparent) { 9459167Ssam dp->i_nlink--; 9469167Ssam dp->i_flag |= ICHG; 94739597Smckusick error = vn_rdwr(UIO_READ, ITOV(xp), (caddr_t)&dirbuf, 94837737Smckusick sizeof (struct dirtemplate), (off_t)0, 94939597Smckusick UIO_SYSSPACE, IO_NODELOCKED, 95039597Smckusick tndp->ni_cred, (int *)0); 95116776Smckusick if (error == 0) { 95216776Smckusick if (dirbuf.dotdot_namlen != 2 || 95316776Smckusick dirbuf.dotdot_name[0] != '.' || 95416776Smckusick dirbuf.dotdot_name[1] != '.') { 95539610Smckusick dirbad(xp, 12, "rename: mangled dir"); 95616776Smckusick } else { 95716776Smckusick dirbuf.dotdot_ino = newparent; 95839597Smckusick (void) vn_rdwr(UIO_WRITE, ITOV(xp), 95916776Smckusick (caddr_t)&dirbuf, 96016776Smckusick sizeof (struct dirtemplate), 96137740Smckusick (off_t)0, UIO_SYSSPACE, 96239597Smckusick IO_NODELOCKED|IO_SYNC, 96337737Smckusick tndp->ni_cred, (int *)0); 96437737Smckusick cache_purge(ITOV(dp)); 96516776Smckusick } 96616776Smckusick } 9679167Ssam } 96837737Smckusick error = dirremove(fndp); 96937737Smckusick if (!error) { 97016776Smckusick xp->i_nlink--; 97116776Smckusick xp->i_flag |= ICHG; 9729167Ssam } 97316776Smckusick xp->i_flag &= ~IRENAME; 9749167Ssam } 9759167Ssam if (dp) 97637737Smckusick vput(ITOV(dp)); 97716776Smckusick if (xp) 97837737Smckusick vput(ITOV(xp)); 97937737Smckusick vrele(ITOV(ip)); 98037737Smckusick return (error); 9819167Ssam 9829167Ssam bad: 9839167Ssam if (xp) 98437737Smckusick vput(ITOV(xp)); 98537737Smckusick vput(ITOV(dp)); 9869167Ssam out: 9879167Ssam ip->i_nlink--; 9889167Ssam ip->i_flag |= ICHG; 98937737Smckusick vrele(ITOV(ip)); 99037737Smckusick return (error); 9917701Ssam } 9927701Ssam 9937535Sroot /* 99412756Ssam * A virgin directory (no blushing please). 99512756Ssam */ 99612756Ssam struct dirtemplate mastertemplate = { 99712756Ssam 0, 12, 1, ".", 99812756Ssam 0, DIRBLKSIZ - 12, 2, ".." 99912756Ssam }; 100012756Ssam 100112756Ssam /* 100212756Ssam * Mkdir system call 100312756Ssam */ 100437737Smckusick ufs_mkdir(ndp, vap) 100537737Smckusick struct nameidata *ndp; 100637737Smckusick struct vattr *vap; 100712756Ssam { 100812756Ssam register struct inode *ip, *dp; 100937737Smckusick struct inode *tip; 101037737Smckusick struct vnode *dvp; 101112756Ssam struct dirtemplate dirtemplate; 101237737Smckusick int error; 101337737Smckusick int dmode; 101412756Ssam 101537737Smckusick dvp = ndp->ni_dvp; 101637737Smckusick dp = VTOI(dvp); 101737737Smckusick dmode = vap->va_mode&0777; 101837737Smckusick dmode |= IFDIR; 101912756Ssam /* 102012756Ssam * Must simulate part of maknode here 102112756Ssam * in order to acquire the inode, but 102212756Ssam * not have it entered in the parent 102312756Ssam * directory. The entry is made later 102412756Ssam * after writing "." and ".." entries out. 102512756Ssam */ 102637737Smckusick error = ialloc(dp, dirpref(dp->i_fs), dmode, &tip); 102737737Smckusick if (error) { 102812756Ssam iput(dp); 102937737Smckusick return (error); 103012756Ssam } 103137737Smckusick ip = tip; 103212756Ssam #ifdef QUOTA 103312756Ssam if (ip->i_dquot != NODQUOT) 103412756Ssam panic("mkdir: dquot"); 103512756Ssam #endif 103612756Ssam ip->i_flag |= IACC|IUPD|ICHG; 103737737Smckusick ip->i_mode = dmode; 103837737Smckusick ITOV(ip)->v_type = VDIR; /* Rest init'd in iget() */ 103912756Ssam ip->i_nlink = 2; 104037737Smckusick ip->i_uid = ndp->ni_cred->cr_uid; 104112756Ssam ip->i_gid = dp->i_gid; 104212756Ssam #ifdef QUOTA 104312756Ssam ip->i_dquot = inoquota(ip); 104412756Ssam #endif 104537737Smckusick error = iupdat(ip, &time, &time, 1); 104612756Ssam 104712756Ssam /* 104812756Ssam * Bump link count in parent directory 104912756Ssam * to reflect work done below. Should 105012756Ssam * be done before reference is created 105112756Ssam * so reparation is possible if we crash. 105212756Ssam */ 105312756Ssam dp->i_nlink++; 105412756Ssam dp->i_flag |= ICHG; 105537737Smckusick error = iupdat(dp, &time, &time, 1); 105612756Ssam 105712756Ssam /* 105812756Ssam * Initialize directory with "." 105912756Ssam * and ".." from static template. 106012756Ssam */ 106112756Ssam dirtemplate = mastertemplate; 106212756Ssam dirtemplate.dot_ino = ip->i_number; 106312756Ssam dirtemplate.dotdot_ino = dp->i_number; 106439597Smckusick error = vn_rdwr(UIO_WRITE, ITOV(ip), (caddr_t)&dirtemplate, 106537737Smckusick sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE, 106639597Smckusick IO_NODELOCKED|IO_SYNC, ndp->ni_cred, (int *)0); 106737737Smckusick if (error) { 106812756Ssam dp->i_nlink--; 106912756Ssam dp->i_flag |= ICHG; 107012756Ssam goto bad; 107112756Ssam } 107237737Smckusick if (DIRBLKSIZ > dp->i_fs->fs_fsize) 107337737Smckusick panic("mkdir: blksize"); /* XXX - should grow w/balloc() */ 107418103Smckusick else 107518103Smckusick ip->i_size = DIRBLKSIZ; 107612756Ssam /* 107712756Ssam * Directory all set up, now 107812756Ssam * install the entry for it in 107912756Ssam * the parent directory. 108012756Ssam */ 108137737Smckusick error = direnter(ip, ndp); 108212756Ssam dp = NULL; 108337737Smckusick if (error) { 108416694Smckusick ndp->ni_nameiop = LOOKUP | NOCACHE; 108537737Smckusick error = namei(ndp); 108637737Smckusick if (!error) { 108737737Smckusick dp = VTOI(ndp->ni_vp); 108812756Ssam dp->i_nlink--; 108912756Ssam dp->i_flag |= ICHG; 109012756Ssam } 109112756Ssam } 109212756Ssam bad: 109312756Ssam /* 109412756Ssam * No need to do an explicit itrunc here, 109537737Smckusick * vrele will do this for us because we set 109612756Ssam * the link count to 0. 109712756Ssam */ 109837737Smckusick if (error) { 109912756Ssam ip->i_nlink = 0; 110012756Ssam ip->i_flag |= ICHG; 110138144Smckusick iput(ip); 110238144Smckusick } else 110338144Smckusick ndp->ni_vp = ITOV(ip); 110412756Ssam if (dp) 110512756Ssam iput(dp); 110637737Smckusick return (error); 110712756Ssam } 110812756Ssam 110912756Ssam /* 111012756Ssam * Rmdir system call. 111112756Ssam */ 111237737Smckusick ufs_rmdir(ndp) 111337737Smckusick register struct nameidata *ndp; 111412756Ssam { 111512756Ssam register struct inode *ip, *dp; 111637737Smckusick int error = 0; 111712756Ssam 111837737Smckusick ip = VTOI(ndp->ni_vp); 111937737Smckusick dp = VTOI(ndp->ni_dvp); 112012756Ssam /* 112112756Ssam * No rmdir "." please. 112212756Ssam */ 112312756Ssam if (dp == ip) { 112437737Smckusick vrele(ITOV(dp)); 112512756Ssam iput(ip); 112637737Smckusick return (EINVAL); 112712756Ssam } 112812756Ssam /* 112912756Ssam * Verify the directory is empty (and valid). 113012756Ssam * (Rmdir ".." won't be valid since 113112756Ssam * ".." will contain a reference to 113212756Ssam * the current directory and thus be 113312756Ssam * non-empty.) 113412756Ssam */ 113537737Smckusick if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number, ndp->ni_cred)) { 113637737Smckusick error = ENOTEMPTY; 113712756Ssam goto out; 113812756Ssam } 113912756Ssam /* 114012756Ssam * Delete reference to directory before purging 114112756Ssam * inode. If we crash in between, the directory 114212756Ssam * will be reattached to lost+found, 114312756Ssam */ 114437737Smckusick if (error = dirremove(ndp)) 114512756Ssam goto out; 114612756Ssam dp->i_nlink--; 114712756Ssam dp->i_flag |= ICHG; 114837737Smckusick cache_purge(ITOV(dp)); 114912756Ssam iput(dp); 115037737Smckusick ndp->ni_dvp = NULL; 115112756Ssam /* 115212756Ssam * Truncate inode. The only stuff left 115312756Ssam * in the directory is "." and "..". The 115412756Ssam * "." reference is inconsequential since 115512756Ssam * we're quashing it. The ".." reference 115612756Ssam * has already been adjusted above. We've 115712756Ssam * removed the "." reference and the reference 115812756Ssam * in the parent directory, but there may be 115912756Ssam * other hard links so decrement by 2 and 116012756Ssam * worry about them later. 116112756Ssam */ 116212756Ssam ip->i_nlink -= 2; 1163*39674Smckusick error = itrunc(ip, (u_long)0, IO_SYNC); 116437737Smckusick cache_purge(ITOV(ip)); 116512756Ssam out: 116637737Smckusick if (ndp->ni_dvp) 116712756Ssam iput(dp); 116812756Ssam iput(ip); 116937737Smckusick return (error); 117012756Ssam } 117112756Ssam 117237737Smckusick /* 117337737Smckusick * symlink -- make a symbolic link 117437737Smckusick */ 117537737Smckusick ufs_symlink(ndp, vap, target) 117637737Smckusick struct nameidata *ndp; 117737737Smckusick struct vattr *vap; 117837737Smckusick char *target; 117912756Ssam { 118037737Smckusick struct inode *ip; 118137737Smckusick int error; 118212756Ssam 118337737Smckusick error = maknode(IFLNK | vap->va_mode, ndp, &ip); 118437737Smckusick if (error) 118537737Smckusick return (error); 118639597Smckusick error = vn_rdwr(UIO_WRITE, ITOV(ip), target, strlen(target), (off_t)0, 118739597Smckusick UIO_SYSSPACE, IO_NODELOCKED, ndp->ni_cred, (int *)0); 118837737Smckusick iput(ip); 118937737Smckusick return (error); 119037737Smckusick } 119137737Smckusick 119237737Smckusick /* 119337737Smckusick * Vnode op for read and write 119437737Smckusick */ 119539597Smckusick ufs_readdir(vp, uio, cred) 119637737Smckusick struct vnode *vp; 119737737Smckusick register struct uio *uio; 119837737Smckusick struct ucred *cred; 119937737Smckusick { 120039597Smckusick int count, lost, error; 120137737Smckusick 120237737Smckusick count = uio->uio_resid; 120337737Smckusick count &= ~(DIRBLKSIZ - 1); 120439597Smckusick lost = uio->uio_resid - count; 120539597Smckusick if (count < DIRBLKSIZ || (uio->uio_offset & (DIRBLKSIZ -1))) 120637737Smckusick return (EINVAL); 120737737Smckusick uio->uio_resid = count; 120837737Smckusick uio->uio_iov->iov_len = count; 120939597Smckusick error = ufs_read(vp, uio, 0, cred); 121039597Smckusick uio->uio_resid += lost; 121137737Smckusick return (error); 121237737Smckusick } 121337737Smckusick 121437737Smckusick /* 121537737Smckusick * Return target name of a symbolic link 121637737Smckusick */ 121737737Smckusick ufs_readlink(vp, uiop, cred) 121837737Smckusick struct vnode *vp; 121937737Smckusick struct uio *uiop; 122037737Smckusick struct ucred *cred; 122137737Smckusick { 122237737Smckusick 122339597Smckusick return (ufs_read(vp, uiop, 0, cred)); 122437737Smckusick } 122537737Smckusick 122637737Smckusick /* 122737737Smckusick * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually 122837737Smckusick * done. Iff ni_vp/ni_dvp not null and locked, unlock. 122937737Smckusick */ 123037737Smckusick ufs_abortop(ndp) 123137737Smckusick register struct nameidata *ndp; 123237737Smckusick { 123337737Smckusick register struct inode *ip; 123437737Smckusick 123537737Smckusick if (ndp->ni_vp) { 123637737Smckusick ip = VTOI(ndp->ni_vp); 123737737Smckusick if (ip->i_flag & ILOCKED) 123837737Smckusick IUNLOCK(ip); 123937737Smckusick vrele(ndp->ni_vp); 124012756Ssam } 124137737Smckusick if (ndp->ni_dvp) { 124237737Smckusick ip = VTOI(ndp->ni_dvp); 124337737Smckusick if (ip->i_flag & ILOCKED) 124437737Smckusick IUNLOCK(ip); 124537737Smckusick vrele(ndp->ni_dvp); 124637737Smckusick } 124737737Smckusick return; 124812756Ssam } 124912756Ssam 125037737Smckusick ufs_lock(vp) 125137737Smckusick struct vnode *vp; 125237737Smckusick { 125337737Smckusick register struct inode *ip = VTOI(vp); 125437737Smckusick 125537737Smckusick ILOCK(ip); 125637737Smckusick return (0); 125737737Smckusick } 125837737Smckusick 125937737Smckusick ufs_unlock(vp) 126037737Smckusick struct vnode *vp; 126137737Smckusick { 126237737Smckusick register struct inode *ip = VTOI(vp); 126337737Smckusick 126437737Smckusick if (!(ip->i_flag & ILOCKED)) 126537737Smckusick panic("ufs_unlock NOT LOCKED"); 126637737Smckusick IUNLOCK(ip); 126737737Smckusick return (0); 126837737Smckusick } 126937737Smckusick 127012756Ssam /* 127137737Smckusick * Get access to bmap 127212756Ssam */ 127337737Smckusick ufs_bmap(vp, bn, vpp, bnp) 127437737Smckusick struct vnode *vp; 127537737Smckusick daddr_t bn; 127637737Smckusick struct vnode **vpp; 127737737Smckusick daddr_t *bnp; 127812756Ssam { 127937737Smckusick struct inode *ip = VTOI(vp); 128012756Ssam 128137737Smckusick if (vpp != NULL) 128237737Smckusick *vpp = ip->i_devvp; 128337737Smckusick if (bnp == NULL) 128437737Smckusick return (0); 128537737Smckusick return (bmap(ip, bn, bnp, (daddr_t *)0, (int *)0)); 128612756Ssam } 128737737Smckusick 128837737Smckusick /* 128937737Smckusick * Just call the device strategy routine 129037737Smckusick */ 1291*39674Smckusick int checkoverlap = 1; 1292*39674Smckusick 129337737Smckusick ufs_strategy(bp) 129437737Smckusick register struct buf *bp; 129537737Smckusick { 1296*39674Smckusick register struct inode *ip = VTOI(bp->b_vp); 1297*39674Smckusick register struct buf *ep; 1298*39674Smckusick struct vnode *vp; 1299*39674Smckusick struct buf *ebp; 1300*39674Smckusick daddr_t start, last; 1301*39674Smckusick int error; 1302*39674Smckusick 1303*39674Smckusick if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR) 1304*39674Smckusick panic("ufs_strategy: spec"); 1305*39674Smckusick if (bp->b_blkno == bp->b_lblkno) { 1306*39674Smckusick if (error = bmap(ip, bp->b_lblkno, &bp->b_blkno)) 1307*39674Smckusick return (error); 1308*39674Smckusick if ((long)bp->b_blkno == -1) { 1309*39674Smckusick clrbuf(bp); 1310*39674Smckusick biodone(bp); 1311*39674Smckusick } 1312*39674Smckusick } 1313*39674Smckusick if ((long)bp->b_blkno == -1) 1314*39674Smckusick return (0); 1315*39674Smckusick if (checkoverlap) { 1316*39674Smckusick ebp = &buf[nbuf]; 1317*39674Smckusick start = bp->b_blkno; 1318*39674Smckusick last = start + btodb(bp->b_bcount) - 1; 1319*39674Smckusick for (ep = buf; ep < ebp; ep++) { 1320*39674Smckusick if (ep == bp || (ep->b_flags & B_INVAL) || 1321*39674Smckusick ep->b_vp == (struct vnode *)0) 1322*39674Smckusick continue; 1323*39674Smckusick if (VOP_BMAP(ep->b_vp, (daddr_t)0, &vp, (daddr_t)0)) 1324*39674Smckusick continue; 1325*39674Smckusick if (vp != ip->i_devvp) 1326*39674Smckusick continue; 1327*39674Smckusick /* look for overlap */ 1328*39674Smckusick if (ep->b_bcount == 0 || ep->b_blkno > last || 1329*39674Smckusick ep->b_blkno + btodb(ep->b_bcount) <= start) 1330*39674Smckusick continue; 1331*39674Smckusick panic("Disk overlap"); 1332*39674Smckusick } 1333*39674Smckusick } 1334*39674Smckusick vp = ip->i_devvp; 1335*39674Smckusick bp->b_dev = vp->v_rdev; 1336*39674Smckusick (*(vp->v_op->vn_strategy))(bp); 133737737Smckusick return (0); 133837737Smckusick } 133937737Smckusick 134037737Smckusick /* 1341*39674Smckusick * Print out the contents of an inode. 1342*39674Smckusick */ 1343*39674Smckusick ufs_print(vp) 1344*39674Smckusick struct vnode *vp; 1345*39674Smckusick { 1346*39674Smckusick register struct inode *ip = VTOI(vp); 1347*39674Smckusick 1348*39674Smckusick printf("tag VT_UFS, ino %d, on dev %d, %d%s\n", ip->i_number, 1349*39674Smckusick major(ip->i_dev), minor(ip->i_dev), 1350*39674Smckusick (ip->i_flag & ILOCKED) ? " (LOCKED)" : ""); 1351*39674Smckusick } 1352*39674Smckusick 1353*39674Smckusick /* 135439628Smckusick * Read wrapper for special devices. 135539628Smckusick */ 135639628Smckusick ufsspec_read(vp, uio, ioflag, cred) 135739628Smckusick struct vnode *vp; 135839628Smckusick struct uio *uio; 135939628Smckusick int ioflag; 136039628Smckusick struct ucred *cred; 136139628Smckusick { 136239628Smckusick 136339628Smckusick /* 136439628Smckusick * Set access flag. 136539628Smckusick */ 136639628Smckusick VTOI(vp)->i_flag |= IACC; 136739628Smckusick return (spec_read(vp, uio, ioflag, cred)); 136839628Smckusick } 136939628Smckusick 137039628Smckusick /* 137139628Smckusick * Write wrapper for special devices. 137239628Smckusick */ 137339628Smckusick ufsspec_write(vp, uio, ioflag, cred) 137439628Smckusick struct vnode *vp; 137539628Smckusick struct uio *uio; 137639628Smckusick int ioflag; 137739628Smckusick struct ucred *cred; 137839628Smckusick { 137939628Smckusick 138039628Smckusick /* 138139628Smckusick * Set update and change flags. 138239628Smckusick */ 138339628Smckusick VTOI(vp)->i_flag |= IUPD|ICHG; 138439628Smckusick return (spec_write(vp, uio, ioflag, cred)); 138539628Smckusick } 138639628Smckusick 138739628Smckusick /* 138839628Smckusick * Close wrapper for special devices. 138939628Smckusick * 139039628Smckusick * Update the times on the inode then do device close. 139139628Smckusick */ 139239628Smckusick ufsspec_close(vp, fflag, cred) 139339628Smckusick struct vnode *vp; 139439628Smckusick int fflag; 139539628Smckusick struct ucred *cred; 139639628Smckusick { 139739628Smckusick register struct inode *ip = VTOI(vp); 139839628Smckusick 139939628Smckusick if (vp->v_count > 1 && !(ip->i_flag & ILOCKED)) 140039628Smckusick ITIMES(ip, &time, &time); 140139628Smckusick return (spec_close(vp, fflag, cred)); 140239628Smckusick } 140339628Smckusick 140439628Smckusick /* 140537737Smckusick * Make a new file. 140637737Smckusick */ 140737737Smckusick maknode(mode, ndp, ipp) 140837737Smckusick int mode; 140937737Smckusick register struct nameidata *ndp; 141037737Smckusick struct inode **ipp; 141137737Smckusick { 141237737Smckusick register struct inode *ip; 141337737Smckusick struct inode *tip; 141437737Smckusick register struct inode *pdir = VTOI(ndp->ni_dvp); 141537737Smckusick ino_t ipref; 141637737Smckusick int error; 141737737Smckusick 141837737Smckusick *ipp = 0; 141937737Smckusick if ((mode & IFMT) == IFDIR) 142037737Smckusick ipref = dirpref(pdir->i_fs); 142137737Smckusick else 142237737Smckusick ipref = pdir->i_number; 142337737Smckusick error = ialloc(pdir, ipref, mode, &tip); 142437737Smckusick if (error) { 142537737Smckusick iput(pdir); 142637737Smckusick return (error); 142737737Smckusick } 142837737Smckusick ip = tip; 142937737Smckusick #ifdef QUOTA 143037737Smckusick if (ip->i_dquot != NODQUOT) 143137737Smckusick panic("maknode: dquot"); 143237737Smckusick #endif 143337737Smckusick ip->i_flag |= IACC|IUPD|ICHG; 143437737Smckusick if ((mode & IFMT) == 0) 143537737Smckusick mode |= IFREG; 143637737Smckusick ip->i_mode = mode; 143737737Smckusick ITOV(ip)->v_type = IFTOVT(mode); /* Rest init'd in iget() */ 143837737Smckusick ip->i_nlink = 1; 143937737Smckusick ip->i_uid = ndp->ni_cred->cr_uid; 144037737Smckusick ip->i_gid = pdir->i_gid; 144137737Smckusick if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, ndp->ni_cred) && 144237737Smckusick suser(ndp->ni_cred, NULL)) 144337737Smckusick ip->i_mode &= ~ISGID; 144437737Smckusick #ifdef QUOTA 144537737Smckusick ip->i_dquot = inoquota(ip); 144637737Smckusick #endif 144737737Smckusick 144837737Smckusick /* 144937737Smckusick * Make sure inode goes to disk before directory entry. 145037737Smckusick */ 145137737Smckusick if ((error = iupdat(ip, &time, &time, 1)) || 145237737Smckusick (error = direnter(ip, ndp))) { 145337737Smckusick /* 145437737Smckusick * Write error occurred trying to update the inode 145537737Smckusick * or the directory so must deallocate the inode. 145637737Smckusick */ 145737737Smckusick ip->i_nlink = 0; 145837737Smckusick ip->i_flag |= ICHG; 145937737Smckusick iput(ip); 146037737Smckusick return (error); 146137737Smckusick } 146237737Smckusick *ipp = ip; 146337737Smckusick return (0); 146437737Smckusick } 1465