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*39610Smckusick * @(#)lfs_vnops.c 7.23 (Berkeley) 11/24/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(), 7137737Smckusick ufs_strategy(); 726254Sroot 7337737Smckusick struct vnodeops ufs_vnodeops = { 7437737Smckusick ufs_lookup, 7537737Smckusick ufs_create, 7637737Smckusick ufs_mknod, 7737737Smckusick ufs_open, 7837737Smckusick ufs_close, 7937737Smckusick ufs_access, 8037737Smckusick ufs_getattr, 8137737Smckusick ufs_setattr, 8237737Smckusick ufs_read, 8337737Smckusick ufs_write, 8437737Smckusick ufs_ioctl, 8537737Smckusick ufs_select, 8637737Smckusick ufs_mmap, 8737737Smckusick ufs_fsync, 8837737Smckusick ufs_seek, 8937737Smckusick ufs_remove, 9037737Smckusick ufs_link, 9137737Smckusick ufs_rename, 9237737Smckusick ufs_mkdir, 9337737Smckusick ufs_rmdir, 9437737Smckusick ufs_symlink, 9537737Smckusick ufs_readdir, 9637737Smckusick ufs_readlink, 9737737Smckusick ufs_abortop, 9837737Smckusick ufs_inactive, 9939391Smckusick ufs_reclaim, 10037737Smckusick ufs_lock, 10137737Smckusick ufs_unlock, 10237737Smckusick ufs_bmap, 10337737Smckusick ufs_strategy, 10437737Smckusick }; 1056254Sroot 10639435Smckusick int spec_lookup(), 10739435Smckusick spec_open(), 10839435Smckusick spec_read(), 10939435Smckusick spec_write(), 11039435Smckusick spec_strategy(), 11139435Smckusick spec_ioctl(), 11239435Smckusick spec_select(), 11339435Smckusick spec_close(), 11439435Smckusick spec_badop(), 11539435Smckusick spec_nullop(); 11639435Smckusick 11739435Smckusick struct vnodeops spec_inodeops = { 11839597Smckusick spec_lookup, /* lookup */ 11939597Smckusick spec_badop, /* create */ 12039597Smckusick spec_badop, /* mknod */ 12139597Smckusick spec_open, /* open */ 12239597Smckusick spec_close, /* close */ 12339597Smckusick ufs_access, /* access */ 12439597Smckusick ufs_getattr, /* getattr */ 12539597Smckusick ufs_setattr, /* setattr */ 12639597Smckusick spec_read, /* read */ 12739597Smckusick spec_write, /* write */ 12839597Smckusick spec_ioctl, /* ioctl */ 12939597Smckusick spec_select, /* select */ 13039597Smckusick spec_badop, /* mmap */ 13139597Smckusick spec_nullop, /* fsync */ 13239597Smckusick spec_badop, /* seek */ 13339597Smckusick spec_badop, /* remove */ 13439597Smckusick spec_badop, /* link */ 13539597Smckusick spec_badop, /* rename */ 13639597Smckusick spec_badop, /* mkdir */ 13739597Smckusick spec_badop, /* rmdir */ 13839597Smckusick spec_badop, /* symlink */ 13939597Smckusick spec_badop, /* readdir */ 14039597Smckusick spec_badop, /* readlink */ 14139597Smckusick spec_badop, /* abortop */ 14239597Smckusick ufs_inactive, /* inactive */ 14339597Smckusick ufs_reclaim, /* reclaim */ 14439597Smckusick ufs_lock, /* lock */ 14539597Smckusick ufs_unlock, /* unlock */ 14639597Smckusick spec_badop, /* bmap */ 14739597Smckusick spec_strategy, /* strategy */ 14839435Smckusick }; 14939435Smckusick 15037737Smckusick enum vtype iftovt_tab[8] = { 15137737Smckusick VNON, VCHR, VDIR, VBLK, VREG, VLNK, VSOCK, VBAD, 15237737Smckusick }; 15337737Smckusick int vttoif_tab[8] = { 15437737Smckusick 0, IFREG, IFDIR, IFBLK, IFCHR, IFLNK, IFSOCK, IFMT, 15537737Smckusick }; 1566254Sroot 1579167Ssam /* 15837737Smckusick * Create a regular file 1599167Ssam */ 16037737Smckusick ufs_create(ndp, vap) 16137737Smckusick struct nameidata *ndp; 16237737Smckusick struct vattr *vap; 1636254Sroot { 16437737Smckusick struct inode *ip; 16537737Smckusick int error; 1666254Sroot 16737737Smckusick if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip)) 16837737Smckusick return (error); 16937737Smckusick ndp->ni_vp = ITOV(ip); 17037737Smckusick return (0); 1716254Sroot } 1726254Sroot 17337Sbill /* 17437737Smckusick * Mknod vnode call 1756254Sroot */ 17637737Smckusick /* ARGSUSED */ 17737737Smckusick ufs_mknod(ndp, vap, cred) 17837737Smckusick struct nameidata *ndp; 17937737Smckusick struct ucred *cred; 18037737Smckusick struct vattr *vap; 1816254Sroot { 18239435Smckusick register struct vnode *vp; 18337737Smckusick struct inode *ip; 18437737Smckusick int error; 1856254Sroot 18637737Smckusick if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip)) 18737737Smckusick return (error); 18839435Smckusick vp = ITOV(ip); 18937737Smckusick if (vap->va_rdev) { 19037737Smckusick /* 19137737Smckusick * Want to be able to use this to make badblock 19237737Smckusick * inodes, so don't truncate the dev number. 19337737Smckusick */ 19439608Smckusick ip->i_rdev = vap->va_rdev; 19537737Smckusick ip->i_flag |= IACC|IUPD|ICHG; 19612756Ssam } 19737737Smckusick /* 19837737Smckusick * Remove inode so that it will be reloaded by iget and 19937737Smckusick * checked to see if it is an alias of an existing entry 20037737Smckusick * in the inode cache. 20137737Smckusick */ 20238809Smckusick iput(ip); 20339435Smckusick vp->v_type = VNON; 20439435Smckusick vgone(vp); 20537737Smckusick return (0); 2066254Sroot } 2076254Sroot 2086254Sroot /* 20937737Smckusick * Open called. 21037737Smckusick * 21137737Smckusick * Nothing to do. 2126254Sroot */ 21337737Smckusick /* ARGSUSED */ 21437737Smckusick ufs_open(vp, mode, cred) 21537737Smckusick struct vnode *vp; 21637737Smckusick int mode; 21737737Smckusick struct ucred *cred; 2186254Sroot { 2196254Sroot 22037737Smckusick return (0); 2216254Sroot } 2226254Sroot 2236254Sroot /* 22437737Smckusick * Close called 22537737Smckusick * 22637737Smckusick * Update the times on the inode. 2276254Sroot */ 22837737Smckusick /* ARGSUSED */ 22937737Smckusick ufs_close(vp, fflag, cred) 23037737Smckusick struct vnode *vp; 23137737Smckusick int fflag; 23237737Smckusick struct ucred *cred; 2336254Sroot { 23437737Smckusick register struct inode *ip = VTOI(vp); 2356254Sroot 23637737Smckusick if (vp->v_count > 1 && !(ip->i_flag & ILOCKED)) 23737737Smckusick ITIMES(ip, &time, &time); 23837737Smckusick return (0); 2396254Sroot } 2406254Sroot 24137737Smckusick ufs_access(vp, mode, cred) 24237737Smckusick struct vnode *vp; 24337737Smckusick int mode; 24437737Smckusick struct ucred *cred; 2456254Sroot { 2466254Sroot 24737737Smckusick return (iaccess(VTOI(vp), mode, cred)); 2486254Sroot } 2496254Sroot 25037737Smckusick /* ARGSUSED */ 25137737Smckusick ufs_getattr(vp, vap, cred) 25237737Smckusick struct vnode *vp; 25337737Smckusick register struct vattr *vap; 25437737Smckusick struct ucred *cred; 2556254Sroot { 25637737Smckusick register struct inode *ip = VTOI(vp); 2576254Sroot 25837737Smckusick ITIMES(ip, &time, &time); 2596254Sroot /* 26037737Smckusick * Copy from inode table 2616254Sroot */ 26237737Smckusick vap->va_fsid = ip->i_dev; 26337737Smckusick vap->va_fileid = ip->i_number; 26437737Smckusick vap->va_mode = ip->i_mode & ~IFMT; 26537737Smckusick vap->va_nlink = ip->i_nlink; 26637737Smckusick vap->va_uid = ip->i_uid; 26737737Smckusick vap->va_gid = ip->i_gid; 26837737Smckusick vap->va_rdev = (dev_t)ip->i_rdev; 26939391Smckusick vap->va_size = ip->i_din.di_qsize.val[0]; 27039391Smckusick vap->va_size1 = ip->i_din.di_qsize.val[1]; 27137737Smckusick vap->va_atime.tv_sec = ip->i_atime; 27238578Smckusick vap->va_atime.tv_usec = 0; 27337737Smckusick vap->va_mtime.tv_sec = ip->i_mtime; 27438578Smckusick vap->va_mtime.tv_usec = 0; 27537737Smckusick vap->va_ctime.tv_sec = ip->i_ctime; 27638578Smckusick vap->va_ctime.tv_usec = 0; 27738254Smckusick vap->va_flags = ip->i_flags; 27838254Smckusick vap->va_gen = ip->i_gen; 27937737Smckusick /* this doesn't belong here */ 28037737Smckusick if (vp->v_type == VBLK) 28137737Smckusick vap->va_blocksize = BLKDEV_IOSIZE; 28237737Smckusick else if (vp->v_type == VCHR) 28337737Smckusick vap->va_blocksize = MAXBSIZE; 2847142Smckusick else 28537737Smckusick vap->va_blocksize = ip->i_fs->fs_bsize; 28638657Smckusick vap->va_bytes = dbtob(ip->i_blocks); 28737737Smckusick vap->va_bytes1 = -1; 28837737Smckusick vap->va_type = vp->v_type; 28937737Smckusick return (0); 2906254Sroot } 2916254Sroot 2926254Sroot /* 29337737Smckusick * Set attribute vnode op. called from several syscalls 2946254Sroot */ 29537737Smckusick ufs_setattr(vp, vap, cred) 29637737Smckusick register struct vnode *vp; 29737737Smckusick register struct vattr *vap; 29837737Smckusick register struct ucred *cred; 2996254Sroot { 30037737Smckusick register struct inode *ip = VTOI(vp); 30137737Smckusick int error = 0; 3026254Sroot 30337737Smckusick /* 30437737Smckusick * Check for unsetable attributes. 30537737Smckusick */ 30637737Smckusick if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || 30737737Smckusick (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || 30837737Smckusick (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || 30938254Smckusick ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { 31037737Smckusick return (EINVAL); 31116540Ssam } 31237737Smckusick /* 31337737Smckusick * Go through the fields and update iff not VNOVAL. 31437737Smckusick */ 31537737Smckusick if (vap->va_uid != (u_short)VNOVAL || vap->va_gid != (u_short)VNOVAL) 31637737Smckusick if (error = chown1(vp, vap->va_uid, vap->va_gid, cred)) 31737737Smckusick return (error); 31837737Smckusick if (vap->va_size != VNOVAL) { 31937737Smckusick if (vp->v_type == VDIR) 32037737Smckusick return (EISDIR); 32137737Smckusick if (error = itrunc(ip, vap->va_size)) 32237737Smckusick return (error); 32313878Ssam } 32437737Smckusick if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { 32537773Smckusick if (cred->cr_uid != ip->i_uid && 32637773Smckusick (error = suser(cred, &u.u_acflag))) 32737773Smckusick return (error); 32837737Smckusick if (vap->va_atime.tv_sec != VNOVAL) 32937737Smckusick ip->i_flag |= IACC; 33037737Smckusick if (vap->va_mtime.tv_sec != VNOVAL) 33137737Smckusick ip->i_flag |= IUPD; 33237737Smckusick ip->i_flag |= ICHG; 33337737Smckusick if (error = iupdat(ip, &vap->va_atime, &vap->va_mtime, 1)) 33437737Smckusick return (error); 3356254Sroot } 33637737Smckusick if (vap->va_mode != (u_short)VNOVAL) 33737737Smckusick error = chmod1(vp, (int)vap->va_mode, cred); 33838254Smckusick if (vap->va_flags != VNOVAL) { 33938254Smckusick if (cred->cr_uid != ip->i_uid && 34038254Smckusick (error = suser(cred, &u.u_acflag))) 34138254Smckusick return (error); 34238254Smckusick if (cred->cr_uid == 0) { 34338254Smckusick ip->i_flags = vap->va_flags; 34438254Smckusick } else { 34538254Smckusick ip->i_flags &= 0xffff0000; 34638254Smckusick ip->i_flags |= (vap->va_flags & 0xffff); 34738254Smckusick } 34838254Smckusick ip->i_flag |= ICHG; 34938254Smckusick } 35037737Smckusick return (error); 3516254Sroot } 3526254Sroot 3536254Sroot /* 3549167Ssam * Change the mode on a file. 3559167Ssam * Inode must be locked before calling. 3569167Ssam */ 35737737Smckusick chmod1(vp, mode, cred) 35837737Smckusick register struct vnode *vp; 3597701Ssam register int mode; 36037737Smckusick struct ucred *cred; 3617701Ssam { 36237737Smckusick register struct inode *ip = VTOI(vp); 36337773Smckusick int error; 3647868Sroot 36537773Smckusick if (cred->cr_uid != ip->i_uid && 36637773Smckusick (error = suser(cred, &u.u_acflag))) 36737773Smckusick return (error); 3686254Sroot ip->i_mode &= ~07777; 36937737Smckusick if (cred->cr_uid) { 37037737Smckusick if (vp->v_type != VDIR) 37121015Smckusick mode &= ~ISVTX; 37237737Smckusick if (!groupmember(ip->i_gid, cred)) 37311811Ssam mode &= ~ISGID; 3747439Sroot } 37537737Smckusick ip->i_mode |= mode & 07777; 3766254Sroot ip->i_flag |= ICHG; 37737737Smckusick if ((vp->v_flag & VTEXT) && (ip->i_mode & ISVTX) == 0) 37837737Smckusick xrele(vp); 37921015Smckusick return (0); 3805992Swnj } 3815992Swnj 3829167Ssam /* 3837701Ssam * Perform chown operation on inode ip; 3847701Ssam * inode must be locked prior to call. 3857701Ssam */ 38637737Smckusick chown1(vp, uid, gid, cred) 38737737Smckusick register struct vnode *vp; 38837737Smckusick uid_t uid; 38937737Smckusick gid_t gid; 39037737Smckusick struct ucred *cred; 3917701Ssam { 39237737Smckusick register struct inode *ip = VTOI(vp); 3937701Ssam #ifdef QUOTA 3947701Ssam register long change; 39511811Ssam #endif 39637737Smckusick int error; 3977701Ssam 39837737Smckusick if (uid == (u_short)VNOVAL) 39911811Ssam uid = ip->i_uid; 40037737Smckusick if (gid == (u_short)VNOVAL) 40111811Ssam gid = ip->i_gid; 40236614Sbostic /* 40336614Sbostic * If we don't own the file, are trying to change the owner 40436614Sbostic * of the file, or are not a member of the target group, 40536614Sbostic * the caller must be superuser or the call fails. 40636614Sbostic */ 40737737Smckusick if ((cred->cr_uid != ip->i_uid || uid != ip->i_uid || 40837737Smckusick !groupmember((gid_t)gid, cred)) && 40937737Smckusick (error = suser(cred, &u.u_acflag))) 41037737Smckusick return (error); 41111811Ssam #ifdef QUOTA 41214385Ssam if (ip->i_uid == uid) /* this just speeds things a little */ 4137482Skre change = 0; 41412646Ssam else 41512646Ssam change = ip->i_blocks; 41612646Ssam (void) chkdq(ip, -change, 1); 41712646Ssam (void) chkiq(ip->i_dev, ip, ip->i_uid, 1); 4187482Skre dqrele(ip->i_dquot); 4197482Skre #endif 42039306Smckusick if (ip->i_uid != uid && cred->cr_uid != 0) 42139306Smckusick ip->i_mode &= ~ISUID; 42239306Smckusick if (ip->i_gid != gid && cred->cr_uid != 0) 42339306Smckusick ip->i_mode &= ~ISGID; 42411811Ssam ip->i_uid = uid; 42511811Ssam ip->i_gid = gid; 4266254Sroot ip->i_flag |= ICHG; 4277701Ssam #ifdef QUOTA 4287482Skre ip->i_dquot = inoquota(ip); 42912646Ssam (void) chkdq(ip, change, 1); 43026361Skarels (void) chkiq(ip->i_dev, (struct inode *)NULL, (uid_t)uid, 1); 43112646Ssam return (u.u_error); /* should == 0 ALWAYS !! */ 43212646Ssam #else 43312646Ssam return (0); 4347482Skre #endif 43537Sbill } 43637Sbill 43739608Smckusick /* 43839608Smckusick * Vnode op for reading. 43939608Smckusick */ 44037737Smckusick /* ARGSUSED */ 44139608Smckusick ufs_read(vp, uio, ioflag, cred) 44239608Smckusick struct vnode *vp; 44339608Smckusick register struct uio *uio; 44439608Smckusick int ioflag; 44539608Smckusick struct ucred *cred; 44639608Smckusick { 44739608Smckusick register struct inode *ip = VTOI(vp); 44839608Smckusick register struct fs *fs; 44939608Smckusick struct buf *bp; 45039608Smckusick daddr_t lbn, bn, rablock; 45139608Smckusick int size, rasize, diff, error = 0; 45239608Smckusick long n, on, type; 45339608Smckusick 45439608Smckusick if (uio->uio_rw != UIO_READ) 45539608Smckusick panic("ufs_read mode"); 45639608Smckusick type = ip->i_mode & IFMT; 45739608Smckusick if (type != IFDIR && type != IFREG && type != IFLNK) 45839608Smckusick panic("ufs_read type"); 45939608Smckusick if (uio->uio_resid == 0) 46039608Smckusick return (0); 46139608Smckusick if (uio->uio_offset < 0) 46239608Smckusick return (EINVAL); 46339608Smckusick ip->i_flag |= IACC; 46439608Smckusick fs = ip->i_fs; 46539608Smckusick do { 46639608Smckusick lbn = lblkno(fs, uio->uio_offset); 46739608Smckusick on = blkoff(fs, uio->uio_offset); 46839608Smckusick n = MIN((unsigned)(fs->fs_bsize - on), uio->uio_resid); 46939608Smckusick diff = ip->i_size - uio->uio_offset; 47039608Smckusick if (diff <= 0) 47139608Smckusick return (0); 47239608Smckusick if (diff < n) 47339608Smckusick n = diff; 47439608Smckusick if (error = bmap(ip, lbn, &bn, &rablock, &rasize)) 47539608Smckusick return (error); 47639608Smckusick size = blksize(fs, ip, lbn); 47739608Smckusick if ((long)bn < 0) { 47839608Smckusick bp = geteblk(size); 47939608Smckusick clrbuf(bp); 48039608Smckusick } else if (ip->i_lastr + 1 == lbn) 48139608Smckusick error = breada(ip->i_devvp, bn, size, rablock, rasize, 48239608Smckusick NOCRED, &bp); 48339608Smckusick else 48439608Smckusick error = bread(ip->i_devvp, bn, size, NOCRED, &bp); 48539608Smckusick ip->i_lastr = lbn; 48639608Smckusick n = MIN(n, size - bp->b_resid); 48739608Smckusick if (error) { 48839608Smckusick brelse(bp); 48939608Smckusick return (error); 49039608Smckusick } 49139608Smckusick error = uiomove(bp->b_un.b_addr + on, (int)n, uio); 49239608Smckusick if (n + on == fs->fs_bsize || uio->uio_offset == ip->i_size) 49339608Smckusick bp->b_flags |= B_AGE; 49439608Smckusick brelse(bp); 49539608Smckusick } while (error == 0 && uio->uio_resid > 0 && n != 0); 49639608Smckusick return (error); 49739608Smckusick } 49839608Smckusick 49939608Smckusick /* 50039608Smckusick * Vnode op for writing. 50139608Smckusick */ 50239608Smckusick ufs_write(vp, uio, ioflag, cred) 50339608Smckusick register struct vnode *vp; 50439608Smckusick struct uio *uio; 50539608Smckusick int ioflag; 50639608Smckusick struct ucred *cred; 50739608Smckusick { 50839608Smckusick register struct inode *ip = VTOI(vp); 50939608Smckusick register struct fs *fs; 51039608Smckusick struct buf *bp; 51139608Smckusick daddr_t lbn, bn; 51239608Smckusick u_long osize; 51339608Smckusick int i, n, on, flags; 51439608Smckusick int count, size, resid, error = 0; 51539608Smckusick 51639608Smckusick if (uio->uio_rw != UIO_WRITE) 51739608Smckusick panic("ufs_write mode"); 51839608Smckusick switch (vp->v_type) { 51939608Smckusick case VREG: 52039608Smckusick if (ioflag & IO_APPEND) 52139608Smckusick uio->uio_offset = ip->i_size; 52239608Smckusick /* fall through */ 52339608Smckusick case VLNK: 52439608Smckusick break; 52539608Smckusick 52639608Smckusick case VDIR: 52739608Smckusick if ((ioflag & IO_SYNC) == 0) 52839608Smckusick panic("ufs_write nonsync dir write"); 52939608Smckusick break; 53039608Smckusick 53139608Smckusick default: 53239608Smckusick panic("ufs_write type"); 53339608Smckusick } 53439608Smckusick if (uio->uio_offset < 0) 53539608Smckusick return (EINVAL); 53639608Smckusick if (uio->uio_resid == 0) 53739608Smckusick return (0); 53839608Smckusick /* 53939608Smckusick * Maybe this should be above the vnode op call, but so long as 54039608Smckusick * file servers have no limits, i don't think it matters 54139608Smckusick */ 54239608Smckusick if (vp->v_type == VREG && 54339608Smckusick uio->uio_offset + uio->uio_resid > 54439608Smckusick u.u_rlimit[RLIMIT_FSIZE].rlim_cur) { 54539608Smckusick psignal(u.u_procp, SIGXFSZ); 54639608Smckusick return (EFBIG); 54739608Smckusick } 54839608Smckusick resid = uio->uio_resid; 54939608Smckusick osize = ip->i_size; 55039608Smckusick fs = ip->i_fs; 55139608Smckusick do { 55239608Smckusick lbn = lblkno(fs, uio->uio_offset); 55339608Smckusick on = blkoff(fs, uio->uio_offset); 55439608Smckusick n = MIN((unsigned)(fs->fs_bsize - on), uio->uio_resid); 55539608Smckusick if (n < fs->fs_bsize) 55639608Smckusick flags = B_CLRBUF; 55739608Smckusick else 55839608Smckusick flags = 0; 55939608Smckusick if (error = balloc(ip, lbn, (int)(on + n), &bn, flags)) 56039608Smckusick break; 56139608Smckusick if (uio->uio_offset + n > ip->i_size) 56239608Smckusick ip->i_size = uio->uio_offset + n; 56339608Smckusick size = blksize(fs, ip, lbn); 56439608Smckusick count = howmany(size, CLBYTES); 56539608Smckusick for (i = 0; i < count; i++) 56639608Smckusick munhash(ip->i_devvp, bn + i * CLBYTES / DEV_BSIZE); 56739608Smckusick if (n == fs->fs_bsize) 56839608Smckusick bp = getblk(ip->i_devvp, bn, size); 56939608Smckusick else 57039608Smckusick error = bread(ip->i_devvp, bn, size, NOCRED, &bp); 57139608Smckusick n = MIN(n, size - bp->b_resid); 57239608Smckusick if (error) { 57339608Smckusick brelse(bp); 57439608Smckusick break; 57539608Smckusick } 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)) { 58939608Smckusick (void) itrunc(ip, osize); 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; 64739597Smckusick return (syncip(ip, waitfor)); 6487701Ssam } 6497701Ssam 6509167Ssam /* 65137737Smckusick * Seek on a file 65237737Smckusick * 65337737Smckusick * Nothing to do, so just return. 6549167Ssam */ 65537737Smckusick /* ARGSUSED */ 65637737Smckusick ufs_seek(vp, oldoff, newoff, cred) 65737737Smckusick struct vnode *vp; 65837737Smckusick off_t oldoff, newoff; 65937737Smckusick struct ucred *cred; 6607701Ssam { 6617701Ssam 66237737Smckusick return (0); 66337737Smckusick } 66437737Smckusick 66537737Smckusick /* 66637737Smckusick * ufs remove 66737737Smckusick * Hard to avoid races here, especially 66837737Smckusick * in unlinking directories. 66937737Smckusick */ 67037737Smckusick ufs_remove(ndp) 67137737Smckusick struct nameidata *ndp; 67237737Smckusick { 67337737Smckusick register struct inode *ip, *dp; 67437737Smckusick int error; 67537737Smckusick 67637737Smckusick ip = VTOI(ndp->ni_vp); 67737737Smckusick dp = VTOI(ndp->ni_dvp); 67837737Smckusick error = dirremove(ndp); 67937737Smckusick if (!error) { 68037737Smckusick ip->i_nlink--; 68137737Smckusick ip->i_flag |= ICHG; 6827701Ssam } 68337737Smckusick if (dp == ip) 68437737Smckusick vrele(ITOV(ip)); 68537737Smckusick else 68637737Smckusick iput(ip); 68737737Smckusick iput(dp); 68837737Smckusick return (error); 6897701Ssam } 6907701Ssam 6919167Ssam /* 69237737Smckusick * link vnode call 6939167Ssam */ 69437737Smckusick ufs_link(vp, ndp) 69537737Smckusick register struct vnode *vp; 69637737Smckusick register struct nameidata *ndp; 6979167Ssam { 69837737Smckusick register struct inode *ip = VTOI(vp); 69937737Smckusick int error; 7009167Ssam 70137737Smckusick if (ndp->ni_dvp != vp) 70237737Smckusick ILOCK(ip); 70337737Smckusick if (ip->i_nlink == LINK_MAX - 1) { 70437737Smckusick error = EMLINK; 70537737Smckusick goto out; 70637737Smckusick } 70737737Smckusick ip->i_nlink++; 70837737Smckusick ip->i_flag |= ICHG; 70937737Smckusick error = iupdat(ip, &time, &time, 1); 71037737Smckusick if (!error) 71137737Smckusick error = direnter(ip, ndp); 71237737Smckusick out: 71337737Smckusick if (ndp->ni_dvp != vp) 71437737Smckusick IUNLOCK(ip); 71537737Smckusick if (error) { 71637737Smckusick ip->i_nlink--; 71730598Smckusick ip->i_flag |= ICHG; 71837737Smckusick } 71937737Smckusick return (error); 7209167Ssam } 7219167Ssam 7229167Ssam /* 7239167Ssam * Rename system call. 7249167Ssam * rename("foo", "bar"); 7259167Ssam * is essentially 7269167Ssam * unlink("bar"); 7279167Ssam * link("foo", "bar"); 7289167Ssam * unlink("foo"); 7299167Ssam * but ``atomically''. Can't do full commit without saving state in the 7309167Ssam * inode on disk which isn't feasible at this time. Best we can do is 7319167Ssam * always guarantee the target exists. 7329167Ssam * 7339167Ssam * Basic algorithm is: 7349167Ssam * 7359167Ssam * 1) Bump link count on source while we're linking it to the 73637737Smckusick * target. This also ensure the inode won't be deleted out 73716776Smckusick * from underneath us while we work (it may be truncated by 73816776Smckusick * a concurrent `trunc' or `open' for creation). 7399167Ssam * 2) Link source to destination. If destination already exists, 7409167Ssam * delete it first. 74116776Smckusick * 3) Unlink source reference to inode if still around. If a 74216776Smckusick * directory was moved and the parent of the destination 7439167Ssam * is different from the source, patch the ".." entry in the 7449167Ssam * directory. 7459167Ssam */ 74637737Smckusick ufs_rename(fndp, tndp) 74737737Smckusick register struct nameidata *fndp, *tndp; 7487701Ssam { 7499167Ssam register struct inode *ip, *xp, *dp; 75016776Smckusick struct dirtemplate dirbuf; 75116776Smckusick int doingdirectory = 0, oldparent = 0, newparent = 0; 75210051Ssam int error = 0; 7537701Ssam 75437737Smckusick dp = VTOI(fndp->ni_dvp); 75537737Smckusick ip = VTOI(fndp->ni_vp); 75637737Smckusick ILOCK(ip); 7579167Ssam if ((ip->i_mode&IFMT) == IFDIR) { 75837737Smckusick register struct direct *d = &fndp->ni_dent; 7599167Ssam 7609167Ssam /* 76111641Ssam * Avoid ".", "..", and aliases of "." for obvious reasons. 7629167Ssam */ 76337737Smckusick if ((d->d_namlen == 1 && d->d_name[0] == '.') || dp == ip || 76437737Smckusick fndp->ni_isdotdot || (ip->i_flag & IRENAME)) { 76537737Smckusick IUNLOCK(ip); 76637737Smckusick ufs_abortop(fndp); 76737737Smckusick ufs_abortop(tndp); 76837737Smckusick return (EINVAL); 7699167Ssam } 77016776Smckusick ip->i_flag |= IRENAME; 7719167Ssam oldparent = dp->i_number; 7729167Ssam doingdirectory++; 7739167Ssam } 77437737Smckusick vrele(fndp->ni_dvp); 7759167Ssam 7769167Ssam /* 7779167Ssam * 1) Bump link count while we're moving stuff 7789167Ssam * around. If we crash somewhere before 7799167Ssam * completing our work, the link count 7809167Ssam * may be wrong, but correctable. 7819167Ssam */ 7829167Ssam ip->i_nlink++; 7839167Ssam ip->i_flag |= ICHG; 78437737Smckusick error = iupdat(ip, &time, &time, 1); 78516664Smckusick IUNLOCK(ip); 7869167Ssam 7879167Ssam /* 7889167Ssam * When the target exists, both the directory 78937737Smckusick * and target vnodes are returned locked. 7909167Ssam */ 79137737Smckusick dp = VTOI(tndp->ni_dvp); 79237737Smckusick xp = NULL; 79337737Smckusick if (tndp->ni_vp) 79437737Smckusick xp = VTOI(tndp->ni_vp); 7959167Ssam /* 79611641Ssam * If ".." must be changed (ie the directory gets a new 79712816Smckusick * parent) then the source directory must not be in the 79812816Smckusick * directory heirarchy above the target, as this would 79912816Smckusick * orphan everything below the source directory. Also 80012816Smckusick * the user must have write permission in the source so 80112816Smckusick * as to be able to change "..". We must repeat the call 80212816Smckusick * to namei, as the parent directory is unlocked by the 80312816Smckusick * call to checkpath(). 80411641Ssam */ 80516776Smckusick if (oldparent != dp->i_number) 80616776Smckusick newparent = dp->i_number; 80716776Smckusick if (doingdirectory && newparent) { 80837737Smckusick if (error = iaccess(ip, IWRITE, tndp->ni_cred)) 80912816Smckusick goto bad; 81037737Smckusick tndp->ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE; 81112816Smckusick do { 81237737Smckusick dp = VTOI(tndp->ni_dvp); 81312816Smckusick if (xp != NULL) 81438069Smckusick iput(xp); 81537737Smckusick if (error = checkpath(ip, dp, tndp->ni_cred)) 81612816Smckusick goto out; 81737737Smckusick if (error = namei(tndp)) 81812816Smckusick goto out; 81937737Smckusick xp = NULL; 82037737Smckusick if (tndp->ni_vp) 82137737Smckusick xp = VTOI(tndp->ni_vp); 82237737Smckusick } while (dp != VTOI(tndp->ni_dvp)); 82312816Smckusick } 82411641Ssam /* 8259167Ssam * 2) If target doesn't exist, link the target 8269167Ssam * to the source and unlink the source. 8279167Ssam * Otherwise, rewrite the target directory 8289167Ssam * entry to reference the source inode and 8299167Ssam * expunge the original entry's existence. 8309167Ssam */ 8319167Ssam if (xp == NULL) { 83237737Smckusick if (dp->i_dev != ip->i_dev) 83337737Smckusick panic("rename: EXDEV"); 8349167Ssam /* 83516776Smckusick * Account for ".." in new directory. 83616776Smckusick * When source and destination have the same 83716776Smckusick * parent we don't fool with the link count. 8389167Ssam */ 83916776Smckusick if (doingdirectory && newparent) { 8409167Ssam dp->i_nlink++; 8419167Ssam dp->i_flag |= ICHG; 84237737Smckusick error = iupdat(dp, &time, &time, 1); 8439167Ssam } 84437737Smckusick if (error = direnter(ip, tndp)) 8459167Ssam goto out; 8469167Ssam } else { 84737737Smckusick if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) 84837737Smckusick panic("rename: EXDEV"); 8499167Ssam /* 85010590Ssam * Short circuit rename(foo, foo). 85110590Ssam */ 85210590Ssam if (xp->i_number == ip->i_number) 85337737Smckusick panic("rename: same file"); 85410590Ssam /* 85524433Sbloom * If the parent directory is "sticky", then the user must 85624433Sbloom * own the parent directory, or the destination of the rename, 85724433Sbloom * otherwise the destination may not be changed (except by 85824433Sbloom * root). This implements append-only directories. 85924433Sbloom */ 86037737Smckusick if ((dp->i_mode & ISVTX) && tndp->ni_cred->cr_uid != 0 && 86137737Smckusick tndp->ni_cred->cr_uid != dp->i_uid && 86237737Smckusick xp->i_uid != tndp->ni_cred->cr_uid) { 86324433Sbloom error = EPERM; 86424433Sbloom goto bad; 86524433Sbloom } 86624433Sbloom /* 86710051Ssam * Target must be empty if a directory 86810051Ssam * and have no links to it. 8699167Ssam * Also, insure source and target are 8709167Ssam * compatible (both directories, or both 8719167Ssam * not directories). 8729167Ssam */ 8739167Ssam if ((xp->i_mode&IFMT) == IFDIR) { 87437737Smckusick if (!dirempty(xp, dp->i_number, tndp->ni_cred) || 87537737Smckusick xp->i_nlink > 2) { 87610051Ssam error = ENOTEMPTY; 8779167Ssam goto bad; 8789167Ssam } 8799167Ssam if (!doingdirectory) { 88010051Ssam error = ENOTDIR; 8819167Ssam goto bad; 8829167Ssam } 88337737Smckusick cache_purge(ITOV(dp)); 8849167Ssam } else if (doingdirectory) { 88510051Ssam error = EISDIR; 8869167Ssam goto bad; 8879167Ssam } 88837737Smckusick if (error = dirrewrite(dp, ip, tndp)) 88937737Smckusick goto bad; 89037737Smckusick vput(ITOV(dp)); 8919167Ssam /* 89210051Ssam * Adjust the link count of the target to 89310051Ssam * reflect the dirrewrite above. If this is 89410051Ssam * a directory it is empty and there are 89510051Ssam * no links to it, so we can squash the inode and 89610051Ssam * any space associated with it. We disallowed 89710051Ssam * renaming over top of a directory with links to 89816776Smckusick * it above, as the remaining link would point to 89916776Smckusick * a directory without "." or ".." entries. 9009167Ssam */ 90110051Ssam xp->i_nlink--; 9029167Ssam if (doingdirectory) { 90310051Ssam if (--xp->i_nlink != 0) 90410051Ssam panic("rename: linked directory"); 90537737Smckusick error = itrunc(xp, (u_long)0); 90610051Ssam } 9079167Ssam xp->i_flag |= ICHG; 90838398Smckusick iput(xp); 90910246Ssam xp = NULL; 9109167Ssam } 9119167Ssam 9129167Ssam /* 9139167Ssam * 3) Unlink the source. 9149167Ssam */ 91537737Smckusick fndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF; 91637737Smckusick (void)namei(fndp); 91737737Smckusick if (fndp->ni_vp != NULL) { 91837737Smckusick xp = VTOI(fndp->ni_vp); 91937737Smckusick dp = VTOI(fndp->ni_dvp); 92037737Smckusick } else { 92138069Smckusick if (fndp->ni_dvp != NULL) 92238069Smckusick vput(fndp->ni_dvp); 92337737Smckusick xp = NULL; 92417758Smckusick dp = NULL; 92537737Smckusick } 9269167Ssam /* 92737737Smckusick * Ensure that the directory entry still exists and has not 92816776Smckusick * changed while the new name has been entered. If the source is 92916776Smckusick * a file then the entry may have been unlinked or renamed. In 93016776Smckusick * either case there is no further work to be done. If the source 93116776Smckusick * is a directory then it cannot have been rmdir'ed; its link 93216776Smckusick * count of three would cause a rmdir to fail with ENOTEMPTY. 93337737Smckusick * The IRENAME flag ensures that it cannot be moved by another 93416776Smckusick * rename. 9359167Ssam */ 93617758Smckusick if (xp != ip) { 93716776Smckusick if (doingdirectory) 93817758Smckusick panic("rename: lost dir entry"); 93916776Smckusick } else { 9409167Ssam /* 94116776Smckusick * If the source is a directory with a 94216776Smckusick * new parent, the link count of the old 94316776Smckusick * parent directory must be decremented 94416776Smckusick * and ".." set to point to the new parent. 9459167Ssam */ 94616776Smckusick if (doingdirectory && newparent) { 9479167Ssam dp->i_nlink--; 9489167Ssam dp->i_flag |= ICHG; 94939597Smckusick error = vn_rdwr(UIO_READ, ITOV(xp), (caddr_t)&dirbuf, 95037737Smckusick sizeof (struct dirtemplate), (off_t)0, 95139597Smckusick UIO_SYSSPACE, IO_NODELOCKED, 95239597Smckusick tndp->ni_cred, (int *)0); 95316776Smckusick if (error == 0) { 95416776Smckusick if (dirbuf.dotdot_namlen != 2 || 95516776Smckusick dirbuf.dotdot_name[0] != '.' || 95616776Smckusick dirbuf.dotdot_name[1] != '.') { 957*39610Smckusick dirbad(xp, 12, "rename: mangled dir"); 95816776Smckusick } else { 95916776Smckusick dirbuf.dotdot_ino = newparent; 96039597Smckusick (void) vn_rdwr(UIO_WRITE, ITOV(xp), 96116776Smckusick (caddr_t)&dirbuf, 96216776Smckusick sizeof (struct dirtemplate), 96337740Smckusick (off_t)0, UIO_SYSSPACE, 96439597Smckusick IO_NODELOCKED|IO_SYNC, 96537737Smckusick tndp->ni_cred, (int *)0); 96637737Smckusick cache_purge(ITOV(dp)); 96716776Smckusick } 96816776Smckusick } 9699167Ssam } 97037737Smckusick error = dirremove(fndp); 97137737Smckusick if (!error) { 97216776Smckusick xp->i_nlink--; 97316776Smckusick xp->i_flag |= ICHG; 9749167Ssam } 97516776Smckusick xp->i_flag &= ~IRENAME; 9769167Ssam } 9779167Ssam if (dp) 97837737Smckusick vput(ITOV(dp)); 97916776Smckusick if (xp) 98037737Smckusick vput(ITOV(xp)); 98137737Smckusick vrele(ITOV(ip)); 98237737Smckusick return (error); 9839167Ssam 9849167Ssam bad: 9859167Ssam if (xp) 98637737Smckusick vput(ITOV(xp)); 98737737Smckusick vput(ITOV(dp)); 9889167Ssam out: 9899167Ssam ip->i_nlink--; 9909167Ssam ip->i_flag |= ICHG; 99137737Smckusick vrele(ITOV(ip)); 99237737Smckusick return (error); 9937701Ssam } 9947701Ssam 9957535Sroot /* 99612756Ssam * A virgin directory (no blushing please). 99712756Ssam */ 99812756Ssam struct dirtemplate mastertemplate = { 99912756Ssam 0, 12, 1, ".", 100012756Ssam 0, DIRBLKSIZ - 12, 2, ".." 100112756Ssam }; 100212756Ssam 100312756Ssam /* 100412756Ssam * Mkdir system call 100512756Ssam */ 100637737Smckusick ufs_mkdir(ndp, vap) 100737737Smckusick struct nameidata *ndp; 100837737Smckusick struct vattr *vap; 100912756Ssam { 101012756Ssam register struct inode *ip, *dp; 101137737Smckusick struct inode *tip; 101237737Smckusick struct vnode *dvp; 101312756Ssam struct dirtemplate dirtemplate; 101437737Smckusick int error; 101537737Smckusick int dmode; 101612756Ssam 101737737Smckusick dvp = ndp->ni_dvp; 101837737Smckusick dp = VTOI(dvp); 101937737Smckusick dmode = vap->va_mode&0777; 102037737Smckusick dmode |= IFDIR; 102112756Ssam /* 102212756Ssam * Must simulate part of maknode here 102312756Ssam * in order to acquire the inode, but 102412756Ssam * not have it entered in the parent 102512756Ssam * directory. The entry is made later 102612756Ssam * after writing "." and ".." entries out. 102712756Ssam */ 102837737Smckusick error = ialloc(dp, dirpref(dp->i_fs), dmode, &tip); 102937737Smckusick if (error) { 103012756Ssam iput(dp); 103137737Smckusick return (error); 103212756Ssam } 103337737Smckusick ip = tip; 103412756Ssam #ifdef QUOTA 103512756Ssam if (ip->i_dquot != NODQUOT) 103612756Ssam panic("mkdir: dquot"); 103712756Ssam #endif 103812756Ssam ip->i_flag |= IACC|IUPD|ICHG; 103937737Smckusick ip->i_mode = dmode; 104037737Smckusick ITOV(ip)->v_type = VDIR; /* Rest init'd in iget() */ 104112756Ssam ip->i_nlink = 2; 104237737Smckusick ip->i_uid = ndp->ni_cred->cr_uid; 104312756Ssam ip->i_gid = dp->i_gid; 104412756Ssam #ifdef QUOTA 104512756Ssam ip->i_dquot = inoquota(ip); 104612756Ssam #endif 104737737Smckusick error = iupdat(ip, &time, &time, 1); 104812756Ssam 104912756Ssam /* 105012756Ssam * Bump link count in parent directory 105112756Ssam * to reflect work done below. Should 105212756Ssam * be done before reference is created 105312756Ssam * so reparation is possible if we crash. 105412756Ssam */ 105512756Ssam dp->i_nlink++; 105612756Ssam dp->i_flag |= ICHG; 105737737Smckusick error = iupdat(dp, &time, &time, 1); 105812756Ssam 105912756Ssam /* 106012756Ssam * Initialize directory with "." 106112756Ssam * and ".." from static template. 106212756Ssam */ 106312756Ssam dirtemplate = mastertemplate; 106412756Ssam dirtemplate.dot_ino = ip->i_number; 106512756Ssam dirtemplate.dotdot_ino = dp->i_number; 106639597Smckusick error = vn_rdwr(UIO_WRITE, ITOV(ip), (caddr_t)&dirtemplate, 106737737Smckusick sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE, 106839597Smckusick IO_NODELOCKED|IO_SYNC, ndp->ni_cred, (int *)0); 106937737Smckusick if (error) { 107012756Ssam dp->i_nlink--; 107112756Ssam dp->i_flag |= ICHG; 107212756Ssam goto bad; 107312756Ssam } 107437737Smckusick if (DIRBLKSIZ > dp->i_fs->fs_fsize) 107537737Smckusick panic("mkdir: blksize"); /* XXX - should grow w/balloc() */ 107618103Smckusick else 107718103Smckusick ip->i_size = DIRBLKSIZ; 107812756Ssam /* 107912756Ssam * Directory all set up, now 108012756Ssam * install the entry for it in 108112756Ssam * the parent directory. 108212756Ssam */ 108337737Smckusick error = direnter(ip, ndp); 108412756Ssam dp = NULL; 108537737Smckusick if (error) { 108616694Smckusick ndp->ni_nameiop = LOOKUP | NOCACHE; 108737737Smckusick error = namei(ndp); 108837737Smckusick if (!error) { 108937737Smckusick dp = VTOI(ndp->ni_vp); 109012756Ssam dp->i_nlink--; 109112756Ssam dp->i_flag |= ICHG; 109212756Ssam } 109312756Ssam } 109412756Ssam bad: 109512756Ssam /* 109612756Ssam * No need to do an explicit itrunc here, 109737737Smckusick * vrele will do this for us because we set 109812756Ssam * the link count to 0. 109912756Ssam */ 110037737Smckusick if (error) { 110112756Ssam ip->i_nlink = 0; 110212756Ssam ip->i_flag |= ICHG; 110338144Smckusick iput(ip); 110438144Smckusick } else 110538144Smckusick ndp->ni_vp = ITOV(ip); 110612756Ssam if (dp) 110712756Ssam iput(dp); 110837737Smckusick return (error); 110912756Ssam } 111012756Ssam 111112756Ssam /* 111212756Ssam * Rmdir system call. 111312756Ssam */ 111437737Smckusick ufs_rmdir(ndp) 111537737Smckusick register struct nameidata *ndp; 111612756Ssam { 111712756Ssam register struct inode *ip, *dp; 111837737Smckusick int error = 0; 111912756Ssam 112037737Smckusick ip = VTOI(ndp->ni_vp); 112137737Smckusick dp = VTOI(ndp->ni_dvp); 112212756Ssam /* 112312756Ssam * No rmdir "." please. 112412756Ssam */ 112512756Ssam if (dp == ip) { 112637737Smckusick vrele(ITOV(dp)); 112712756Ssam iput(ip); 112837737Smckusick return (EINVAL); 112912756Ssam } 113012756Ssam /* 113112756Ssam * Verify the directory is empty (and valid). 113212756Ssam * (Rmdir ".." won't be valid since 113312756Ssam * ".." will contain a reference to 113412756Ssam * the current directory and thus be 113512756Ssam * non-empty.) 113612756Ssam */ 113737737Smckusick if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number, ndp->ni_cred)) { 113837737Smckusick error = ENOTEMPTY; 113912756Ssam goto out; 114012756Ssam } 114112756Ssam /* 114212756Ssam * Delete reference to directory before purging 114312756Ssam * inode. If we crash in between, the directory 114412756Ssam * will be reattached to lost+found, 114512756Ssam */ 114637737Smckusick if (error = dirremove(ndp)) 114712756Ssam goto out; 114812756Ssam dp->i_nlink--; 114912756Ssam dp->i_flag |= ICHG; 115037737Smckusick cache_purge(ITOV(dp)); 115112756Ssam iput(dp); 115237737Smckusick ndp->ni_dvp = NULL; 115312756Ssam /* 115412756Ssam * Truncate inode. The only stuff left 115512756Ssam * in the directory is "." and "..". The 115612756Ssam * "." reference is inconsequential since 115712756Ssam * we're quashing it. The ".." reference 115812756Ssam * has already been adjusted above. We've 115912756Ssam * removed the "." reference and the reference 116012756Ssam * in the parent directory, but there may be 116112756Ssam * other hard links so decrement by 2 and 116212756Ssam * worry about them later. 116312756Ssam */ 116412756Ssam ip->i_nlink -= 2; 116537737Smckusick error = itrunc(ip, (u_long)0); 116637737Smckusick cache_purge(ITOV(ip)); 116712756Ssam out: 116837737Smckusick if (ndp->ni_dvp) 116912756Ssam iput(dp); 117012756Ssam iput(ip); 117137737Smckusick return (error); 117212756Ssam } 117312756Ssam 117437737Smckusick /* 117537737Smckusick * symlink -- make a symbolic link 117637737Smckusick */ 117737737Smckusick ufs_symlink(ndp, vap, target) 117837737Smckusick struct nameidata *ndp; 117937737Smckusick struct vattr *vap; 118037737Smckusick char *target; 118112756Ssam { 118237737Smckusick struct inode *ip; 118337737Smckusick int error; 118412756Ssam 118537737Smckusick error = maknode(IFLNK | vap->va_mode, ndp, &ip); 118637737Smckusick if (error) 118737737Smckusick return (error); 118839597Smckusick error = vn_rdwr(UIO_WRITE, ITOV(ip), target, strlen(target), (off_t)0, 118939597Smckusick UIO_SYSSPACE, IO_NODELOCKED, ndp->ni_cred, (int *)0); 119037737Smckusick iput(ip); 119137737Smckusick return (error); 119237737Smckusick } 119337737Smckusick 119437737Smckusick /* 119537737Smckusick * Vnode op for read and write 119637737Smckusick */ 119739597Smckusick ufs_readdir(vp, uio, cred) 119837737Smckusick struct vnode *vp; 119937737Smckusick register struct uio *uio; 120037737Smckusick struct ucred *cred; 120137737Smckusick { 120239597Smckusick int count, lost, error; 120337737Smckusick 120437737Smckusick count = uio->uio_resid; 120537737Smckusick count &= ~(DIRBLKSIZ - 1); 120639597Smckusick lost = uio->uio_resid - count; 120739597Smckusick if (count < DIRBLKSIZ || (uio->uio_offset & (DIRBLKSIZ -1))) 120837737Smckusick return (EINVAL); 120937737Smckusick uio->uio_resid = count; 121037737Smckusick uio->uio_iov->iov_len = count; 121139597Smckusick error = ufs_read(vp, uio, 0, cred); 121239597Smckusick uio->uio_resid += lost; 121337737Smckusick return (error); 121437737Smckusick } 121537737Smckusick 121637737Smckusick /* 121737737Smckusick * Return target name of a symbolic link 121837737Smckusick */ 121937737Smckusick ufs_readlink(vp, uiop, cred) 122037737Smckusick struct vnode *vp; 122137737Smckusick struct uio *uiop; 122237737Smckusick struct ucred *cred; 122337737Smckusick { 122437737Smckusick 122539597Smckusick return (ufs_read(vp, uiop, 0, cred)); 122637737Smckusick } 122737737Smckusick 122837737Smckusick /* 122937737Smckusick * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually 123037737Smckusick * done. Iff ni_vp/ni_dvp not null and locked, unlock. 123137737Smckusick */ 123237737Smckusick ufs_abortop(ndp) 123337737Smckusick register struct nameidata *ndp; 123437737Smckusick { 123537737Smckusick register struct inode *ip; 123637737Smckusick 123737737Smckusick if (ndp->ni_vp) { 123837737Smckusick ip = VTOI(ndp->ni_vp); 123937737Smckusick if (ip->i_flag & ILOCKED) 124037737Smckusick IUNLOCK(ip); 124137737Smckusick vrele(ndp->ni_vp); 124212756Ssam } 124337737Smckusick if (ndp->ni_dvp) { 124437737Smckusick ip = VTOI(ndp->ni_dvp); 124537737Smckusick if (ip->i_flag & ILOCKED) 124637737Smckusick IUNLOCK(ip); 124737737Smckusick vrele(ndp->ni_dvp); 124837737Smckusick } 124937737Smckusick return; 125012756Ssam } 125112756Ssam 125237737Smckusick ufs_lock(vp) 125337737Smckusick struct vnode *vp; 125437737Smckusick { 125537737Smckusick register struct inode *ip = VTOI(vp); 125637737Smckusick 125737737Smckusick ILOCK(ip); 125837737Smckusick return (0); 125937737Smckusick } 126037737Smckusick 126137737Smckusick ufs_unlock(vp) 126237737Smckusick struct vnode *vp; 126337737Smckusick { 126437737Smckusick register struct inode *ip = VTOI(vp); 126537737Smckusick 126637737Smckusick if (!(ip->i_flag & ILOCKED)) 126737737Smckusick panic("ufs_unlock NOT LOCKED"); 126837737Smckusick IUNLOCK(ip); 126937737Smckusick return (0); 127037737Smckusick } 127137737Smckusick 127212756Ssam /* 127337737Smckusick * Get access to bmap 127412756Ssam */ 127537737Smckusick ufs_bmap(vp, bn, vpp, bnp) 127637737Smckusick struct vnode *vp; 127737737Smckusick daddr_t bn; 127837737Smckusick struct vnode **vpp; 127937737Smckusick daddr_t *bnp; 128012756Ssam { 128137737Smckusick struct inode *ip = VTOI(vp); 128212756Ssam 128337737Smckusick if (vpp != NULL) 128437737Smckusick *vpp = ip->i_devvp; 128537737Smckusick if (bnp == NULL) 128637737Smckusick return (0); 128737737Smckusick return (bmap(ip, bn, bnp, (daddr_t *)0, (int *)0)); 128812756Ssam } 128937737Smckusick 129037737Smckusick /* 129137737Smckusick * Just call the device strategy routine 129237737Smckusick */ 129337737Smckusick ufs_strategy(bp) 129437737Smckusick register struct buf *bp; 129537737Smckusick { 129637737Smckusick (*bdevsw[major(bp->b_dev)].d_strategy)(bp); 129737737Smckusick return (0); 129837737Smckusick } 129937737Smckusick 130037737Smckusick /* 130137737Smckusick * Make a new file. 130237737Smckusick */ 130337737Smckusick maknode(mode, ndp, ipp) 130437737Smckusick int mode; 130537737Smckusick register struct nameidata *ndp; 130637737Smckusick struct inode **ipp; 130737737Smckusick { 130837737Smckusick register struct inode *ip; 130937737Smckusick struct inode *tip; 131037737Smckusick register struct inode *pdir = VTOI(ndp->ni_dvp); 131137737Smckusick ino_t ipref; 131237737Smckusick int error; 131337737Smckusick 131437737Smckusick *ipp = 0; 131537737Smckusick if ((mode & IFMT) == IFDIR) 131637737Smckusick ipref = dirpref(pdir->i_fs); 131737737Smckusick else 131837737Smckusick ipref = pdir->i_number; 131937737Smckusick error = ialloc(pdir, ipref, mode, &tip); 132037737Smckusick if (error) { 132137737Smckusick iput(pdir); 132237737Smckusick return (error); 132337737Smckusick } 132437737Smckusick ip = tip; 132537737Smckusick #ifdef QUOTA 132637737Smckusick if (ip->i_dquot != NODQUOT) 132737737Smckusick panic("maknode: dquot"); 132837737Smckusick #endif 132937737Smckusick ip->i_flag |= IACC|IUPD|ICHG; 133037737Smckusick if ((mode & IFMT) == 0) 133137737Smckusick mode |= IFREG; 133237737Smckusick ip->i_mode = mode; 133337737Smckusick ITOV(ip)->v_type = IFTOVT(mode); /* Rest init'd in iget() */ 133437737Smckusick ip->i_nlink = 1; 133537737Smckusick ip->i_uid = ndp->ni_cred->cr_uid; 133637737Smckusick ip->i_gid = pdir->i_gid; 133737737Smckusick if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, ndp->ni_cred) && 133837737Smckusick suser(ndp->ni_cred, NULL)) 133937737Smckusick ip->i_mode &= ~ISGID; 134037737Smckusick #ifdef QUOTA 134137737Smckusick ip->i_dquot = inoquota(ip); 134237737Smckusick #endif 134337737Smckusick 134437737Smckusick /* 134537737Smckusick * Make sure inode goes to disk before directory entry. 134637737Smckusick */ 134737737Smckusick if ((error = iupdat(ip, &time, &time, 1)) || 134837737Smckusick (error = direnter(ip, ndp))) { 134937737Smckusick /* 135037737Smckusick * Write error occurred trying to update the inode 135137737Smckusick * or the directory so must deallocate the inode. 135237737Smckusick */ 135337737Smckusick ip->i_nlink = 0; 135437737Smckusick ip->i_flag |= ICHG; 135537737Smckusick iput(ip); 135637737Smckusick return (error); 135737737Smckusick } 135837737Smckusick *ipp = ip; 135937737Smckusick return (0); 136037737Smckusick } 1361