xref: /csrg-svn/sys/ufs/lfs/lfs_vnops.c (revision 23405)
1*23405Smckusick /*
2*23405Smckusick  * Copyright (c) 1982 Regents of the University of California.
3*23405Smckusick  * All rights reserved.  The Berkeley software License Agreement
4*23405Smckusick  * specifies the terms and conditions for redistribution.
5*23405Smckusick  *
6*23405Smckusick  *	@(#)lfs_vnops.c	6.19 (Berkeley) 06/08/85
7*23405Smckusick  */
837Sbill 
917101Sbloom #include "param.h"
1017101Sbloom #include "systm.h"
1117101Sbloom #include "dir.h"
1217101Sbloom #include "user.h"
1317101Sbloom #include "kernel.h"
1417101Sbloom #include "file.h"
1517101Sbloom #include "stat.h"
1617101Sbloom #include "inode.h"
1717101Sbloom #include "fs.h"
1817101Sbloom #include "buf.h"
1917101Sbloom #include "proc.h"
2017101Sbloom #include "quota.h"
2117101Sbloom #include "uio.h"
2217101Sbloom #include "socket.h"
2317101Sbloom #include "socketvar.h"
2417101Sbloom #include "mount.h"
2537Sbill 
2612756Ssam extern	struct fileops inodeops;
2712756Ssam struct	file *getinode();
2812756Ssam 
299167Ssam /*
309167Ssam  * Change current working directory (``.'').
319167Ssam  */
326254Sroot chdir()
336254Sroot {
346254Sroot 
356254Sroot 	chdirec(&u.u_cdir);
366254Sroot }
376254Sroot 
389167Ssam /*
399167Ssam  * Change notion of root (``/'') directory.
409167Ssam  */
416254Sroot chroot()
426254Sroot {
436254Sroot 
446254Sroot 	if (suser())
456254Sroot 		chdirec(&u.u_rdir);
466254Sroot }
476254Sroot 
489167Ssam /*
499167Ssam  * Common routine for chroot and chdir.
509167Ssam  */
516254Sroot chdirec(ipp)
527701Ssam 	register struct inode **ipp;
536254Sroot {
546254Sroot 	register struct inode *ip;
556254Sroot 	struct a {
566254Sroot 		char	*fname;
5716694Smckusick 	} *uap = (struct a *)u.u_ap;
5816694Smckusick 	register struct nameidata *ndp = &u.u_nd;
596254Sroot 
6016694Smckusick 	ndp->ni_nameiop = LOOKUP | FOLLOW;
6116694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
6216694Smckusick 	ndp->ni_dirp = uap->fname;
6316694Smckusick 	ip = namei(ndp);
649167Ssam 	if (ip == NULL)
656254Sroot 		return;
669167Ssam 	if ((ip->i_mode&IFMT) != IFDIR) {
676254Sroot 		u.u_error = ENOTDIR;
686254Sroot 		goto bad;
696254Sroot 	}
709167Ssam 	if (access(ip, IEXEC))
716254Sroot 		goto bad;
7216664Smckusick 	IUNLOCK(ip);
737142Smckusick 	if (*ipp)
747142Smckusick 		irele(*ipp);
756254Sroot 	*ipp = ip;
766254Sroot 	return;
776254Sroot 
786254Sroot bad:
796254Sroot 	iput(ip);
806254Sroot }
816254Sroot 
8237Sbill /*
836254Sroot  * Open system call.
846254Sroot  */
856254Sroot open()
866254Sroot {
8712756Ssam 	struct a {
886254Sroot 		char	*fname;
897701Ssam 		int	mode;
9012756Ssam 		int	crtmode;
9112756Ssam 	} *uap = (struct a *) u.u_ap;
926254Sroot 
9316694Smckusick 	copen(uap->mode-FOPEN, uap->crtmode, uap->fname);
946254Sroot }
956254Sroot 
966254Sroot /*
976254Sroot  * Creat system call.
986254Sroot  */
9912756Ssam creat()
1006254Sroot {
10112756Ssam 	struct a {
1026254Sroot 		char	*fname;
1036254Sroot 		int	fmode;
10412756Ssam 	} *uap = (struct a *)u.u_ap;
1056254Sroot 
10616694Smckusick 	copen(FWRITE|FCREAT|FTRUNC, uap->fmode, uap->fname);
1076254Sroot }
1086254Sroot 
1096254Sroot /*
1106254Sroot  * Common code for open and creat.
11112756Ssam  * Check permissions, allocate an open file structure,
11212756Ssam  * and call the device open routine if any.
1136254Sroot  */
11416694Smckusick copen(mode, arg, fname)
11512756Ssam 	register int mode;
11612756Ssam 	int arg;
11716694Smckusick 	caddr_t fname;
11812756Ssam {
1196254Sroot 	register struct inode *ip;
1206254Sroot 	register struct file *fp;
12116694Smckusick 	register struct nameidata *ndp = &u.u_nd;
12212756Ssam 	int i;
1236254Sroot 
12412756Ssam #ifdef notdef
12512756Ssam 	if ((mode&(FREAD|FWRITE)) == 0) {
12612756Ssam 		u.u_error = EINVAL;
12712756Ssam 		return;
12812756Ssam 	}
12912756Ssam #endif
13016694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
13116694Smckusick 	ndp->ni_dirp = fname;
13212756Ssam 	if (mode&FCREAT) {
13318416Smckusick 		if (mode & FEXCL)
13418416Smckusick 			ndp->ni_nameiop = CREATE;
13518416Smckusick 		else
13618416Smckusick 			ndp->ni_nameiop = CREATE | FOLLOW;
13716694Smckusick 		ip = namei(ndp);
13812756Ssam 		if (ip == NULL) {
13912756Ssam 			if (u.u_error)
14012756Ssam 				return;
14116694Smckusick 			ip = maknode(arg&07777&(~ISVTX), ndp);
14212756Ssam 			if (ip == NULL)
14312756Ssam 				return;
14412756Ssam 			mode &= ~FTRUNC;
14512756Ssam 		} else {
14612756Ssam 			if (mode&FEXCL) {
14712756Ssam 				u.u_error = EEXIST;
14812756Ssam 				iput(ip);
14912756Ssam 				return;
15012756Ssam 			}
15112756Ssam 			mode &= ~FCREAT;
15212756Ssam 		}
15312756Ssam 	} else {
15416694Smckusick 		ndp->ni_nameiop = LOOKUP | FOLLOW;
15516694Smckusick 		ip = namei(ndp);
15612756Ssam 		if (ip == NULL)
15712756Ssam 			return;
15812756Ssam 	}
15912756Ssam 	if ((ip->i_mode & IFMT) == IFSOCK) {
16012756Ssam 		u.u_error = EOPNOTSUPP;
16112756Ssam 		goto bad;
16212756Ssam 	}
16312756Ssam 	if ((mode&FCREAT) == 0) {
1646254Sroot 		if (mode&FREAD)
1657701Ssam 			if (access(ip, IREAD))
1667701Ssam 				goto bad;
16716032Skarels 		if (mode&(FWRITE|FTRUNC)) {
1687701Ssam 			if (access(ip, IWRITE))
1697701Ssam 				goto bad;
1707701Ssam 			if ((ip->i_mode&IFMT) == IFDIR) {
1716254Sroot 				u.u_error = EISDIR;
1727701Ssam 				goto bad;
1737701Ssam 			}
1746254Sroot 		}
1756254Sroot 	}
17612756Ssam 	fp = falloc();
17712756Ssam 	if (fp == NULL)
17812756Ssam 		goto bad;
17912756Ssam 	if (mode&FTRUNC)
1809167Ssam 		itrunc(ip, (u_long)0);
18116664Smckusick 	IUNLOCK(ip);
18212756Ssam 	fp->f_flag = mode&FMASK;
18312756Ssam 	fp->f_type = DTYPE_INODE;
18412756Ssam 	fp->f_ops = &inodeops;
18512756Ssam 	fp->f_data = (caddr_t)ip;
1866254Sroot 	i = u.u_r.r_val1;
18712756Ssam 	if (setjmp(&u.u_qsave)) {
18812756Ssam 		if (u.u_error == 0)
18912756Ssam 			u.u_error = EINTR;
19012756Ssam 		u.u_ofile[i] = NULL;
19112756Ssam 		closef(fp);
19212756Ssam 		return;
19312756Ssam 	}
1948559Sroot 	u.u_error = openi(ip, mode);
19512756Ssam 	if (u.u_error == 0)
1966254Sroot 		return;
1976254Sroot 	u.u_ofile[i] = NULL;
1986254Sroot 	fp->f_count--;
1997142Smckusick 	irele(ip);
2007701Ssam 	return;
2017701Ssam bad:
2027701Ssam 	iput(ip);
2036254Sroot }
2046254Sroot 
2056254Sroot /*
2066254Sroot  * Mknod system call
2076254Sroot  */
2086254Sroot mknod()
2096254Sroot {
2106254Sroot 	register struct inode *ip;
2116254Sroot 	register struct a {
2126254Sroot 		char	*fname;
2136254Sroot 		int	fmode;
2146254Sroot 		int	dev;
21516694Smckusick 	} *uap = (struct a *)u.u_ap;
21616694Smckusick 	register struct nameidata *ndp = &u.u_nd;
2176254Sroot 
21812756Ssam 	if (!suser())
21912756Ssam 		return;
22016694Smckusick 	ndp->ni_nameiop = CREATE;
22116694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
22216694Smckusick 	ndp->ni_dirp = uap->fname;
22316694Smckusick 	ip = namei(ndp);
22412756Ssam 	if (ip != NULL) {
22512756Ssam 		u.u_error = EEXIST;
22612756Ssam 		goto out;
2276254Sroot 	}
2286254Sroot 	if (u.u_error)
2296254Sroot 		return;
23016694Smckusick 	ip = maknode(uap->fmode, ndp);
2316254Sroot 	if (ip == NULL)
2326254Sroot 		return;
23312756Ssam 	switch (ip->i_mode & IFMT) {
23412756Ssam 
23515093Smckusick 	case IFMT:	/* used by badsect to flag bad sectors */
23612756Ssam 	case IFCHR:
23712756Ssam 	case IFBLK:
23812756Ssam 		if (uap->dev) {
23912756Ssam 			/*
24012756Ssam 			 * Want to be able to use this to make badblock
24112756Ssam 			 * inodes, so don't truncate the dev number.
24212756Ssam 			 */
24312756Ssam 			ip->i_rdev = uap->dev;
24412756Ssam 			ip->i_flag |= IACC|IUPD|ICHG;
24512756Ssam 		}
2466254Sroot 	}
2476254Sroot 
2486254Sroot out:
2496254Sroot 	iput(ip);
2506254Sroot }
2516254Sroot 
2526254Sroot /*
2536254Sroot  * link system call
2546254Sroot  */
2556254Sroot link()
2566254Sroot {
2576254Sroot 	register struct inode *ip, *xp;
2586254Sroot 	register struct a {
2596254Sroot 		char	*target;
2606254Sroot 		char	*linkname;
26116694Smckusick 	} *uap = (struct a *)u.u_ap;
26216694Smckusick 	register struct nameidata *ndp = &u.u_nd;
2636254Sroot 
26416694Smckusick 	ndp->ni_nameiop = LOOKUP | FOLLOW;
26516694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
26616694Smckusick 	ndp->ni_dirp = uap->target;
26716694Smckusick 	ip = namei(ndp);	/* well, this routine is doomed anyhow */
2686254Sroot 	if (ip == NULL)
2696254Sroot 		return;
2709167Ssam 	if ((ip->i_mode&IFMT) == IFDIR && !suser()) {
2717439Sroot 		iput(ip);
2727439Sroot 		return;
2737439Sroot 	}
2746254Sroot 	ip->i_nlink++;
2756254Sroot 	ip->i_flag |= ICHG;
2768673Sroot 	iupdat(ip, &time, &time, 1);
27716664Smckusick 	IUNLOCK(ip);
27816694Smckusick 	ndp->ni_nameiop = CREATE;
27916694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
28016694Smckusick 	ndp->ni_dirp = (caddr_t)uap->linkname;
28116694Smckusick 	xp = namei(ndp);
2826254Sroot 	if (xp != NULL) {
2836254Sroot 		u.u_error = EEXIST;
2846254Sroot 		iput(xp);
2856254Sroot 		goto out;
2866254Sroot 	}
2876254Sroot 	if (u.u_error)
2886254Sroot 		goto out;
28916694Smckusick 	if (ndp->ni_pdir->i_dev != ip->i_dev) {
29016694Smckusick 		iput(ndp->ni_pdir);
2916254Sroot 		u.u_error = EXDEV;
2926254Sroot 		goto out;
2936254Sroot 	}
29416694Smckusick 	u.u_error = direnter(ip, ndp);
2956254Sroot out:
2966254Sroot 	if (u.u_error) {
2976254Sroot 		ip->i_nlink--;
2986254Sroot 		ip->i_flag |= ICHG;
2996254Sroot 	}
3007142Smckusick 	irele(ip);
3016254Sroot }
3026254Sroot 
3036254Sroot /*
3046254Sroot  * symlink -- make a symbolic link
3056254Sroot  */
3066254Sroot symlink()
3076254Sroot {
3086254Sroot 	register struct a {
3096254Sroot 		char	*target;
3106254Sroot 		char	*linkname;
31116694Smckusick 	} *uap = (struct a *)u.u_ap;
3126254Sroot 	register struct inode *ip;
3136254Sroot 	register char *tp;
3146254Sroot 	register c, nc;
31516694Smckusick 	register struct nameidata *ndp = &u.u_nd;
3166254Sroot 
3176254Sroot 	tp = uap->target;
3186254Sroot 	nc = 0;
3196254Sroot 	while (c = fubyte(tp)) {
3206254Sroot 		if (c < 0) {
3216254Sroot 			u.u_error = EFAULT;
3226254Sroot 			return;
3236254Sroot 		}
3246254Sroot 		tp++;
3256254Sroot 		nc++;
3266254Sroot 	}
32716694Smckusick 	ndp->ni_nameiop = CREATE;
32816694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
32916694Smckusick 	ndp->ni_dirp = uap->linkname;
33016694Smckusick 	ip = namei(ndp);
3316254Sroot 	if (ip) {
3326254Sroot 		iput(ip);
3336254Sroot 		u.u_error = EEXIST;
3346254Sroot 		return;
3356254Sroot 	}
3366254Sroot 	if (u.u_error)
3376254Sroot 		return;
33816694Smckusick 	ip = maknode(IFLNK | 0777, ndp);
3396254Sroot 	if (ip == NULL)
3406254Sroot 		return;
3417826Sroot 	u.u_error = rdwri(UIO_WRITE, ip, uap->target, nc, 0, 0, (int *)0);
3429167Ssam 	/* handle u.u_error != 0 */
3436254Sroot 	iput(ip);
3446254Sroot }
3456254Sroot 
3466254Sroot /*
3476254Sroot  * Unlink system call.
3486254Sroot  * Hard to avoid races here, especially
3496254Sroot  * in unlinking directories.
3506254Sroot  */
3516254Sroot unlink()
3526254Sroot {
3536254Sroot 	struct a {
3546254Sroot 		char	*fname;
35516694Smckusick 	} *uap = (struct a *)u.u_ap;
3569167Ssam 	register struct inode *ip, *dp;
35716694Smckusick 	register struct nameidata *ndp = &u.u_nd;
3586254Sroot 
35916694Smckusick 	ndp->ni_nameiop = DELETE | LOCKPARENT;
36016694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
36116694Smckusick 	ndp->ni_dirp = uap->fname;
36216694Smckusick 	ip = namei(ndp);
3639167Ssam 	if (ip == NULL)
3646254Sroot 		return;
36516694Smckusick 	dp = ndp->ni_pdir;
3669167Ssam 	if ((ip->i_mode&IFMT) == IFDIR && !suser())
3676254Sroot 		goto out;
3686254Sroot 	/*
3696254Sroot 	 * Don't unlink a mounted file.
3706254Sroot 	 */
3719167Ssam 	if (ip->i_dev != dp->i_dev) {
3726254Sroot 		u.u_error = EBUSY;
3736254Sroot 		goto out;
3746254Sroot 	}
3756254Sroot 	if (ip->i_flag&ITEXT)
3766254Sroot 		xrele(ip);	/* try once to free text */
37716694Smckusick 	if (dirremove(ndp)) {
3787535Sroot 		ip->i_nlink--;
3797535Sroot 		ip->i_flag |= ICHG;
3806254Sroot 	}
3816254Sroot out:
3829167Ssam 	if (dp == ip)
3837142Smckusick 		irele(ip);
3847142Smckusick 	else
3857142Smckusick 		iput(ip);
3869167Ssam 	iput(dp);
3876254Sroot }
3886254Sroot 
3896254Sroot /*
3906254Sroot  * Seek system call
3916254Sroot  */
3928040Sroot lseek()
3936254Sroot {
3946254Sroot 	register struct file *fp;
3956254Sroot 	register struct a {
3967701Ssam 		int	fd;
3976254Sroot 		off_t	off;
3986254Sroot 		int	sbase;
39916694Smckusick 	} *uap = (struct a *)u.u_ap;
4006254Sroot 
40116540Ssam 	GETF(fp, uap->fd);
40216540Ssam 	if (fp->f_type != DTYPE_INODE) {
40316540Ssam 		u.u_error = ESPIPE;
4046254Sroot 		return;
40516540Ssam 	}
40613878Ssam 	switch (uap->sbase) {
40713878Ssam 
40813878Ssam 	case L_INCR:
40913878Ssam 		fp->f_offset += uap->off;
41013878Ssam 		break;
41113878Ssam 
41213878Ssam 	case L_XTND:
41313878Ssam 		fp->f_offset = uap->off + ((struct inode *)fp->f_data)->i_size;
41413878Ssam 		break;
41513878Ssam 
41613878Ssam 	case L_SET:
41713878Ssam 		fp->f_offset = uap->off;
41813878Ssam 		break;
41913878Ssam 
42013878Ssam 	default:
42113878Ssam 		u.u_error = EINVAL;
42213878Ssam 		return;
42313878Ssam 	}
42413878Ssam 	u.u_r.r_off = fp->f_offset;
4256254Sroot }
4266254Sroot 
4276254Sroot /*
4286254Sroot  * Access system call
4296254Sroot  */
4306254Sroot saccess()
4316254Sroot {
4326254Sroot 	register svuid, svgid;
4336254Sroot 	register struct inode *ip;
4346254Sroot 	register struct a {
4356254Sroot 		char	*fname;
4366254Sroot 		int	fmode;
43716694Smckusick 	} *uap = (struct a *)u.u_ap;
43816694Smckusick 	register struct nameidata *ndp = &u.u_nd;
4396254Sroot 
4406254Sroot 	svuid = u.u_uid;
4416254Sroot 	svgid = u.u_gid;
4426254Sroot 	u.u_uid = u.u_ruid;
4436254Sroot 	u.u_gid = u.u_rgid;
44416694Smckusick 	ndp->ni_nameiop = LOOKUP | FOLLOW;
44516694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
44616694Smckusick 	ndp->ni_dirp = uap->fname;
44716694Smckusick 	ip = namei(ndp);
4486254Sroot 	if (ip != NULL) {
44912756Ssam 		if ((uap->fmode&R_OK) && access(ip, IREAD))
4507701Ssam 			goto done;
45112756Ssam 		if ((uap->fmode&W_OK) && access(ip, IWRITE))
4527701Ssam 			goto done;
45312756Ssam 		if ((uap->fmode&X_OK) && access(ip, IEXEC))
4547701Ssam 			goto done;
4557701Ssam done:
4566254Sroot 		iput(ip);
4576254Sroot 	}
4586254Sroot 	u.u_uid = svuid;
4596254Sroot 	u.u_gid = svgid;
4606254Sroot }
4616254Sroot 
4626254Sroot /*
4636574Smckusic  * Stat system call.  This version follows links.
46437Sbill  */
46537Sbill stat()
46637Sbill {
46737Sbill 
46816694Smckusick 	stat1(FOLLOW);
46937Sbill }
47037Sbill 
47137Sbill /*
4726574Smckusic  * Lstat system call.  This version does not follow links.
4735992Swnj  */
4745992Swnj lstat()
4755992Swnj {
47612756Ssam 
47716694Smckusick 	stat1(NOFOLLOW);
47812756Ssam }
47912756Ssam 
48012756Ssam stat1(follow)
48112756Ssam 	int follow;
48212756Ssam {
4835992Swnj 	register struct inode *ip;
4845992Swnj 	register struct a {
4855992Swnj 		char	*fname;
48612756Ssam 		struct stat *ub;
48716694Smckusick 	} *uap = (struct a *)u.u_ap;
48812756Ssam 	struct stat sb;
48916694Smckusick 	register struct nameidata *ndp = &u.u_nd;
4905992Swnj 
49116694Smckusick 	ndp->ni_nameiop = LOOKUP | follow;
49216694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
49316694Smckusick 	ndp->ni_dirp = uap->fname;
49416694Smckusick 	ip = namei(ndp);
4955992Swnj 	if (ip == NULL)
4965992Swnj 		return;
49713043Ssam 	(void) ino_stat(ip, &sb);
4985992Swnj 	iput(ip);
49912756Ssam 	u.u_error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb));
5005992Swnj }
5015992Swnj 
5025992Swnj /*
5035992Swnj  * Return target name of a symbolic link
50437Sbill  */
5055992Swnj readlink()
5065992Swnj {
5075992Swnj 	register struct inode *ip;
5085992Swnj 	register struct a {
5095992Swnj 		char	*name;
5105992Swnj 		char	*buf;
5115992Swnj 		int	count;
5127826Sroot 	} *uap = (struct a *)u.u_ap;
51316694Smckusick 	register struct nameidata *ndp = &u.u_nd;
5147826Sroot 	int resid;
5155992Swnj 
51616694Smckusick 	ndp->ni_nameiop = LOOKUP;
51716694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
51816694Smckusick 	ndp->ni_dirp = uap->name;
51916694Smckusick 	ip = namei(ndp);
5205992Swnj 	if (ip == NULL)
5215992Swnj 		return;
5225992Swnj 	if ((ip->i_mode&IFMT) != IFLNK) {
52321015Smckusick 		u.u_error = EINVAL;
5245992Swnj 		goto out;
5255992Swnj 	}
5267826Sroot 	u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, 0, 0, &resid);
5275992Swnj out:
5285992Swnj 	iput(ip);
5297826Sroot 	u.u_r.r_val1 = uap->count - resid;
5305992Swnj }
5315992Swnj 
5329167Ssam /*
5339167Ssam  * Change mode of a file given path name.
5349167Ssam  */
5356254Sroot chmod()
5365992Swnj {
5377701Ssam 	struct inode *ip;
5387701Ssam 	struct a {
5396254Sroot 		char	*fname;
5406254Sroot 		int	fmode;
54116694Smckusick 	} *uap = (struct a *)u.u_ap;
5425992Swnj 
54316694Smckusick 	if ((ip = owner(uap->fname, FOLLOW)) == NULL)
5445992Swnj 		return;
54521015Smckusick 	u.u_error = chmod1(ip, uap->fmode);
5469167Ssam 	iput(ip);
5477701Ssam }
5487439Sroot 
5499167Ssam /*
5509167Ssam  * Change mode of a file given a file descriptor.
5519167Ssam  */
5527701Ssam fchmod()
5537701Ssam {
5547701Ssam 	struct a {
5557701Ssam 		int	fd;
5567701Ssam 		int	fmode;
55716694Smckusick 	} *uap = (struct a *)u.u_ap;
5587701Ssam 	register struct inode *ip;
5597701Ssam 	register struct file *fp;
5607701Ssam 
56112756Ssam 	fp = getinode(uap->fd);
5627701Ssam 	if (fp == NULL)
5637701Ssam 		return;
56412756Ssam 	ip = (struct inode *)fp->f_data;
5659167Ssam 	if (u.u_uid != ip->i_uid && !suser())
5669167Ssam 		return;
56716664Smckusick 	ILOCK(ip);
56821015Smckusick 	u.u_error = chmod1(ip, uap->fmode);
56916664Smckusick 	IUNLOCK(ip);
5707701Ssam }
5717701Ssam 
5729167Ssam /*
5739167Ssam  * Change the mode on a file.
5749167Ssam  * Inode must be locked before calling.
5759167Ssam  */
5767701Ssam chmod1(ip, mode)
5777701Ssam 	register struct inode *ip;
5787701Ssam 	register int mode;
5797701Ssam {
5807868Sroot 
58121015Smckusick 	if (ip->i_fs->fs_ronly)
58221015Smckusick 		return (EROFS);
5836254Sroot 	ip->i_mode &= ~07777;
5847439Sroot 	if (u.u_uid) {
58521015Smckusick 		if ((ip->i_mode & IFMT) != IFDIR)
58621015Smckusick 			mode &= ~ISVTX;
58711811Ssam 		if (!groupmember(ip->i_gid))
58811811Ssam 			mode &= ~ISGID;
5897439Sroot 	}
5907701Ssam 	ip->i_mode |= mode&07777;
5916254Sroot 	ip->i_flag |= ICHG;
5926254Sroot 	if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0)
5936254Sroot 		xrele(ip);
59421015Smckusick 	return (0);
5955992Swnj }
5965992Swnj 
5979167Ssam /*
5989167Ssam  * Set ownership given a path name.
5999167Ssam  */
6006254Sroot chown()
60137Sbill {
6027701Ssam 	struct inode *ip;
6037701Ssam 	struct a {
6046254Sroot 		char	*fname;
6056254Sroot 		int	uid;
6066254Sroot 		int	gid;
60716694Smckusick 	} *uap = (struct a *)u.u_ap;
60837Sbill 
60916694Smckusick 	if (!suser() || (ip = owner(uap->fname, NOFOLLOW)) == NULL)
61037Sbill 		return;
61111811Ssam 	u.u_error = chown1(ip, uap->uid, uap->gid);
6129167Ssam 	iput(ip);
6137701Ssam }
6147439Sroot 
6159167Ssam /*
6169167Ssam  * Set ownership given a file descriptor.
6179167Ssam  */
6187701Ssam fchown()
6197701Ssam {
6207701Ssam 	struct a {
6217701Ssam 		int	fd;
6227701Ssam 		int	uid;
6237701Ssam 		int	gid;
62416694Smckusick 	} *uap = (struct a *)u.u_ap;
6257701Ssam 	register struct inode *ip;
6267701Ssam 	register struct file *fp;
6277701Ssam 
62812756Ssam 	fp = getinode(uap->fd);
6297701Ssam 	if (fp == NULL)
6307701Ssam 		return;
63112756Ssam 	ip = (struct inode *)fp->f_data;
63211821Ssam 	if (!suser())
6339167Ssam 		return;
63416664Smckusick 	ILOCK(ip);
63511811Ssam 	u.u_error = chown1(ip, uap->uid, uap->gid);
63616664Smckusick 	IUNLOCK(ip);
6377701Ssam }
6387701Ssam 
6397701Ssam /*
6407701Ssam  * Perform chown operation on inode ip;
6417701Ssam  * inode must be locked prior to call.
6427701Ssam  */
6437701Ssam chown1(ip, uid, gid)
6447701Ssam 	register struct inode *ip;
6457701Ssam 	int uid, gid;
6467701Ssam {
6477701Ssam #ifdef QUOTA
6487701Ssam 	register long change;
64911811Ssam #endif
6507701Ssam 
65121015Smckusick 	if (ip->i_fs->fs_ronly)
65221015Smckusick 		return (EROFS);
65311811Ssam 	if (uid == -1)
65411811Ssam 		uid = ip->i_uid;
65511811Ssam 	if (gid == -1)
65611811Ssam 		gid = ip->i_gid;
65711811Ssam #ifdef QUOTA
65814385Ssam 	if (ip->i_uid == uid)		/* this just speeds things a little */
6597482Skre 		change = 0;
66012646Ssam 	else
66112646Ssam 		change = ip->i_blocks;
66212646Ssam 	(void) chkdq(ip, -change, 1);
66312646Ssam 	(void) chkiq(ip->i_dev, ip, ip->i_uid, 1);
6647482Skre 	dqrele(ip->i_dquot);
6657482Skre #endif
66611811Ssam 	ip->i_uid = uid;
66711811Ssam 	ip->i_gid = gid;
6686254Sroot 	ip->i_flag |= ICHG;
6696254Sroot 	if (u.u_ruid != 0)
6706254Sroot 		ip->i_mode &= ~(ISUID|ISGID);
6717701Ssam #ifdef QUOTA
6727482Skre 	ip->i_dquot = inoquota(ip);
67312646Ssam 	(void) chkdq(ip, change, 1);
67412646Ssam 	(void) chkiq(ip->i_dev, (struct inode *)NULL, uid, 1);
67512646Ssam 	return (u.u_error);		/* should == 0 ALWAYS !! */
67612646Ssam #else
67712646Ssam 	return (0);
6787482Skre #endif
67937Sbill }
68037Sbill 
68111811Ssam utimes()
68211811Ssam {
68311811Ssam 	register struct a {
68411811Ssam 		char	*fname;
68511811Ssam 		struct	timeval *tptr;
68611811Ssam 	} *uap = (struct a *)u.u_ap;
68711811Ssam 	register struct inode *ip;
68811811Ssam 	struct timeval tv[2];
68911811Ssam 
69016694Smckusick 	if ((ip = owner(uap->fname, FOLLOW)) == NULL)
69111811Ssam 		return;
69221015Smckusick 	if (ip->i_fs->fs_ronly) {
69321015Smckusick 		u.u_error = EROFS;
69421015Smckusick 		iput(ip);
69521015Smckusick 		return;
69621015Smckusick 	}
69711811Ssam 	u.u_error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv));
69811811Ssam 	if (u.u_error == 0) {
69911811Ssam 		ip->i_flag |= IACC|IUPD|ICHG;
70011811Ssam 		iupdat(ip, &tv[0], &tv[1], 0);
70111811Ssam 	}
70211811Ssam 	iput(ip);
70311811Ssam }
70411811Ssam 
7059167Ssam /*
7069167Ssam  * Flush any pending I/O.
7079167Ssam  */
7086254Sroot sync()
70937Sbill {
71037Sbill 
7118673Sroot 	update();
71237Sbill }
7137535Sroot 
7149167Ssam /*
7159167Ssam  * Truncate a file given its path name.
7169167Ssam  */
7177701Ssam truncate()
7187701Ssam {
7197701Ssam 	struct a {
7207701Ssam 		char	*fname;
7219167Ssam 		u_long	length;
7227826Sroot 	} *uap = (struct a *)u.u_ap;
7237701Ssam 	struct inode *ip;
72416694Smckusick 	register struct nameidata *ndp = &u.u_nd;
7257701Ssam 
72616694Smckusick 	ndp->ni_nameiop = LOOKUP | FOLLOW;
72716694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
72816694Smckusick 	ndp->ni_dirp = uap->fname;
72916694Smckusick 	ip = namei(ndp);
7307701Ssam 	if (ip == NULL)
7317701Ssam 		return;
7327701Ssam 	if (access(ip, IWRITE))
7337701Ssam 		goto bad;
7347701Ssam 	if ((ip->i_mode&IFMT) == IFDIR) {
7357701Ssam 		u.u_error = EISDIR;
7367701Ssam 		goto bad;
7377701Ssam 	}
7387701Ssam 	itrunc(ip, uap->length);
7397701Ssam bad:
7407701Ssam 	iput(ip);
7417701Ssam }
7427701Ssam 
7439167Ssam /*
7449167Ssam  * Truncate a file given a file descriptor.
7459167Ssam  */
7467701Ssam ftruncate()
7477701Ssam {
7487701Ssam 	struct a {
7497701Ssam 		int	fd;
7509167Ssam 		u_long	length;
7517826Sroot 	} *uap = (struct a *)u.u_ap;
7527701Ssam 	struct inode *ip;
7537701Ssam 	struct file *fp;
7547701Ssam 
75512756Ssam 	fp = getinode(uap->fd);
7567701Ssam 	if (fp == NULL)
7577701Ssam 		return;
7587701Ssam 	if ((fp->f_flag&FWRITE) == 0) {
7597701Ssam 		u.u_error = EINVAL;
7607701Ssam 		return;
7617701Ssam 	}
76212756Ssam 	ip = (struct inode *)fp->f_data;
76316664Smckusick 	ILOCK(ip);
7647701Ssam 	itrunc(ip, uap->length);
76516664Smckusick 	IUNLOCK(ip);
7667701Ssam }
7677701Ssam 
7689167Ssam /*
7699167Ssam  * Synch an open file.
7709167Ssam  */
7719167Ssam fsync()
7729167Ssam {
7739167Ssam 	struct a {
7749167Ssam 		int	fd;
7759167Ssam 	} *uap = (struct a *)u.u_ap;
7769167Ssam 	struct inode *ip;
7779167Ssam 	struct file *fp;
7789167Ssam 
77912756Ssam 	fp = getinode(uap->fd);
7809167Ssam 	if (fp == NULL)
7819167Ssam 		return;
78212756Ssam 	ip = (struct inode *)fp->f_data;
78316664Smckusick 	ILOCK(ip);
7849167Ssam 	syncip(ip);
78516664Smckusick 	IUNLOCK(ip);
7869167Ssam }
7879167Ssam 
7889167Ssam /*
7899167Ssam  * Rename system call.
7909167Ssam  * 	rename("foo", "bar");
7919167Ssam  * is essentially
7929167Ssam  *	unlink("bar");
7939167Ssam  *	link("foo", "bar");
7949167Ssam  *	unlink("foo");
7959167Ssam  * but ``atomically''.  Can't do full commit without saving state in the
7969167Ssam  * inode on disk which isn't feasible at this time.  Best we can do is
7979167Ssam  * always guarantee the target exists.
7989167Ssam  *
7999167Ssam  * Basic algorithm is:
8009167Ssam  *
8019167Ssam  * 1) Bump link count on source while we're linking it to the
8029167Ssam  *    target.  This also insure the inode won't be deleted out
80316776Smckusick  *    from underneath us while we work (it may be truncated by
80416776Smckusick  *    a concurrent `trunc' or `open' for creation).
8059167Ssam  * 2) Link source to destination.  If destination already exists,
8069167Ssam  *    delete it first.
80716776Smckusick  * 3) Unlink source reference to inode if still around. If a
80816776Smckusick  *    directory was moved and the parent of the destination
8099167Ssam  *    is different from the source, patch the ".." entry in the
8109167Ssam  *    directory.
8119167Ssam  *
8129167Ssam  * Source and destination must either both be directories, or both
8139167Ssam  * not be directories.  If target is a directory, it must be empty.
8149167Ssam  */
8157701Ssam rename()
8167701Ssam {
8177701Ssam 	struct a {
8187701Ssam 		char	*from;
8197701Ssam 		char	*to;
82016694Smckusick 	} *uap = (struct a *)u.u_ap;
8219167Ssam 	register struct inode *ip, *xp, *dp;
82216776Smckusick 	struct dirtemplate dirbuf;
82316776Smckusick 	int doingdirectory = 0, oldparent = 0, newparent = 0;
82416694Smckusick 	register struct nameidata *ndp = &u.u_nd;
82510051Ssam 	int error = 0;
8267701Ssam 
82716694Smckusick 	ndp->ni_nameiop = DELETE | LOCKPARENT;
82816694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
82916694Smckusick 	ndp->ni_dirp = uap->from;
83016694Smckusick 	ip = namei(ndp);
8319167Ssam 	if (ip == NULL)
8329167Ssam 		return;
83316694Smckusick 	dp = ndp->ni_pdir;
8349167Ssam 	if ((ip->i_mode&IFMT) == IFDIR) {
8359167Ssam 		register struct direct *d;
8369167Ssam 
83716694Smckusick 		d = &ndp->ni_dent;
8389167Ssam 		/*
83911641Ssam 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
8409167Ssam 		 */
84111641Ssam 		if ((d->d_namlen == 1 && d->d_name[0] == '.') ||
84211641Ssam 		    (d->d_namlen == 2 && bcmp(d->d_name, "..", 2) == 0) ||
84316776Smckusick 		    (dp == ip) || (ip->i_flag & IRENAME)) {
84411641Ssam 			iput(dp);
84511641Ssam 			if (dp == ip)
84611641Ssam 				irele(ip);
84711641Ssam 			else
84810051Ssam 				iput(ip);
84911641Ssam 			u.u_error = EINVAL;
85011641Ssam 			return;
8519167Ssam 		}
85216776Smckusick 		ip->i_flag |= IRENAME;
8539167Ssam 		oldparent = dp->i_number;
8549167Ssam 		doingdirectory++;
8559167Ssam 	}
85611641Ssam 	iput(dp);
8579167Ssam 
8589167Ssam 	/*
8599167Ssam 	 * 1) Bump link count while we're moving stuff
8609167Ssam 	 *    around.  If we crash somewhere before
8619167Ssam 	 *    completing our work, the link count
8629167Ssam 	 *    may be wrong, but correctable.
8639167Ssam 	 */
8649167Ssam 	ip->i_nlink++;
8659167Ssam 	ip->i_flag |= ICHG;
8669167Ssam 	iupdat(ip, &time, &time, 1);
86716664Smckusick 	IUNLOCK(ip);
8689167Ssam 
8699167Ssam 	/*
8709167Ssam 	 * When the target exists, both the directory
8719167Ssam 	 * and target inodes are returned locked.
8729167Ssam 	 */
87316694Smckusick 	ndp->ni_nameiop = CREATE | LOCKPARENT | NOCACHE;
87416694Smckusick 	ndp->ni_dirp = (caddr_t)uap->to;
87516694Smckusick 	xp = namei(ndp);
87610051Ssam 	if (u.u_error) {
87710051Ssam 		error = u.u_error;
8789167Ssam 		goto out;
87910051Ssam 	}
88016694Smckusick 	dp = ndp->ni_pdir;
8819167Ssam 	/*
88211641Ssam 	 * If ".." must be changed (ie the directory gets a new
88312816Smckusick 	 * parent) then the source directory must not be in the
88412816Smckusick 	 * directory heirarchy above the target, as this would
88512816Smckusick 	 * orphan everything below the source directory. Also
88612816Smckusick 	 * the user must have write permission in the source so
88712816Smckusick 	 * as to be able to change "..". We must repeat the call
88812816Smckusick 	 * to namei, as the parent directory is unlocked by the
88912816Smckusick 	 * call to checkpath().
89011641Ssam 	 */
89116776Smckusick 	if (oldparent != dp->i_number)
89216776Smckusick 		newparent = dp->i_number;
89316776Smckusick 	if (doingdirectory && newparent) {
89412816Smckusick 		if (access(ip, IWRITE))
89512816Smckusick 			goto bad;
89612816Smckusick 		do {
89716694Smckusick 			dp = ndp->ni_pdir;
89812816Smckusick 			if (xp != NULL)
89912816Smckusick 				iput(xp);
90012816Smckusick 			u.u_error = checkpath(ip, dp);
90112816Smckusick 			if (u.u_error)
90212816Smckusick 				goto out;
90316694Smckusick 			xp = namei(ndp);
90412816Smckusick 			if (u.u_error) {
90512816Smckusick 				error = u.u_error;
90612816Smckusick 				goto out;
90712816Smckusick 			}
90816694Smckusick 		} while (dp != ndp->ni_pdir);
90912816Smckusick 	}
91011641Ssam 	/*
9119167Ssam 	 * 2) If target doesn't exist, link the target
9129167Ssam 	 *    to the source and unlink the source.
9139167Ssam 	 *    Otherwise, rewrite the target directory
9149167Ssam 	 *    entry to reference the source inode and
9159167Ssam 	 *    expunge the original entry's existence.
9169167Ssam 	 */
9179167Ssam 	if (xp == NULL) {
9189167Ssam 		if (dp->i_dev != ip->i_dev) {
91910051Ssam 			error = EXDEV;
9209167Ssam 			goto bad;
9219167Ssam 		}
9229167Ssam 		/*
92316776Smckusick 		 * Account for ".." in new directory.
92416776Smckusick 		 * When source and destination have the same
92516776Smckusick 		 * parent we don't fool with the link count.
9269167Ssam 		 */
92716776Smckusick 		if (doingdirectory && newparent) {
9289167Ssam 			dp->i_nlink++;
9299167Ssam 			dp->i_flag |= ICHG;
9309167Ssam 			iupdat(dp, &time, &time, 1);
9319167Ssam 		}
93216694Smckusick 		error = direnter(ip, ndp);
93310850Ssam 		if (error)
9349167Ssam 			goto out;
9359167Ssam 	} else {
9369167Ssam 		if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) {
93710051Ssam 			error = EXDEV;
9389167Ssam 			goto bad;
9399167Ssam 		}
9409167Ssam 		/*
94110590Ssam 		 * Short circuit rename(foo, foo).
94210590Ssam 		 */
94310590Ssam 		if (xp->i_number == ip->i_number)
94410590Ssam 			goto bad;
94510590Ssam 		/*
94610051Ssam 		 * Target must be empty if a directory
94710051Ssam 		 * and have no links to it.
9489167Ssam 		 * Also, insure source and target are
9499167Ssam 		 * compatible (both directories, or both
9509167Ssam 		 * not directories).
9519167Ssam 		 */
9529167Ssam 		if ((xp->i_mode&IFMT) == IFDIR) {
95316776Smckusick 			if (!dirempty(xp, dp->i_number) || xp->i_nlink > 2) {
95410051Ssam 				error = ENOTEMPTY;
9559167Ssam 				goto bad;
9569167Ssam 			}
9579167Ssam 			if (!doingdirectory) {
95810051Ssam 				error = ENOTDIR;
9599167Ssam 				goto bad;
9609167Ssam 			}
96116776Smckusick 			cacheinval(dp);
9629167Ssam 		} else if (doingdirectory) {
96310051Ssam 			error = EISDIR;
9649167Ssam 			goto bad;
9659167Ssam 		}
96616694Smckusick 		dirrewrite(dp, ip, ndp);
96710051Ssam 		if (u.u_error) {
96810051Ssam 			error = u.u_error;
9699167Ssam 			goto bad1;
97010051Ssam 		}
9719167Ssam 		/*
97210051Ssam 		 * Adjust the link count of the target to
97310051Ssam 		 * reflect the dirrewrite above.  If this is
97410051Ssam 		 * a directory it is empty and there are
97510051Ssam 		 * no links to it, so we can squash the inode and
97610051Ssam 		 * any space associated with it.  We disallowed
97710051Ssam 		 * renaming over top of a directory with links to
97816776Smckusick 		 * it above, as the remaining link would point to
97916776Smckusick 		 * a directory without "." or ".." entries.
9809167Ssam 		 */
98110051Ssam 		xp->i_nlink--;
9829167Ssam 		if (doingdirectory) {
98310051Ssam 			if (--xp->i_nlink != 0)
98410051Ssam 				panic("rename: linked directory");
9859167Ssam 			itrunc(xp, (u_long)0);
98610051Ssam 		}
9879167Ssam 		xp->i_flag |= ICHG;
9889167Ssam 		iput(xp);
98910246Ssam 		xp = NULL;
9909167Ssam 	}
9919167Ssam 
9929167Ssam 	/*
9939167Ssam 	 * 3) Unlink the source.
9949167Ssam 	 */
99516694Smckusick 	ndp->ni_nameiop = DELETE | LOCKPARENT;
99616694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
99716694Smckusick 	ndp->ni_dirp = uap->from;
99816776Smckusick 	xp = namei(ndp);
99917758Smckusick 	if (xp != NULL)
100017758Smckusick 		dp = ndp->ni_pdir;
100117758Smckusick 	else
100217758Smckusick 		dp = NULL;
10039167Ssam 	/*
100416776Smckusick 	 * Insure that the directory entry still exists and has not
100516776Smckusick 	 * changed while the new name has been entered. If the source is
100616776Smckusick 	 * a file then the entry may have been unlinked or renamed. In
100716776Smckusick 	 * either case there is no further work to be done. If the source
100816776Smckusick 	 * is a directory then it cannot have been rmdir'ed; its link
100916776Smckusick 	 * count of three would cause a rmdir to fail with ENOTEMPTY.
101016776Smckusick 	 * The IRENAME flag insures that it cannot be moved by another
101116776Smckusick 	 * rename.
10129167Ssam 	 */
101317758Smckusick 	if (xp != ip) {
101416776Smckusick 		if (doingdirectory)
101517758Smckusick 			panic("rename: lost dir entry");
101616776Smckusick 	} else {
10179167Ssam 		/*
101816776Smckusick 		 * If the source is a directory with a
101916776Smckusick 		 * new parent, the link count of the old
102016776Smckusick 		 * parent directory must be decremented
102116776Smckusick 		 * and ".." set to point to the new parent.
10229167Ssam 		 */
102316776Smckusick 		if (doingdirectory && newparent) {
10249167Ssam 			dp->i_nlink--;
10259167Ssam 			dp->i_flag |= ICHG;
102616776Smckusick 			error = rdwri(UIO_READ, xp, (caddr_t)&dirbuf,
102716776Smckusick 				sizeof (struct dirtemplate), (off_t)0, 1,
102816776Smckusick 				(int *)0);
102916776Smckusick 			if (error == 0) {
103016776Smckusick 				if (dirbuf.dotdot_namlen != 2 ||
103116776Smckusick 				    dirbuf.dotdot_name[0] != '.' ||
103216776Smckusick 				    dirbuf.dotdot_name[1] != '.') {
103316776Smckusick 					printf("rename: mangled dir\n");
103416776Smckusick 				} else {
103516776Smckusick 					dirbuf.dotdot_ino = newparent;
103616776Smckusick 					(void) rdwri(UIO_WRITE, xp,
103716776Smckusick 					    (caddr_t)&dirbuf,
103816776Smckusick 					    sizeof (struct dirtemplate),
103916776Smckusick 					    (off_t)0, 1, (int *)0);
104016776Smckusick 					cacheinval(dp);
104116776Smckusick 				}
104216776Smckusick 			}
10439167Ssam 		}
104416694Smckusick 		if (dirremove(ndp)) {
104516776Smckusick 			xp->i_nlink--;
104616776Smckusick 			xp->i_flag |= ICHG;
10479167Ssam 		}
104816776Smckusick 		xp->i_flag &= ~IRENAME;
104916776Smckusick 		if (error == 0)		/* XXX conservative */
105010051Ssam 			error = u.u_error;
10519167Ssam 	}
10529167Ssam 	if (dp)
10539167Ssam 		iput(dp);
105416776Smckusick 	if (xp)
105516776Smckusick 		iput(xp);
105616776Smckusick 	irele(ip);
105716776Smckusick 	if (error)
105816776Smckusick 		u.u_error = error;
105916776Smckusick 	return;
10609167Ssam 
10619167Ssam bad:
106210246Ssam 	iput(dp);
10639167Ssam bad1:
10649167Ssam 	if (xp)
106510246Ssam 		iput(xp);
10669167Ssam out:
10679167Ssam 	ip->i_nlink--;
10689167Ssam 	ip->i_flag |= ICHG;
10699167Ssam 	irele(ip);
107010051Ssam 	if (error)
107110051Ssam 		u.u_error = error;
10727701Ssam }
10737701Ssam 
10747535Sroot /*
10757535Sroot  * Make a new file.
10767535Sroot  */
10777535Sroot struct inode *
107816694Smckusick maknode(mode, ndp)
10797535Sroot 	int mode;
108016694Smckusick 	register struct nameidata *ndp;
10817535Sroot {
10827535Sroot 	register struct inode *ip;
108316694Smckusick 	register struct inode *pdir = ndp->ni_pdir;
10847535Sroot 	ino_t ipref;
10857535Sroot 
10867535Sroot 	if ((mode & IFMT) == IFDIR)
108716694Smckusick 		ipref = dirpref(pdir->i_fs);
10887535Sroot 	else
108916694Smckusick 		ipref = pdir->i_number;
109016694Smckusick 	ip = ialloc(pdir, ipref, mode);
10917535Sroot 	if (ip == NULL) {
109216694Smckusick 		iput(pdir);
10937701Ssam 		return (NULL);
10947535Sroot 	}
10957701Ssam #ifdef QUOTA
10967535Sroot 	if (ip->i_dquot != NODQUOT)
10977535Sroot 		panic("maknode: dquot");
10987535Sroot #endif
10997535Sroot 	ip->i_flag |= IACC|IUPD|ICHG;
11007535Sroot 	if ((mode & IFMT) == 0)
11017535Sroot 		mode |= IFREG;
11027535Sroot 	ip->i_mode = mode & ~u.u_cmask;
11037535Sroot 	ip->i_nlink = 1;
11047535Sroot 	ip->i_uid = u.u_uid;
110516694Smckusick 	ip->i_gid = pdir->i_gid;
110611811Ssam 	if (ip->i_mode & ISGID && !groupmember(ip->i_gid))
110711811Ssam 		ip->i_mode &= ~ISGID;
11087701Ssam #ifdef QUOTA
11097535Sroot 	ip->i_dquot = inoquota(ip);
11107535Sroot #endif
11117535Sroot 
11127535Sroot 	/*
11137535Sroot 	 * Make sure inode goes to disk before directory entry.
11147535Sroot 	 */
11158673Sroot 	iupdat(ip, &time, &time, 1);
111616694Smckusick 	u.u_error = direnter(ip, ndp);
11177535Sroot 	if (u.u_error) {
11187535Sroot 		/*
111910850Ssam 		 * Write error occurred trying to update directory
112010850Ssam 		 * so must deallocate the inode.
11217535Sroot 		 */
11227535Sroot 		ip->i_nlink = 0;
11237535Sroot 		ip->i_flag |= ICHG;
11247535Sroot 		iput(ip);
11257701Ssam 		return (NULL);
11267535Sroot 	}
11277701Ssam 	return (ip);
11287535Sroot }
112912756Ssam 
113012756Ssam /*
113112756Ssam  * A virgin directory (no blushing please).
113212756Ssam  */
113312756Ssam struct dirtemplate mastertemplate = {
113412756Ssam 	0, 12, 1, ".",
113512756Ssam 	0, DIRBLKSIZ - 12, 2, ".."
113612756Ssam };
113712756Ssam 
113812756Ssam /*
113912756Ssam  * Mkdir system call
114012756Ssam  */
114112756Ssam mkdir()
114212756Ssam {
114312756Ssam 	struct a {
114412756Ssam 		char	*name;
114512756Ssam 		int	dmode;
114616694Smckusick 	} *uap = (struct a *)u.u_ap;
114712756Ssam 	register struct inode *ip, *dp;
114812756Ssam 	struct dirtemplate dirtemplate;
114916694Smckusick 	register struct nameidata *ndp = &u.u_nd;
115012756Ssam 
115116694Smckusick 	ndp->ni_nameiop = CREATE;
115216694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
115316694Smckusick 	ndp->ni_dirp = uap->name;
115416694Smckusick 	ip = namei(ndp);
115512756Ssam 	if (u.u_error)
115612756Ssam 		return;
115712756Ssam 	if (ip != NULL) {
115812756Ssam 		iput(ip);
115912756Ssam 		u.u_error = EEXIST;
116012756Ssam 		return;
116112756Ssam 	}
116216694Smckusick 	dp = ndp->ni_pdir;
116312756Ssam 	uap->dmode &= 0777;
116412756Ssam 	uap->dmode |= IFDIR;
116512756Ssam 	/*
116612756Ssam 	 * Must simulate part of maknode here
116712756Ssam 	 * in order to acquire the inode, but
116812756Ssam 	 * not have it entered in the parent
116912756Ssam 	 * directory.  The entry is made later
117012756Ssam 	 * after writing "." and ".." entries out.
117112756Ssam 	 */
117212756Ssam 	ip = ialloc(dp, dirpref(dp->i_fs), uap->dmode);
117312756Ssam 	if (ip == NULL) {
117412756Ssam 		iput(dp);
117512756Ssam 		return;
117612756Ssam 	}
117712756Ssam #ifdef QUOTA
117812756Ssam 	if (ip->i_dquot != NODQUOT)
117912756Ssam 		panic("mkdir: dquot");
118012756Ssam #endif
118112756Ssam 	ip->i_flag |= IACC|IUPD|ICHG;
118212756Ssam 	ip->i_mode = uap->dmode & ~u.u_cmask;
118312756Ssam 	ip->i_nlink = 2;
118412756Ssam 	ip->i_uid = u.u_uid;
118512756Ssam 	ip->i_gid = dp->i_gid;
118612756Ssam #ifdef QUOTA
118712756Ssam 	ip->i_dquot = inoquota(ip);
118812756Ssam #endif
118912756Ssam 	iupdat(ip, &time, &time, 1);
119012756Ssam 
119112756Ssam 	/*
119212756Ssam 	 * Bump link count in parent directory
119312756Ssam 	 * to reflect work done below.  Should
119412756Ssam 	 * be done before reference is created
119512756Ssam 	 * so reparation is possible if we crash.
119612756Ssam 	 */
119712756Ssam 	dp->i_nlink++;
119812756Ssam 	dp->i_flag |= ICHG;
119912756Ssam 	iupdat(dp, &time, &time, 1);
120012756Ssam 
120112756Ssam 	/*
120212756Ssam 	 * Initialize directory with "."
120312756Ssam 	 * and ".." from static template.
120412756Ssam 	 */
120512756Ssam 	dirtemplate = mastertemplate;
120612756Ssam 	dirtemplate.dot_ino = ip->i_number;
120712756Ssam 	dirtemplate.dotdot_ino = dp->i_number;
120812756Ssam 	u.u_error = rdwri(UIO_WRITE, ip, (caddr_t)&dirtemplate,
120912756Ssam 		sizeof (dirtemplate), (off_t)0, 1, (int *)0);
121012756Ssam 	if (u.u_error) {
121112756Ssam 		dp->i_nlink--;
121212756Ssam 		dp->i_flag |= ICHG;
121312756Ssam 		goto bad;
121412756Ssam 	}
121518103Smckusick 	if (DIRBLKSIZ > ip->i_fs->fs_fsize)
121618103Smckusick 		panic("mkdir: blksize");     /* XXX - should grow with bmap() */
121718103Smckusick 	else
121818103Smckusick 		ip->i_size = DIRBLKSIZ;
121912756Ssam 	/*
122012756Ssam 	 * Directory all set up, now
122112756Ssam 	 * install the entry for it in
122212756Ssam 	 * the parent directory.
122312756Ssam 	 */
122416694Smckusick 	u.u_error = direnter(ip, ndp);
122512756Ssam 	dp = NULL;
122612756Ssam 	if (u.u_error) {
122716694Smckusick 		ndp->ni_nameiop = LOOKUP | NOCACHE;
122816694Smckusick 		ndp->ni_segflg = UIO_USERSPACE;
122916694Smckusick 		ndp->ni_dirp = uap->name;
123016694Smckusick 		dp = namei(ndp);
123112756Ssam 		if (dp) {
123212756Ssam 			dp->i_nlink--;
123312756Ssam 			dp->i_flag |= ICHG;
123412756Ssam 		}
123512756Ssam 	}
123612756Ssam bad:
123712756Ssam 	/*
123812756Ssam 	 * No need to do an explicit itrunc here,
123912756Ssam 	 * irele will do this for us because we set
124012756Ssam 	 * the link count to 0.
124112756Ssam 	 */
124212756Ssam 	if (u.u_error) {
124312756Ssam 		ip->i_nlink = 0;
124412756Ssam 		ip->i_flag |= ICHG;
124512756Ssam 	}
124612756Ssam 	if (dp)
124712756Ssam 		iput(dp);
124812756Ssam 	iput(ip);
124912756Ssam }
125012756Ssam 
125112756Ssam /*
125212756Ssam  * Rmdir system call.
125312756Ssam  */
125412756Ssam rmdir()
125512756Ssam {
125612756Ssam 	struct a {
125712756Ssam 		char	*name;
125816694Smckusick 	} *uap = (struct a *)u.u_ap;
125912756Ssam 	register struct inode *ip, *dp;
126016694Smckusick 	register struct nameidata *ndp = &u.u_nd;
126112756Ssam 
126216694Smckusick 	ndp->ni_nameiop = DELETE | LOCKPARENT;
126316694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
126416694Smckusick 	ndp->ni_dirp = uap->name;
126516694Smckusick 	ip = namei(ndp);
126612756Ssam 	if (ip == NULL)
126712756Ssam 		return;
126816694Smckusick 	dp = ndp->ni_pdir;
126912756Ssam 	/*
127012756Ssam 	 * No rmdir "." please.
127112756Ssam 	 */
127212756Ssam 	if (dp == ip) {
127312756Ssam 		irele(dp);
127412756Ssam 		iput(ip);
127512756Ssam 		u.u_error = EINVAL;
127612756Ssam 		return;
127712756Ssam 	}
127812756Ssam 	if ((ip->i_mode&IFMT) != IFDIR) {
127912756Ssam 		u.u_error = ENOTDIR;
128012756Ssam 		goto out;
128112756Ssam 	}
128212756Ssam 	/*
128312756Ssam 	 * Don't remove a mounted on directory.
128412756Ssam 	 */
128512756Ssam 	if (ip->i_dev != dp->i_dev) {
128612756Ssam 		u.u_error = EBUSY;
128712756Ssam 		goto out;
128812756Ssam 	}
128912756Ssam 	/*
129012756Ssam 	 * Verify the directory is empty (and valid).
129112756Ssam 	 * (Rmdir ".." won't be valid since
129212756Ssam 	 *  ".." will contain a reference to
129312756Ssam 	 *  the current directory and thus be
129412756Ssam 	 *  non-empty.)
129512756Ssam 	 */
129616776Smckusick 	if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number)) {
129712756Ssam 		u.u_error = ENOTEMPTY;
129812756Ssam 		goto out;
129912756Ssam 	}
130012756Ssam 	/*
130112756Ssam 	 * Delete reference to directory before purging
130212756Ssam 	 * inode.  If we crash in between, the directory
130312756Ssam 	 * will be reattached to lost+found,
130412756Ssam 	 */
130516694Smckusick 	if (dirremove(ndp) == 0)
130612756Ssam 		goto out;
130712756Ssam 	dp->i_nlink--;
130812756Ssam 	dp->i_flag |= ICHG;
130916776Smckusick 	cacheinval(dp);
131012756Ssam 	iput(dp);
131112756Ssam 	dp = NULL;
131212756Ssam 	/*
131312756Ssam 	 * Truncate inode.  The only stuff left
131412756Ssam 	 * in the directory is "." and "..".  The
131512756Ssam 	 * "." reference is inconsequential since
131612756Ssam 	 * we're quashing it.  The ".." reference
131712756Ssam 	 * has already been adjusted above.  We've
131812756Ssam 	 * removed the "." reference and the reference
131912756Ssam 	 * in the parent directory, but there may be
132012756Ssam 	 * other hard links so decrement by 2 and
132112756Ssam 	 * worry about them later.
132212756Ssam 	 */
132312756Ssam 	ip->i_nlink -= 2;
132412756Ssam 	itrunc(ip, (u_long)0);
132516739Smckusick 	cacheinval(ip);
132612756Ssam out:
132712756Ssam 	if (dp)
132812756Ssam 		iput(dp);
132912756Ssam 	iput(ip);
133012756Ssam }
133112756Ssam 
133212756Ssam struct file *
133312756Ssam getinode(fdes)
133412756Ssam 	int fdes;
133512756Ssam {
133616540Ssam 	struct file *fp;
133712756Ssam 
133816540Ssam 	if ((unsigned)fdes >= NOFILE || (fp = u.u_ofile[fdes]) == NULL) {
133916540Ssam 		u.u_error = EBADF;
134016540Ssam 		return ((struct file *)0);
134116540Ssam 	}
134212756Ssam 	if (fp->f_type != DTYPE_INODE) {
134312756Ssam 		u.u_error = EINVAL;
134416540Ssam 		return ((struct file *)0);
134512756Ssam 	}
134612756Ssam 	return (fp);
134712756Ssam }
134812756Ssam 
134912756Ssam /*
135012756Ssam  * mode mask for creation of files
135112756Ssam  */
135212756Ssam umask()
135312756Ssam {
135412756Ssam 	register struct a {
135512756Ssam 		int	mask;
135616694Smckusick 	} *uap = (struct a *)u.u_ap;
135712756Ssam 
135812756Ssam 	u.u_r.r_val1 = u.u_cmask;
135912756Ssam 	u.u_cmask = uap->mask & 07777;
136012756Ssam }
1361