xref: /csrg-svn/sys/kern/vfs_syscalls.c (revision 18416)
1*18416Smckusick /*	vfs_syscalls.c	6.17	85/03/19	*/
237Sbill 
317101Sbloom #include "param.h"
417101Sbloom #include "systm.h"
517101Sbloom #include "dir.h"
617101Sbloom #include "user.h"
717101Sbloom #include "kernel.h"
817101Sbloom #include "file.h"
917101Sbloom #include "stat.h"
1017101Sbloom #include "inode.h"
1117101Sbloom #include "fs.h"
1217101Sbloom #include "buf.h"
1317101Sbloom #include "proc.h"
1417101Sbloom #include "quota.h"
1517101Sbloom #include "uio.h"
1617101Sbloom #include "socket.h"
1717101Sbloom #include "socketvar.h"
1817101Sbloom #include "mount.h"
1937Sbill 
2012756Ssam extern	struct fileops inodeops;
2112756Ssam struct	file *getinode();
2212756Ssam 
239167Ssam /*
249167Ssam  * Change current working directory (``.'').
259167Ssam  */
266254Sroot chdir()
276254Sroot {
286254Sroot 
296254Sroot 	chdirec(&u.u_cdir);
306254Sroot }
316254Sroot 
329167Ssam /*
339167Ssam  * Change notion of root (``/'') directory.
349167Ssam  */
356254Sroot chroot()
366254Sroot {
376254Sroot 
386254Sroot 	if (suser())
396254Sroot 		chdirec(&u.u_rdir);
406254Sroot }
416254Sroot 
429167Ssam /*
439167Ssam  * Common routine for chroot and chdir.
449167Ssam  */
456254Sroot chdirec(ipp)
467701Ssam 	register struct inode **ipp;
476254Sroot {
486254Sroot 	register struct inode *ip;
496254Sroot 	struct a {
506254Sroot 		char	*fname;
5116694Smckusick 	} *uap = (struct a *)u.u_ap;
5216694Smckusick 	register struct nameidata *ndp = &u.u_nd;
536254Sroot 
5416694Smckusick 	ndp->ni_nameiop = LOOKUP | FOLLOW;
5516694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
5616694Smckusick 	ndp->ni_dirp = uap->fname;
5716694Smckusick 	ip = namei(ndp);
589167Ssam 	if (ip == NULL)
596254Sroot 		return;
609167Ssam 	if ((ip->i_mode&IFMT) != IFDIR) {
616254Sroot 		u.u_error = ENOTDIR;
626254Sroot 		goto bad;
636254Sroot 	}
649167Ssam 	if (access(ip, IEXEC))
656254Sroot 		goto bad;
6616664Smckusick 	IUNLOCK(ip);
677142Smckusick 	if (*ipp)
687142Smckusick 		irele(*ipp);
696254Sroot 	*ipp = ip;
706254Sroot 	return;
716254Sroot 
726254Sroot bad:
736254Sroot 	iput(ip);
746254Sroot }
756254Sroot 
7637Sbill /*
776254Sroot  * Open system call.
786254Sroot  */
796254Sroot open()
806254Sroot {
8112756Ssam 	struct a {
826254Sroot 		char	*fname;
837701Ssam 		int	mode;
8412756Ssam 		int	crtmode;
8512756Ssam 	} *uap = (struct a *) u.u_ap;
866254Sroot 
8716694Smckusick 	copen(uap->mode-FOPEN, uap->crtmode, uap->fname);
886254Sroot }
896254Sroot 
906254Sroot /*
916254Sroot  * Creat system call.
926254Sroot  */
9312756Ssam creat()
946254Sroot {
9512756Ssam 	struct a {
966254Sroot 		char	*fname;
976254Sroot 		int	fmode;
9812756Ssam 	} *uap = (struct a *)u.u_ap;
996254Sroot 
10016694Smckusick 	copen(FWRITE|FCREAT|FTRUNC, uap->fmode, uap->fname);
1016254Sroot }
1026254Sroot 
1036254Sroot /*
1046254Sroot  * Common code for open and creat.
10512756Ssam  * Check permissions, allocate an open file structure,
10612756Ssam  * and call the device open routine if any.
1076254Sroot  */
10816694Smckusick copen(mode, arg, fname)
10912756Ssam 	register int mode;
11012756Ssam 	int arg;
11116694Smckusick 	caddr_t fname;
11212756Ssam {
1136254Sroot 	register struct inode *ip;
1146254Sroot 	register struct file *fp;
11516694Smckusick 	register struct nameidata *ndp = &u.u_nd;
11612756Ssam 	int i;
1176254Sroot 
11812756Ssam #ifdef notdef
11912756Ssam 	if ((mode&(FREAD|FWRITE)) == 0) {
12012756Ssam 		u.u_error = EINVAL;
12112756Ssam 		return;
12212756Ssam 	}
12312756Ssam #endif
12416694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
12516694Smckusick 	ndp->ni_dirp = fname;
12612756Ssam 	if (mode&FCREAT) {
127*18416Smckusick 		if (mode & FEXCL)
128*18416Smckusick 			ndp->ni_nameiop = CREATE;
129*18416Smckusick 		else
130*18416Smckusick 			ndp->ni_nameiop = CREATE | FOLLOW;
13116694Smckusick 		ip = namei(ndp);
13212756Ssam 		if (ip == NULL) {
13312756Ssam 			if (u.u_error)
13412756Ssam 				return;
13516694Smckusick 			ip = maknode(arg&07777&(~ISVTX), ndp);
13612756Ssam 			if (ip == NULL)
13712756Ssam 				return;
13812756Ssam 			mode &= ~FTRUNC;
13912756Ssam 		} else {
14012756Ssam 			if (mode&FEXCL) {
14112756Ssam 				u.u_error = EEXIST;
14212756Ssam 				iput(ip);
14312756Ssam 				return;
14412756Ssam 			}
14512756Ssam 			mode &= ~FCREAT;
14612756Ssam 		}
14712756Ssam 	} else {
14816694Smckusick 		ndp->ni_nameiop = LOOKUP | FOLLOW;
14916694Smckusick 		ip = namei(ndp);
15012756Ssam 		if (ip == NULL)
15112756Ssam 			return;
15212756Ssam 	}
15312756Ssam 	if ((ip->i_mode & IFMT) == IFSOCK) {
15412756Ssam 		u.u_error = EOPNOTSUPP;
15512756Ssam 		goto bad;
15612756Ssam 	}
15712756Ssam 	if ((mode&FCREAT) == 0) {
1586254Sroot 		if (mode&FREAD)
1597701Ssam 			if (access(ip, IREAD))
1607701Ssam 				goto bad;
16116032Skarels 		if (mode&(FWRITE|FTRUNC)) {
1627701Ssam 			if (access(ip, IWRITE))
1637701Ssam 				goto bad;
1647701Ssam 			if ((ip->i_mode&IFMT) == IFDIR) {
1656254Sroot 				u.u_error = EISDIR;
1667701Ssam 				goto bad;
1677701Ssam 			}
1686254Sroot 		}
1696254Sroot 	}
17012756Ssam 	fp = falloc();
17112756Ssam 	if (fp == NULL)
17212756Ssam 		goto bad;
17312756Ssam 	if (mode&FTRUNC)
1749167Ssam 		itrunc(ip, (u_long)0);
17516664Smckusick 	IUNLOCK(ip);
17612756Ssam 	fp->f_flag = mode&FMASK;
17712756Ssam 	fp->f_type = DTYPE_INODE;
17812756Ssam 	fp->f_ops = &inodeops;
17912756Ssam 	fp->f_data = (caddr_t)ip;
1806254Sroot 	i = u.u_r.r_val1;
18112756Ssam 	if (setjmp(&u.u_qsave)) {
18212756Ssam 		if (u.u_error == 0)
18312756Ssam 			u.u_error = EINTR;
18412756Ssam 		u.u_ofile[i] = NULL;
18512756Ssam 		closef(fp);
18612756Ssam 		return;
18712756Ssam 	}
1888559Sroot 	u.u_error = openi(ip, mode);
18912756Ssam 	if (u.u_error == 0)
1906254Sroot 		return;
1916254Sroot 	u.u_ofile[i] = NULL;
1926254Sroot 	fp->f_count--;
1937142Smckusick 	irele(ip);
1947701Ssam 	return;
1957701Ssam bad:
1967701Ssam 	iput(ip);
1976254Sroot }
1986254Sroot 
1996254Sroot /*
2006254Sroot  * Mknod system call
2016254Sroot  */
2026254Sroot mknod()
2036254Sroot {
2046254Sroot 	register struct inode *ip;
2056254Sroot 	register struct a {
2066254Sroot 		char	*fname;
2076254Sroot 		int	fmode;
2086254Sroot 		int	dev;
20916694Smckusick 	} *uap = (struct a *)u.u_ap;
21016694Smckusick 	register struct nameidata *ndp = &u.u_nd;
2116254Sroot 
21212756Ssam 	if (!suser())
21312756Ssam 		return;
21416694Smckusick 	ndp->ni_nameiop = CREATE;
21516694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
21616694Smckusick 	ndp->ni_dirp = uap->fname;
21716694Smckusick 	ip = namei(ndp);
21812756Ssam 	if (ip != NULL) {
21912756Ssam 		u.u_error = EEXIST;
22012756Ssam 		goto out;
2216254Sroot 	}
2226254Sroot 	if (u.u_error)
2236254Sroot 		return;
22416694Smckusick 	ip = maknode(uap->fmode, ndp);
2256254Sroot 	if (ip == NULL)
2266254Sroot 		return;
22712756Ssam 	switch (ip->i_mode & IFMT) {
22812756Ssam 
22915093Smckusick 	case IFMT:	/* used by badsect to flag bad sectors */
23012756Ssam 	case IFCHR:
23112756Ssam 	case IFBLK:
23212756Ssam 		if (uap->dev) {
23312756Ssam 			/*
23412756Ssam 			 * Want to be able to use this to make badblock
23512756Ssam 			 * inodes, so don't truncate the dev number.
23612756Ssam 			 */
23712756Ssam 			ip->i_rdev = uap->dev;
23812756Ssam 			ip->i_flag |= IACC|IUPD|ICHG;
23912756Ssam 		}
2406254Sroot 	}
2416254Sroot 
2426254Sroot out:
2436254Sroot 	iput(ip);
2446254Sroot }
2456254Sroot 
2466254Sroot /*
2476254Sroot  * link system call
2486254Sroot  */
2496254Sroot link()
2506254Sroot {
2516254Sroot 	register struct inode *ip, *xp;
2526254Sroot 	register struct a {
2536254Sroot 		char	*target;
2546254Sroot 		char	*linkname;
25516694Smckusick 	} *uap = (struct a *)u.u_ap;
25616694Smckusick 	register struct nameidata *ndp = &u.u_nd;
2576254Sroot 
25816694Smckusick 	ndp->ni_nameiop = LOOKUP | FOLLOW;
25916694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
26016694Smckusick 	ndp->ni_dirp = uap->target;
26116694Smckusick 	ip = namei(ndp);	/* well, this routine is doomed anyhow */
2626254Sroot 	if (ip == NULL)
2636254Sroot 		return;
2649167Ssam 	if ((ip->i_mode&IFMT) == IFDIR && !suser()) {
2657439Sroot 		iput(ip);
2667439Sroot 		return;
2677439Sroot 	}
2686254Sroot 	ip->i_nlink++;
2696254Sroot 	ip->i_flag |= ICHG;
2708673Sroot 	iupdat(ip, &time, &time, 1);
27116664Smckusick 	IUNLOCK(ip);
27216694Smckusick 	ndp->ni_nameiop = CREATE;
27316694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
27416694Smckusick 	ndp->ni_dirp = (caddr_t)uap->linkname;
27516694Smckusick 	xp = namei(ndp);
2766254Sroot 	if (xp != NULL) {
2776254Sroot 		u.u_error = EEXIST;
2786254Sroot 		iput(xp);
2796254Sroot 		goto out;
2806254Sroot 	}
2816254Sroot 	if (u.u_error)
2826254Sroot 		goto out;
28316694Smckusick 	if (ndp->ni_pdir->i_dev != ip->i_dev) {
28416694Smckusick 		iput(ndp->ni_pdir);
2856254Sroot 		u.u_error = EXDEV;
2866254Sroot 		goto out;
2876254Sroot 	}
28816694Smckusick 	u.u_error = direnter(ip, ndp);
2896254Sroot out:
2906254Sroot 	if (u.u_error) {
2916254Sroot 		ip->i_nlink--;
2926254Sroot 		ip->i_flag |= ICHG;
2936254Sroot 	}
2947142Smckusick 	irele(ip);
2956254Sroot }
2966254Sroot 
2976254Sroot /*
2986254Sroot  * symlink -- make a symbolic link
2996254Sroot  */
3006254Sroot symlink()
3016254Sroot {
3026254Sroot 	register struct a {
3036254Sroot 		char	*target;
3046254Sroot 		char	*linkname;
30516694Smckusick 	} *uap = (struct a *)u.u_ap;
3066254Sroot 	register struct inode *ip;
3076254Sroot 	register char *tp;
3086254Sroot 	register c, nc;
30916694Smckusick 	register struct nameidata *ndp = &u.u_nd;
3106254Sroot 
3116254Sroot 	tp = uap->target;
3126254Sroot 	nc = 0;
3136254Sroot 	while (c = fubyte(tp)) {
3146254Sroot 		if (c < 0) {
3156254Sroot 			u.u_error = EFAULT;
3166254Sroot 			return;
3176254Sroot 		}
3186254Sroot 		tp++;
3196254Sroot 		nc++;
3206254Sroot 	}
32116694Smckusick 	ndp->ni_nameiop = CREATE;
32216694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
32316694Smckusick 	ndp->ni_dirp = uap->linkname;
32416694Smckusick 	ip = namei(ndp);
3256254Sroot 	if (ip) {
3266254Sroot 		iput(ip);
3276254Sroot 		u.u_error = EEXIST;
3286254Sroot 		return;
3296254Sroot 	}
3306254Sroot 	if (u.u_error)
3316254Sroot 		return;
33216694Smckusick 	ip = maknode(IFLNK | 0777, ndp);
3336254Sroot 	if (ip == NULL)
3346254Sroot 		return;
3357826Sroot 	u.u_error = rdwri(UIO_WRITE, ip, uap->target, nc, 0, 0, (int *)0);
3369167Ssam 	/* handle u.u_error != 0 */
3376254Sroot 	iput(ip);
3386254Sroot }
3396254Sroot 
3406254Sroot /*
3416254Sroot  * Unlink system call.
3426254Sroot  * Hard to avoid races here, especially
3436254Sroot  * in unlinking directories.
3446254Sroot  */
3456254Sroot unlink()
3466254Sroot {
3476254Sroot 	struct a {
3486254Sroot 		char	*fname;
34916694Smckusick 	} *uap = (struct a *)u.u_ap;
3509167Ssam 	register struct inode *ip, *dp;
35116694Smckusick 	register struct nameidata *ndp = &u.u_nd;
3526254Sroot 
35316694Smckusick 	ndp->ni_nameiop = DELETE | LOCKPARENT;
35416694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
35516694Smckusick 	ndp->ni_dirp = uap->fname;
35616694Smckusick 	ip = namei(ndp);
3579167Ssam 	if (ip == NULL)
3586254Sroot 		return;
35916694Smckusick 	dp = ndp->ni_pdir;
3609167Ssam 	if ((ip->i_mode&IFMT) == IFDIR && !suser())
3616254Sroot 		goto out;
3626254Sroot 	/*
3636254Sroot 	 * Don't unlink a mounted file.
3646254Sroot 	 */
3659167Ssam 	if (ip->i_dev != dp->i_dev) {
3666254Sroot 		u.u_error = EBUSY;
3676254Sroot 		goto out;
3686254Sroot 	}
3696254Sroot 	if (ip->i_flag&ITEXT)
3706254Sroot 		xrele(ip);	/* try once to free text */
37116694Smckusick 	if (dirremove(ndp)) {
3727535Sroot 		ip->i_nlink--;
3737535Sroot 		ip->i_flag |= ICHG;
3746254Sroot 	}
3756254Sroot out:
3769167Ssam 	if (dp == ip)
3777142Smckusick 		irele(ip);
3787142Smckusick 	else
3797142Smckusick 		iput(ip);
3809167Ssam 	iput(dp);
3816254Sroot }
3826254Sroot 
3836254Sroot /*
3846254Sroot  * Seek system call
3856254Sroot  */
3868040Sroot lseek()
3876254Sroot {
3886254Sroot 	register struct file *fp;
3896254Sroot 	register struct a {
3907701Ssam 		int	fd;
3916254Sroot 		off_t	off;
3926254Sroot 		int	sbase;
39316694Smckusick 	} *uap = (struct a *)u.u_ap;
3946254Sroot 
39516540Ssam 	GETF(fp, uap->fd);
39616540Ssam 	if (fp->f_type != DTYPE_INODE) {
39716540Ssam 		u.u_error = ESPIPE;
3986254Sroot 		return;
39916540Ssam 	}
40013878Ssam 	switch (uap->sbase) {
40113878Ssam 
40213878Ssam 	case L_INCR:
40313878Ssam 		fp->f_offset += uap->off;
40413878Ssam 		break;
40513878Ssam 
40613878Ssam 	case L_XTND:
40713878Ssam 		fp->f_offset = uap->off + ((struct inode *)fp->f_data)->i_size;
40813878Ssam 		break;
40913878Ssam 
41013878Ssam 	case L_SET:
41113878Ssam 		fp->f_offset = uap->off;
41213878Ssam 		break;
41313878Ssam 
41413878Ssam 	default:
41513878Ssam 		u.u_error = EINVAL;
41613878Ssam 		return;
41713878Ssam 	}
41813878Ssam 	u.u_r.r_off = fp->f_offset;
4196254Sroot }
4206254Sroot 
4216254Sroot /*
4226254Sroot  * Access system call
4236254Sroot  */
4246254Sroot saccess()
4256254Sroot {
4266254Sroot 	register svuid, svgid;
4276254Sroot 	register struct inode *ip;
4286254Sroot 	register struct a {
4296254Sroot 		char	*fname;
4306254Sroot 		int	fmode;
43116694Smckusick 	} *uap = (struct a *)u.u_ap;
43216694Smckusick 	register struct nameidata *ndp = &u.u_nd;
4336254Sroot 
4346254Sroot 	svuid = u.u_uid;
4356254Sroot 	svgid = u.u_gid;
4366254Sroot 	u.u_uid = u.u_ruid;
4376254Sroot 	u.u_gid = u.u_rgid;
43816694Smckusick 	ndp->ni_nameiop = LOOKUP | FOLLOW;
43916694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
44016694Smckusick 	ndp->ni_dirp = uap->fname;
44116694Smckusick 	ip = namei(ndp);
4426254Sroot 	if (ip != NULL) {
44312756Ssam 		if ((uap->fmode&R_OK) && access(ip, IREAD))
4447701Ssam 			goto done;
44512756Ssam 		if ((uap->fmode&W_OK) && access(ip, IWRITE))
4467701Ssam 			goto done;
44712756Ssam 		if ((uap->fmode&X_OK) && access(ip, IEXEC))
4487701Ssam 			goto done;
4497701Ssam done:
4506254Sroot 		iput(ip);
4516254Sroot 	}
4526254Sroot 	u.u_uid = svuid;
4536254Sroot 	u.u_gid = svgid;
4546254Sroot }
4556254Sroot 
4566254Sroot /*
4576574Smckusic  * Stat system call.  This version follows links.
45837Sbill  */
45937Sbill stat()
46037Sbill {
46137Sbill 
46216694Smckusick 	stat1(FOLLOW);
46337Sbill }
46437Sbill 
46537Sbill /*
4666574Smckusic  * Lstat system call.  This version does not follow links.
4675992Swnj  */
4685992Swnj lstat()
4695992Swnj {
47012756Ssam 
47116694Smckusick 	stat1(NOFOLLOW);
47212756Ssam }
47312756Ssam 
47412756Ssam stat1(follow)
47512756Ssam 	int follow;
47612756Ssam {
4775992Swnj 	register struct inode *ip;
4785992Swnj 	register struct a {
4795992Swnj 		char	*fname;
48012756Ssam 		struct stat *ub;
48116694Smckusick 	} *uap = (struct a *)u.u_ap;
48212756Ssam 	struct stat sb;
48316694Smckusick 	register struct nameidata *ndp = &u.u_nd;
4845992Swnj 
48516694Smckusick 	ndp->ni_nameiop = LOOKUP | follow;
48616694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
48716694Smckusick 	ndp->ni_dirp = uap->fname;
48816694Smckusick 	ip = namei(ndp);
4895992Swnj 	if (ip == NULL)
4905992Swnj 		return;
49113043Ssam 	(void) ino_stat(ip, &sb);
4925992Swnj 	iput(ip);
49312756Ssam 	u.u_error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb));
4945992Swnj }
4955992Swnj 
4965992Swnj /*
4975992Swnj  * Return target name of a symbolic link
49837Sbill  */
4995992Swnj readlink()
5005992Swnj {
5015992Swnj 	register struct inode *ip;
5025992Swnj 	register struct a {
5035992Swnj 		char	*name;
5045992Swnj 		char	*buf;
5055992Swnj 		int	count;
5067826Sroot 	} *uap = (struct a *)u.u_ap;
50716694Smckusick 	register struct nameidata *ndp = &u.u_nd;
5087826Sroot 	int resid;
5095992Swnj 
51016694Smckusick 	ndp->ni_nameiop = LOOKUP;
51116694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
51216694Smckusick 	ndp->ni_dirp = uap->name;
51316694Smckusick 	ip = namei(ndp);
5145992Swnj 	if (ip == NULL)
5155992Swnj 		return;
5165992Swnj 	if ((ip->i_mode&IFMT) != IFLNK) {
5175992Swnj 		u.u_error = ENXIO;
5185992Swnj 		goto out;
5195992Swnj 	}
5207826Sroot 	u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, 0, 0, &resid);
5215992Swnj out:
5225992Swnj 	iput(ip);
5237826Sroot 	u.u_r.r_val1 = uap->count - resid;
5245992Swnj }
5255992Swnj 
5269167Ssam /*
5279167Ssam  * Change mode of a file given path name.
5289167Ssam  */
5296254Sroot chmod()
5305992Swnj {
5317701Ssam 	struct inode *ip;
5327701Ssam 	struct a {
5336254Sroot 		char	*fname;
5346254Sroot 		int	fmode;
53516694Smckusick 	} *uap = (struct a *)u.u_ap;
5365992Swnj 
53716694Smckusick 	if ((ip = owner(uap->fname, FOLLOW)) == NULL)
5385992Swnj 		return;
5397701Ssam 	chmod1(ip, uap->fmode);
5409167Ssam 	iput(ip);
5417701Ssam }
5427439Sroot 
5439167Ssam /*
5449167Ssam  * Change mode of a file given a file descriptor.
5459167Ssam  */
5467701Ssam fchmod()
5477701Ssam {
5487701Ssam 	struct a {
5497701Ssam 		int	fd;
5507701Ssam 		int	fmode;
55116694Smckusick 	} *uap = (struct a *)u.u_ap;
5527701Ssam 	register struct inode *ip;
5537701Ssam 	register struct file *fp;
5547701Ssam 
55512756Ssam 	fp = getinode(uap->fd);
5567701Ssam 	if (fp == NULL)
5577701Ssam 		return;
55812756Ssam 	ip = (struct inode *)fp->f_data;
5599167Ssam 	if (u.u_uid != ip->i_uid && !suser())
5609167Ssam 		return;
56116664Smckusick 	ILOCK(ip);
5627701Ssam 	chmod1(ip, uap->fmode);
56316664Smckusick 	IUNLOCK(ip);
5647701Ssam }
5657701Ssam 
5669167Ssam /*
5679167Ssam  * Change the mode on a file.
5689167Ssam  * Inode must be locked before calling.
5699167Ssam  */
5707701Ssam chmod1(ip, mode)
5717701Ssam 	register struct inode *ip;
5727701Ssam 	register int mode;
5737701Ssam {
5747868Sroot 
5756254Sroot 	ip->i_mode &= ~07777;
5767439Sroot 	if (u.u_uid) {
5777701Ssam 		mode &= ~ISVTX;
57811811Ssam 		if (!groupmember(ip->i_gid))
57911811Ssam 			mode &= ~ISGID;
5807439Sroot 	}
5817701Ssam 	ip->i_mode |= mode&07777;
5826254Sroot 	ip->i_flag |= ICHG;
5836254Sroot 	if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0)
5846254Sroot 		xrele(ip);
5855992Swnj }
5865992Swnj 
5879167Ssam /*
5889167Ssam  * Set ownership given a path name.
5899167Ssam  */
5906254Sroot chown()
59137Sbill {
5927701Ssam 	struct inode *ip;
5937701Ssam 	struct a {
5946254Sroot 		char	*fname;
5956254Sroot 		int	uid;
5966254Sroot 		int	gid;
59716694Smckusick 	} *uap = (struct a *)u.u_ap;
59837Sbill 
59916694Smckusick 	if (!suser() || (ip = owner(uap->fname, NOFOLLOW)) == NULL)
60037Sbill 		return;
60111811Ssam 	u.u_error = chown1(ip, uap->uid, uap->gid);
6029167Ssam 	iput(ip);
6037701Ssam }
6047439Sroot 
6059167Ssam /*
6069167Ssam  * Set ownership given a file descriptor.
6079167Ssam  */
6087701Ssam fchown()
6097701Ssam {
6107701Ssam 	struct a {
6117701Ssam 		int	fd;
6127701Ssam 		int	uid;
6137701Ssam 		int	gid;
61416694Smckusick 	} *uap = (struct a *)u.u_ap;
6157701Ssam 	register struct inode *ip;
6167701Ssam 	register struct file *fp;
6177701Ssam 
61812756Ssam 	fp = getinode(uap->fd);
6197701Ssam 	if (fp == NULL)
6207701Ssam 		return;
62112756Ssam 	ip = (struct inode *)fp->f_data;
62211821Ssam 	if (!suser())
6239167Ssam 		return;
62416664Smckusick 	ILOCK(ip);
62511811Ssam 	u.u_error = chown1(ip, uap->uid, uap->gid);
62616664Smckusick 	IUNLOCK(ip);
6277701Ssam }
6287701Ssam 
6297701Ssam /*
6307701Ssam  * Perform chown operation on inode ip;
6317701Ssam  * inode must be locked prior to call.
6327701Ssam  */
6337701Ssam chown1(ip, uid, gid)
6347701Ssam 	register struct inode *ip;
6357701Ssam 	int uid, gid;
6367701Ssam {
6377701Ssam #ifdef QUOTA
6387701Ssam 	register long change;
63911811Ssam #endif
6407701Ssam 
64111811Ssam 	if (uid == -1)
64211811Ssam 		uid = ip->i_uid;
64311811Ssam 	if (gid == -1)
64411811Ssam 		gid = ip->i_gid;
64511811Ssam #ifdef QUOTA
64614385Ssam 	if (ip->i_uid == uid)		/* this just speeds things a little */
6477482Skre 		change = 0;
64812646Ssam 	else
64912646Ssam 		change = ip->i_blocks;
65012646Ssam 	(void) chkdq(ip, -change, 1);
65112646Ssam 	(void) chkiq(ip->i_dev, ip, ip->i_uid, 1);
6527482Skre 	dqrele(ip->i_dquot);
6537482Skre #endif
65411811Ssam 	ip->i_uid = uid;
65511811Ssam 	ip->i_gid = gid;
6566254Sroot 	ip->i_flag |= ICHG;
6576254Sroot 	if (u.u_ruid != 0)
6586254Sroot 		ip->i_mode &= ~(ISUID|ISGID);
6597701Ssam #ifdef QUOTA
6607482Skre 	ip->i_dquot = inoquota(ip);
66112646Ssam 	(void) chkdq(ip, change, 1);
66212646Ssam 	(void) chkiq(ip->i_dev, (struct inode *)NULL, uid, 1);
66312646Ssam 	return (u.u_error);		/* should == 0 ALWAYS !! */
66412646Ssam #else
66512646Ssam 	return (0);
6667482Skre #endif
66737Sbill }
66837Sbill 
66911811Ssam utimes()
67011811Ssam {
67111811Ssam 	register struct a {
67211811Ssam 		char	*fname;
67311811Ssam 		struct	timeval *tptr;
67411811Ssam 	} *uap = (struct a *)u.u_ap;
67511811Ssam 	register struct inode *ip;
67611811Ssam 	struct timeval tv[2];
67711811Ssam 
67816694Smckusick 	if ((ip = owner(uap->fname, FOLLOW)) == NULL)
67911811Ssam 		return;
68011811Ssam 	u.u_error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv));
68111811Ssam 	if (u.u_error == 0) {
68211811Ssam 		ip->i_flag |= IACC|IUPD|ICHG;
68311811Ssam 		iupdat(ip, &tv[0], &tv[1], 0);
68411811Ssam 	}
68511811Ssam 	iput(ip);
68611811Ssam }
68711811Ssam 
6889167Ssam /*
6899167Ssam  * Flush any pending I/O.
6909167Ssam  */
6916254Sroot sync()
69237Sbill {
69337Sbill 
6948673Sroot 	update();
69537Sbill }
6967535Sroot 
6979167Ssam /*
6989167Ssam  * Truncate a file given its path name.
6999167Ssam  */
7007701Ssam truncate()
7017701Ssam {
7027701Ssam 	struct a {
7037701Ssam 		char	*fname;
7049167Ssam 		u_long	length;
7057826Sroot 	} *uap = (struct a *)u.u_ap;
7067701Ssam 	struct inode *ip;
70716694Smckusick 	register struct nameidata *ndp = &u.u_nd;
7087701Ssam 
70916694Smckusick 	ndp->ni_nameiop = LOOKUP | FOLLOW;
71016694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
71116694Smckusick 	ndp->ni_dirp = uap->fname;
71216694Smckusick 	ip = namei(ndp);
7137701Ssam 	if (ip == NULL)
7147701Ssam 		return;
7157701Ssam 	if (access(ip, IWRITE))
7167701Ssam 		goto bad;
7177701Ssam 	if ((ip->i_mode&IFMT) == IFDIR) {
7187701Ssam 		u.u_error = EISDIR;
7197701Ssam 		goto bad;
7207701Ssam 	}
7217701Ssam 	itrunc(ip, uap->length);
7227701Ssam bad:
7237701Ssam 	iput(ip);
7247701Ssam }
7257701Ssam 
7269167Ssam /*
7279167Ssam  * Truncate a file given a file descriptor.
7289167Ssam  */
7297701Ssam ftruncate()
7307701Ssam {
7317701Ssam 	struct a {
7327701Ssam 		int	fd;
7339167Ssam 		u_long	length;
7347826Sroot 	} *uap = (struct a *)u.u_ap;
7357701Ssam 	struct inode *ip;
7367701Ssam 	struct file *fp;
7377701Ssam 
73812756Ssam 	fp = getinode(uap->fd);
7397701Ssam 	if (fp == NULL)
7407701Ssam 		return;
7417701Ssam 	if ((fp->f_flag&FWRITE) == 0) {
7427701Ssam 		u.u_error = EINVAL;
7437701Ssam 		return;
7447701Ssam 	}
74512756Ssam 	ip = (struct inode *)fp->f_data;
74616664Smckusick 	ILOCK(ip);
7477701Ssam 	itrunc(ip, uap->length);
74816664Smckusick 	IUNLOCK(ip);
7497701Ssam }
7507701Ssam 
7519167Ssam /*
7529167Ssam  * Synch an open file.
7539167Ssam  */
7549167Ssam fsync()
7559167Ssam {
7569167Ssam 	struct a {
7579167Ssam 		int	fd;
7589167Ssam 	} *uap = (struct a *)u.u_ap;
7599167Ssam 	struct inode *ip;
7609167Ssam 	struct file *fp;
7619167Ssam 
76212756Ssam 	fp = getinode(uap->fd);
7639167Ssam 	if (fp == NULL)
7649167Ssam 		return;
76512756Ssam 	ip = (struct inode *)fp->f_data;
76616664Smckusick 	ILOCK(ip);
7679167Ssam 	syncip(ip);
76816664Smckusick 	IUNLOCK(ip);
7699167Ssam }
7709167Ssam 
7719167Ssam /*
7729167Ssam  * Rename system call.
7739167Ssam  * 	rename("foo", "bar");
7749167Ssam  * is essentially
7759167Ssam  *	unlink("bar");
7769167Ssam  *	link("foo", "bar");
7779167Ssam  *	unlink("foo");
7789167Ssam  * but ``atomically''.  Can't do full commit without saving state in the
7799167Ssam  * inode on disk which isn't feasible at this time.  Best we can do is
7809167Ssam  * always guarantee the target exists.
7819167Ssam  *
7829167Ssam  * Basic algorithm is:
7839167Ssam  *
7849167Ssam  * 1) Bump link count on source while we're linking it to the
7859167Ssam  *    target.  This also insure the inode won't be deleted out
78616776Smckusick  *    from underneath us while we work (it may be truncated by
78716776Smckusick  *    a concurrent `trunc' or `open' for creation).
7889167Ssam  * 2) Link source to destination.  If destination already exists,
7899167Ssam  *    delete it first.
79016776Smckusick  * 3) Unlink source reference to inode if still around. If a
79116776Smckusick  *    directory was moved and the parent of the destination
7929167Ssam  *    is different from the source, patch the ".." entry in the
7939167Ssam  *    directory.
7949167Ssam  *
7959167Ssam  * Source and destination must either both be directories, or both
7969167Ssam  * not be directories.  If target is a directory, it must be empty.
7979167Ssam  */
7987701Ssam rename()
7997701Ssam {
8007701Ssam 	struct a {
8017701Ssam 		char	*from;
8027701Ssam 		char	*to;
80316694Smckusick 	} *uap = (struct a *)u.u_ap;
8049167Ssam 	register struct inode *ip, *xp, *dp;
80516776Smckusick 	struct dirtemplate dirbuf;
80616776Smckusick 	int doingdirectory = 0, oldparent = 0, newparent = 0;
80716694Smckusick 	register struct nameidata *ndp = &u.u_nd;
80810051Ssam 	int error = 0;
8097701Ssam 
81016694Smckusick 	ndp->ni_nameiop = DELETE | LOCKPARENT;
81116694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
81216694Smckusick 	ndp->ni_dirp = uap->from;
81316694Smckusick 	ip = namei(ndp);
8149167Ssam 	if (ip == NULL)
8159167Ssam 		return;
81616694Smckusick 	dp = ndp->ni_pdir;
8179167Ssam 	if ((ip->i_mode&IFMT) == IFDIR) {
8189167Ssam 		register struct direct *d;
8199167Ssam 
82016694Smckusick 		d = &ndp->ni_dent;
8219167Ssam 		/*
82211641Ssam 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
8239167Ssam 		 */
82411641Ssam 		if ((d->d_namlen == 1 && d->d_name[0] == '.') ||
82511641Ssam 		    (d->d_namlen == 2 && bcmp(d->d_name, "..", 2) == 0) ||
82616776Smckusick 		    (dp == ip) || (ip->i_flag & IRENAME)) {
82711641Ssam 			iput(dp);
82811641Ssam 			if (dp == ip)
82911641Ssam 				irele(ip);
83011641Ssam 			else
83110051Ssam 				iput(ip);
83211641Ssam 			u.u_error = EINVAL;
83311641Ssam 			return;
8349167Ssam 		}
83516776Smckusick 		ip->i_flag |= IRENAME;
8369167Ssam 		oldparent = dp->i_number;
8379167Ssam 		doingdirectory++;
8389167Ssam 	}
83911641Ssam 	iput(dp);
8409167Ssam 
8419167Ssam 	/*
8429167Ssam 	 * 1) Bump link count while we're moving stuff
8439167Ssam 	 *    around.  If we crash somewhere before
8449167Ssam 	 *    completing our work, the link count
8459167Ssam 	 *    may be wrong, but correctable.
8469167Ssam 	 */
8479167Ssam 	ip->i_nlink++;
8489167Ssam 	ip->i_flag |= ICHG;
8499167Ssam 	iupdat(ip, &time, &time, 1);
85016664Smckusick 	IUNLOCK(ip);
8519167Ssam 
8529167Ssam 	/*
8539167Ssam 	 * When the target exists, both the directory
8549167Ssam 	 * and target inodes are returned locked.
8559167Ssam 	 */
85616694Smckusick 	ndp->ni_nameiop = CREATE | LOCKPARENT | NOCACHE;
85716694Smckusick 	ndp->ni_dirp = (caddr_t)uap->to;
85816694Smckusick 	xp = namei(ndp);
85910051Ssam 	if (u.u_error) {
86010051Ssam 		error = u.u_error;
8619167Ssam 		goto out;
86210051Ssam 	}
86316694Smckusick 	dp = ndp->ni_pdir;
8649167Ssam 	/*
86511641Ssam 	 * If ".." must be changed (ie the directory gets a new
86612816Smckusick 	 * parent) then the source directory must not be in the
86712816Smckusick 	 * directory heirarchy above the target, as this would
86812816Smckusick 	 * orphan everything below the source directory. Also
86912816Smckusick 	 * the user must have write permission in the source so
87012816Smckusick 	 * as to be able to change "..". We must repeat the call
87112816Smckusick 	 * to namei, as the parent directory is unlocked by the
87212816Smckusick 	 * call to checkpath().
87311641Ssam 	 */
87416776Smckusick 	if (oldparent != dp->i_number)
87516776Smckusick 		newparent = dp->i_number;
87616776Smckusick 	if (doingdirectory && newparent) {
87712816Smckusick 		if (access(ip, IWRITE))
87812816Smckusick 			goto bad;
87912816Smckusick 		do {
88016694Smckusick 			dp = ndp->ni_pdir;
88112816Smckusick 			if (xp != NULL)
88212816Smckusick 				iput(xp);
88312816Smckusick 			u.u_error = checkpath(ip, dp);
88412816Smckusick 			if (u.u_error)
88512816Smckusick 				goto out;
88616694Smckusick 			xp = namei(ndp);
88712816Smckusick 			if (u.u_error) {
88812816Smckusick 				error = u.u_error;
88912816Smckusick 				goto out;
89012816Smckusick 			}
89116694Smckusick 		} while (dp != ndp->ni_pdir);
89212816Smckusick 	}
89311641Ssam 	/*
8949167Ssam 	 * 2) If target doesn't exist, link the target
8959167Ssam 	 *    to the source and unlink the source.
8969167Ssam 	 *    Otherwise, rewrite the target directory
8979167Ssam 	 *    entry to reference the source inode and
8989167Ssam 	 *    expunge the original entry's existence.
8999167Ssam 	 */
9009167Ssam 	if (xp == NULL) {
9019167Ssam 		if (dp->i_dev != ip->i_dev) {
90210051Ssam 			error = EXDEV;
9039167Ssam 			goto bad;
9049167Ssam 		}
9059167Ssam 		/*
90616776Smckusick 		 * Account for ".." in new directory.
90716776Smckusick 		 * When source and destination have the same
90816776Smckusick 		 * parent we don't fool with the link count.
9099167Ssam 		 */
91016776Smckusick 		if (doingdirectory && newparent) {
9119167Ssam 			dp->i_nlink++;
9129167Ssam 			dp->i_flag |= ICHG;
9139167Ssam 			iupdat(dp, &time, &time, 1);
9149167Ssam 		}
91516694Smckusick 		error = direnter(ip, ndp);
91610850Ssam 		if (error)
9179167Ssam 			goto out;
9189167Ssam 	} else {
9199167Ssam 		if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) {
92010051Ssam 			error = EXDEV;
9219167Ssam 			goto bad;
9229167Ssam 		}
9239167Ssam 		/*
92410590Ssam 		 * Short circuit rename(foo, foo).
92510590Ssam 		 */
92610590Ssam 		if (xp->i_number == ip->i_number)
92710590Ssam 			goto bad;
92810590Ssam 		/*
92910051Ssam 		 * Target must be empty if a directory
93010051Ssam 		 * and have no links to it.
9319167Ssam 		 * Also, insure source and target are
9329167Ssam 		 * compatible (both directories, or both
9339167Ssam 		 * not directories).
9349167Ssam 		 */
9359167Ssam 		if ((xp->i_mode&IFMT) == IFDIR) {
93616776Smckusick 			if (!dirempty(xp, dp->i_number) || xp->i_nlink > 2) {
93710051Ssam 				error = ENOTEMPTY;
9389167Ssam 				goto bad;
9399167Ssam 			}
9409167Ssam 			if (!doingdirectory) {
94110051Ssam 				error = ENOTDIR;
9429167Ssam 				goto bad;
9439167Ssam 			}
94416776Smckusick 			cacheinval(dp);
9459167Ssam 		} else if (doingdirectory) {
94610051Ssam 			error = EISDIR;
9479167Ssam 			goto bad;
9489167Ssam 		}
94916694Smckusick 		dirrewrite(dp, ip, ndp);
95010051Ssam 		if (u.u_error) {
95110051Ssam 			error = u.u_error;
9529167Ssam 			goto bad1;
95310051Ssam 		}
9549167Ssam 		/*
95510051Ssam 		 * Adjust the link count of the target to
95610051Ssam 		 * reflect the dirrewrite above.  If this is
95710051Ssam 		 * a directory it is empty and there are
95810051Ssam 		 * no links to it, so we can squash the inode and
95910051Ssam 		 * any space associated with it.  We disallowed
96010051Ssam 		 * renaming over top of a directory with links to
96116776Smckusick 		 * it above, as the remaining link would point to
96216776Smckusick 		 * a directory without "." or ".." entries.
9639167Ssam 		 */
96410051Ssam 		xp->i_nlink--;
9659167Ssam 		if (doingdirectory) {
96610051Ssam 			if (--xp->i_nlink != 0)
96710051Ssam 				panic("rename: linked directory");
9689167Ssam 			itrunc(xp, (u_long)0);
96910051Ssam 		}
9709167Ssam 		xp->i_flag |= ICHG;
9719167Ssam 		iput(xp);
97210246Ssam 		xp = NULL;
9739167Ssam 	}
9749167Ssam 
9759167Ssam 	/*
9769167Ssam 	 * 3) Unlink the source.
9779167Ssam 	 */
97816694Smckusick 	ndp->ni_nameiop = DELETE | LOCKPARENT;
97916694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
98016694Smckusick 	ndp->ni_dirp = uap->from;
98116776Smckusick 	xp = namei(ndp);
98217758Smckusick 	if (xp != NULL)
98317758Smckusick 		dp = ndp->ni_pdir;
98417758Smckusick 	else
98517758Smckusick 		dp = NULL;
9869167Ssam 	/*
98716776Smckusick 	 * Insure that the directory entry still exists and has not
98816776Smckusick 	 * changed while the new name has been entered. If the source is
98916776Smckusick 	 * a file then the entry may have been unlinked or renamed. In
99016776Smckusick 	 * either case there is no further work to be done. If the source
99116776Smckusick 	 * is a directory then it cannot have been rmdir'ed; its link
99216776Smckusick 	 * count of three would cause a rmdir to fail with ENOTEMPTY.
99316776Smckusick 	 * The IRENAME flag insures that it cannot be moved by another
99416776Smckusick 	 * rename.
9959167Ssam 	 */
99617758Smckusick 	if (xp != ip) {
99716776Smckusick 		if (doingdirectory)
99817758Smckusick 			panic("rename: lost dir entry");
99916776Smckusick 	} else {
10009167Ssam 		/*
100116776Smckusick 		 * If the source is a directory with a
100216776Smckusick 		 * new parent, the link count of the old
100316776Smckusick 		 * parent directory must be decremented
100416776Smckusick 		 * and ".." set to point to the new parent.
10059167Ssam 		 */
100616776Smckusick 		if (doingdirectory && newparent) {
10079167Ssam 			dp->i_nlink--;
10089167Ssam 			dp->i_flag |= ICHG;
100916776Smckusick 			error = rdwri(UIO_READ, xp, (caddr_t)&dirbuf,
101016776Smckusick 				sizeof (struct dirtemplate), (off_t)0, 1,
101116776Smckusick 				(int *)0);
101216776Smckusick 			if (error == 0) {
101316776Smckusick 				if (dirbuf.dotdot_namlen != 2 ||
101416776Smckusick 				    dirbuf.dotdot_name[0] != '.' ||
101516776Smckusick 				    dirbuf.dotdot_name[1] != '.') {
101616776Smckusick 					printf("rename: mangled dir\n");
101716776Smckusick 				} else {
101816776Smckusick 					dirbuf.dotdot_ino = newparent;
101916776Smckusick 					(void) rdwri(UIO_WRITE, xp,
102016776Smckusick 					    (caddr_t)&dirbuf,
102116776Smckusick 					    sizeof (struct dirtemplate),
102216776Smckusick 					    (off_t)0, 1, (int *)0);
102316776Smckusick 					cacheinval(dp);
102416776Smckusick 				}
102516776Smckusick 			}
10269167Ssam 		}
102716694Smckusick 		if (dirremove(ndp)) {
102816776Smckusick 			xp->i_nlink--;
102916776Smckusick 			xp->i_flag |= ICHG;
10309167Ssam 		}
103116776Smckusick 		xp->i_flag &= ~IRENAME;
103216776Smckusick 		if (error == 0)		/* XXX conservative */
103310051Ssam 			error = u.u_error;
10349167Ssam 	}
10359167Ssam 	if (dp)
10369167Ssam 		iput(dp);
103716776Smckusick 	if (xp)
103816776Smckusick 		iput(xp);
103916776Smckusick 	irele(ip);
104016776Smckusick 	if (error)
104116776Smckusick 		u.u_error = error;
104216776Smckusick 	return;
10439167Ssam 
10449167Ssam bad:
104510246Ssam 	iput(dp);
10469167Ssam bad1:
10479167Ssam 	if (xp)
104810246Ssam 		iput(xp);
10499167Ssam out:
10509167Ssam 	ip->i_nlink--;
10519167Ssam 	ip->i_flag |= ICHG;
10529167Ssam 	irele(ip);
105310051Ssam 	if (error)
105410051Ssam 		u.u_error = error;
10557701Ssam }
10567701Ssam 
10577535Sroot /*
10587535Sroot  * Make a new file.
10597535Sroot  */
10607535Sroot struct inode *
106116694Smckusick maknode(mode, ndp)
10627535Sroot 	int mode;
106316694Smckusick 	register struct nameidata *ndp;
10647535Sroot {
10657535Sroot 	register struct inode *ip;
106616694Smckusick 	register struct inode *pdir = ndp->ni_pdir;
10677535Sroot 	ino_t ipref;
10687535Sroot 
10697535Sroot 	if ((mode & IFMT) == IFDIR)
107016694Smckusick 		ipref = dirpref(pdir->i_fs);
10717535Sroot 	else
107216694Smckusick 		ipref = pdir->i_number;
107316694Smckusick 	ip = ialloc(pdir, ipref, mode);
10747535Sroot 	if (ip == NULL) {
107516694Smckusick 		iput(pdir);
10767701Ssam 		return (NULL);
10777535Sroot 	}
10787701Ssam #ifdef QUOTA
10797535Sroot 	if (ip->i_dquot != NODQUOT)
10807535Sroot 		panic("maknode: dquot");
10817535Sroot #endif
10827535Sroot 	ip->i_flag |= IACC|IUPD|ICHG;
10837535Sroot 	if ((mode & IFMT) == 0)
10847535Sroot 		mode |= IFREG;
10857535Sroot 	ip->i_mode = mode & ~u.u_cmask;
10867535Sroot 	ip->i_nlink = 1;
10877535Sroot 	ip->i_uid = u.u_uid;
108816694Smckusick 	ip->i_gid = pdir->i_gid;
108911811Ssam 	if (ip->i_mode & ISGID && !groupmember(ip->i_gid))
109011811Ssam 		ip->i_mode &= ~ISGID;
10917701Ssam #ifdef QUOTA
10927535Sroot 	ip->i_dquot = inoquota(ip);
10937535Sroot #endif
10947535Sroot 
10957535Sroot 	/*
10967535Sroot 	 * Make sure inode goes to disk before directory entry.
10977535Sroot 	 */
10988673Sroot 	iupdat(ip, &time, &time, 1);
109916694Smckusick 	u.u_error = direnter(ip, ndp);
11007535Sroot 	if (u.u_error) {
11017535Sroot 		/*
110210850Ssam 		 * Write error occurred trying to update directory
110310850Ssam 		 * so must deallocate the inode.
11047535Sroot 		 */
11057535Sroot 		ip->i_nlink = 0;
11067535Sroot 		ip->i_flag |= ICHG;
11077535Sroot 		iput(ip);
11087701Ssam 		return (NULL);
11097535Sroot 	}
11107701Ssam 	return (ip);
11117535Sroot }
111212756Ssam 
111312756Ssam /*
111412756Ssam  * A virgin directory (no blushing please).
111512756Ssam  */
111612756Ssam struct dirtemplate mastertemplate = {
111712756Ssam 	0, 12, 1, ".",
111812756Ssam 	0, DIRBLKSIZ - 12, 2, ".."
111912756Ssam };
112012756Ssam 
112112756Ssam /*
112212756Ssam  * Mkdir system call
112312756Ssam  */
112412756Ssam mkdir()
112512756Ssam {
112612756Ssam 	struct a {
112712756Ssam 		char	*name;
112812756Ssam 		int	dmode;
112916694Smckusick 	} *uap = (struct a *)u.u_ap;
113012756Ssam 	register struct inode *ip, *dp;
113112756Ssam 	struct dirtemplate dirtemplate;
113216694Smckusick 	register struct nameidata *ndp = &u.u_nd;
113312756Ssam 
113416694Smckusick 	ndp->ni_nameiop = CREATE;
113516694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
113616694Smckusick 	ndp->ni_dirp = uap->name;
113716694Smckusick 	ip = namei(ndp);
113812756Ssam 	if (u.u_error)
113912756Ssam 		return;
114012756Ssam 	if (ip != NULL) {
114112756Ssam 		iput(ip);
114212756Ssam 		u.u_error = EEXIST;
114312756Ssam 		return;
114412756Ssam 	}
114516694Smckusick 	dp = ndp->ni_pdir;
114612756Ssam 	uap->dmode &= 0777;
114712756Ssam 	uap->dmode |= IFDIR;
114812756Ssam 	/*
114912756Ssam 	 * Must simulate part of maknode here
115012756Ssam 	 * in order to acquire the inode, but
115112756Ssam 	 * not have it entered in the parent
115212756Ssam 	 * directory.  The entry is made later
115312756Ssam 	 * after writing "." and ".." entries out.
115412756Ssam 	 */
115512756Ssam 	ip = ialloc(dp, dirpref(dp->i_fs), uap->dmode);
115612756Ssam 	if (ip == NULL) {
115712756Ssam 		iput(dp);
115812756Ssam 		return;
115912756Ssam 	}
116012756Ssam #ifdef QUOTA
116112756Ssam 	if (ip->i_dquot != NODQUOT)
116212756Ssam 		panic("mkdir: dquot");
116312756Ssam #endif
116412756Ssam 	ip->i_flag |= IACC|IUPD|ICHG;
116512756Ssam 	ip->i_mode = uap->dmode & ~u.u_cmask;
116612756Ssam 	ip->i_nlink = 2;
116712756Ssam 	ip->i_uid = u.u_uid;
116812756Ssam 	ip->i_gid = dp->i_gid;
116912756Ssam #ifdef QUOTA
117012756Ssam 	ip->i_dquot = inoquota(ip);
117112756Ssam #endif
117212756Ssam 	iupdat(ip, &time, &time, 1);
117312756Ssam 
117412756Ssam 	/*
117512756Ssam 	 * Bump link count in parent directory
117612756Ssam 	 * to reflect work done below.  Should
117712756Ssam 	 * be done before reference is created
117812756Ssam 	 * so reparation is possible if we crash.
117912756Ssam 	 */
118012756Ssam 	dp->i_nlink++;
118112756Ssam 	dp->i_flag |= ICHG;
118212756Ssam 	iupdat(dp, &time, &time, 1);
118312756Ssam 
118412756Ssam 	/*
118512756Ssam 	 * Initialize directory with "."
118612756Ssam 	 * and ".." from static template.
118712756Ssam 	 */
118812756Ssam 	dirtemplate = mastertemplate;
118912756Ssam 	dirtemplate.dot_ino = ip->i_number;
119012756Ssam 	dirtemplate.dotdot_ino = dp->i_number;
119112756Ssam 	u.u_error = rdwri(UIO_WRITE, ip, (caddr_t)&dirtemplate,
119212756Ssam 		sizeof (dirtemplate), (off_t)0, 1, (int *)0);
119312756Ssam 	if (u.u_error) {
119412756Ssam 		dp->i_nlink--;
119512756Ssam 		dp->i_flag |= ICHG;
119612756Ssam 		goto bad;
119712756Ssam 	}
119818103Smckusick 	if (DIRBLKSIZ > ip->i_fs->fs_fsize)
119918103Smckusick 		panic("mkdir: blksize");     /* XXX - should grow with bmap() */
120018103Smckusick 	else
120118103Smckusick 		ip->i_size = DIRBLKSIZ;
120212756Ssam 	/*
120312756Ssam 	 * Directory all set up, now
120412756Ssam 	 * install the entry for it in
120512756Ssam 	 * the parent directory.
120612756Ssam 	 */
120716694Smckusick 	u.u_error = direnter(ip, ndp);
120812756Ssam 	dp = NULL;
120912756Ssam 	if (u.u_error) {
121016694Smckusick 		ndp->ni_nameiop = LOOKUP | NOCACHE;
121116694Smckusick 		ndp->ni_segflg = UIO_USERSPACE;
121216694Smckusick 		ndp->ni_dirp = uap->name;
121316694Smckusick 		dp = namei(ndp);
121412756Ssam 		if (dp) {
121512756Ssam 			dp->i_nlink--;
121612756Ssam 			dp->i_flag |= ICHG;
121712756Ssam 		}
121812756Ssam 	}
121912756Ssam bad:
122012756Ssam 	/*
122112756Ssam 	 * No need to do an explicit itrunc here,
122212756Ssam 	 * irele will do this for us because we set
122312756Ssam 	 * the link count to 0.
122412756Ssam 	 */
122512756Ssam 	if (u.u_error) {
122612756Ssam 		ip->i_nlink = 0;
122712756Ssam 		ip->i_flag |= ICHG;
122812756Ssam 	}
122912756Ssam 	if (dp)
123012756Ssam 		iput(dp);
123112756Ssam 	iput(ip);
123212756Ssam }
123312756Ssam 
123412756Ssam /*
123512756Ssam  * Rmdir system call.
123612756Ssam  */
123712756Ssam rmdir()
123812756Ssam {
123912756Ssam 	struct a {
124012756Ssam 		char	*name;
124116694Smckusick 	} *uap = (struct a *)u.u_ap;
124212756Ssam 	register struct inode *ip, *dp;
124316694Smckusick 	register struct nameidata *ndp = &u.u_nd;
124412756Ssam 
124516694Smckusick 	ndp->ni_nameiop = DELETE | LOCKPARENT;
124616694Smckusick 	ndp->ni_segflg = UIO_USERSPACE;
124716694Smckusick 	ndp->ni_dirp = uap->name;
124816694Smckusick 	ip = namei(ndp);
124912756Ssam 	if (ip == NULL)
125012756Ssam 		return;
125116694Smckusick 	dp = ndp->ni_pdir;
125212756Ssam 	/*
125312756Ssam 	 * No rmdir "." please.
125412756Ssam 	 */
125512756Ssam 	if (dp == ip) {
125612756Ssam 		irele(dp);
125712756Ssam 		iput(ip);
125812756Ssam 		u.u_error = EINVAL;
125912756Ssam 		return;
126012756Ssam 	}
126112756Ssam 	if ((ip->i_mode&IFMT) != IFDIR) {
126212756Ssam 		u.u_error = ENOTDIR;
126312756Ssam 		goto out;
126412756Ssam 	}
126512756Ssam 	/*
126612756Ssam 	 * Don't remove a mounted on directory.
126712756Ssam 	 */
126812756Ssam 	if (ip->i_dev != dp->i_dev) {
126912756Ssam 		u.u_error = EBUSY;
127012756Ssam 		goto out;
127112756Ssam 	}
127212756Ssam 	/*
127312756Ssam 	 * Verify the directory is empty (and valid).
127412756Ssam 	 * (Rmdir ".." won't be valid since
127512756Ssam 	 *  ".." will contain a reference to
127612756Ssam 	 *  the current directory and thus be
127712756Ssam 	 *  non-empty.)
127812756Ssam 	 */
127916776Smckusick 	if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number)) {
128012756Ssam 		u.u_error = ENOTEMPTY;
128112756Ssam 		goto out;
128212756Ssam 	}
128312756Ssam 	/*
128412756Ssam 	 * Delete reference to directory before purging
128512756Ssam 	 * inode.  If we crash in between, the directory
128612756Ssam 	 * will be reattached to lost+found,
128712756Ssam 	 */
128816694Smckusick 	if (dirremove(ndp) == 0)
128912756Ssam 		goto out;
129012756Ssam 	dp->i_nlink--;
129112756Ssam 	dp->i_flag |= ICHG;
129216776Smckusick 	cacheinval(dp);
129312756Ssam 	iput(dp);
129412756Ssam 	dp = NULL;
129512756Ssam 	/*
129612756Ssam 	 * Truncate inode.  The only stuff left
129712756Ssam 	 * in the directory is "." and "..".  The
129812756Ssam 	 * "." reference is inconsequential since
129912756Ssam 	 * we're quashing it.  The ".." reference
130012756Ssam 	 * has already been adjusted above.  We've
130112756Ssam 	 * removed the "." reference and the reference
130212756Ssam 	 * in the parent directory, but there may be
130312756Ssam 	 * other hard links so decrement by 2 and
130412756Ssam 	 * worry about them later.
130512756Ssam 	 */
130612756Ssam 	ip->i_nlink -= 2;
130712756Ssam 	itrunc(ip, (u_long)0);
130816739Smckusick 	cacheinval(ip);
130912756Ssam out:
131012756Ssam 	if (dp)
131112756Ssam 		iput(dp);
131212756Ssam 	iput(ip);
131312756Ssam }
131412756Ssam 
131512756Ssam struct file *
131612756Ssam getinode(fdes)
131712756Ssam 	int fdes;
131812756Ssam {
131916540Ssam 	struct file *fp;
132012756Ssam 
132116540Ssam 	if ((unsigned)fdes >= NOFILE || (fp = u.u_ofile[fdes]) == NULL) {
132216540Ssam 		u.u_error = EBADF;
132316540Ssam 		return ((struct file *)0);
132416540Ssam 	}
132512756Ssam 	if (fp->f_type != DTYPE_INODE) {
132612756Ssam 		u.u_error = EINVAL;
132716540Ssam 		return ((struct file *)0);
132812756Ssam 	}
132912756Ssam 	return (fp);
133012756Ssam }
133112756Ssam 
133212756Ssam /*
133312756Ssam  * mode mask for creation of files
133412756Ssam  */
133512756Ssam umask()
133612756Ssam {
133712756Ssam 	register struct a {
133812756Ssam 		int	mask;
133916694Smckusick 	} *uap = (struct a *)u.u_ap;
134012756Ssam 
134112756Ssam 	u.u_r.r_val1 = u.u_cmask;
134212756Ssam 	u.u_cmask = uap->mask & 07777;
134312756Ssam }
1344