123405Smckusick /* 223405Smckusick * Copyright (c) 1982 Regents of the University of California. 323405Smckusick * All rights reserved. The Berkeley software License Agreement 423405Smckusick * specifies the terms and conditions for redistribution. 523405Smckusick * 6*24433Sbloom * @(#)vfs_syscalls.c 6.20 (Berkeley) 08/26/85 723405Smckusick */ 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 /* 946*24433Sbloom * If the parent directory is "sticky", then the user must 947*24433Sbloom * own the parent directory, or the destination of the rename, 948*24433Sbloom * otherwise the destination may not be changed (except by 949*24433Sbloom * root). This implements append-only directories. 950*24433Sbloom */ 951*24433Sbloom if ((dp->i_mode & ISVTX) && u.u_uid != 0 && 952*24433Sbloom u.u_uid != dp->i_uid && xp->i_uid != u.u_uid) { 953*24433Sbloom error = EPERM; 954*24433Sbloom goto bad; 955*24433Sbloom } 956*24433Sbloom /* 95710051Ssam * Target must be empty if a directory 95810051Ssam * and have no links to it. 9599167Ssam * Also, insure source and target are 9609167Ssam * compatible (both directories, or both 9619167Ssam * not directories). 9629167Ssam */ 9639167Ssam if ((xp->i_mode&IFMT) == IFDIR) { 96416776Smckusick if (!dirempty(xp, dp->i_number) || xp->i_nlink > 2) { 96510051Ssam error = ENOTEMPTY; 9669167Ssam goto bad; 9679167Ssam } 9689167Ssam if (!doingdirectory) { 96910051Ssam error = ENOTDIR; 9709167Ssam goto bad; 9719167Ssam } 97216776Smckusick cacheinval(dp); 9739167Ssam } else if (doingdirectory) { 97410051Ssam error = EISDIR; 9759167Ssam goto bad; 9769167Ssam } 97716694Smckusick dirrewrite(dp, ip, ndp); 97810051Ssam if (u.u_error) { 97910051Ssam error = u.u_error; 9809167Ssam goto bad1; 98110051Ssam } 9829167Ssam /* 98310051Ssam * Adjust the link count of the target to 98410051Ssam * reflect the dirrewrite above. If this is 98510051Ssam * a directory it is empty and there are 98610051Ssam * no links to it, so we can squash the inode and 98710051Ssam * any space associated with it. We disallowed 98810051Ssam * renaming over top of a directory with links to 98916776Smckusick * it above, as the remaining link would point to 99016776Smckusick * a directory without "." or ".." entries. 9919167Ssam */ 99210051Ssam xp->i_nlink--; 9939167Ssam if (doingdirectory) { 99410051Ssam if (--xp->i_nlink != 0) 99510051Ssam panic("rename: linked directory"); 9969167Ssam itrunc(xp, (u_long)0); 99710051Ssam } 9989167Ssam xp->i_flag |= ICHG; 9999167Ssam iput(xp); 100010246Ssam xp = NULL; 10019167Ssam } 10029167Ssam 10039167Ssam /* 10049167Ssam * 3) Unlink the source. 10059167Ssam */ 100616694Smckusick ndp->ni_nameiop = DELETE | LOCKPARENT; 100716694Smckusick ndp->ni_segflg = UIO_USERSPACE; 100816694Smckusick ndp->ni_dirp = uap->from; 100916776Smckusick xp = namei(ndp); 101017758Smckusick if (xp != NULL) 101117758Smckusick dp = ndp->ni_pdir; 101217758Smckusick else 101317758Smckusick dp = NULL; 10149167Ssam /* 101516776Smckusick * Insure that the directory entry still exists and has not 101616776Smckusick * changed while the new name has been entered. If the source is 101716776Smckusick * a file then the entry may have been unlinked or renamed. In 101816776Smckusick * either case there is no further work to be done. If the source 101916776Smckusick * is a directory then it cannot have been rmdir'ed; its link 102016776Smckusick * count of three would cause a rmdir to fail with ENOTEMPTY. 102116776Smckusick * The IRENAME flag insures that it cannot be moved by another 102216776Smckusick * rename. 10239167Ssam */ 102417758Smckusick if (xp != ip) { 102516776Smckusick if (doingdirectory) 102617758Smckusick panic("rename: lost dir entry"); 102716776Smckusick } else { 10289167Ssam /* 102916776Smckusick * If the source is a directory with a 103016776Smckusick * new parent, the link count of the old 103116776Smckusick * parent directory must be decremented 103216776Smckusick * and ".." set to point to the new parent. 10339167Ssam */ 103416776Smckusick if (doingdirectory && newparent) { 10359167Ssam dp->i_nlink--; 10369167Ssam dp->i_flag |= ICHG; 103716776Smckusick error = rdwri(UIO_READ, xp, (caddr_t)&dirbuf, 103816776Smckusick sizeof (struct dirtemplate), (off_t)0, 1, 103916776Smckusick (int *)0); 104016776Smckusick if (error == 0) { 104116776Smckusick if (dirbuf.dotdot_namlen != 2 || 104216776Smckusick dirbuf.dotdot_name[0] != '.' || 104316776Smckusick dirbuf.dotdot_name[1] != '.') { 104416776Smckusick printf("rename: mangled dir\n"); 104516776Smckusick } else { 104616776Smckusick dirbuf.dotdot_ino = newparent; 104716776Smckusick (void) rdwri(UIO_WRITE, xp, 104816776Smckusick (caddr_t)&dirbuf, 104916776Smckusick sizeof (struct dirtemplate), 105016776Smckusick (off_t)0, 1, (int *)0); 105116776Smckusick cacheinval(dp); 105216776Smckusick } 105316776Smckusick } 10549167Ssam } 105516694Smckusick if (dirremove(ndp)) { 105616776Smckusick xp->i_nlink--; 105716776Smckusick xp->i_flag |= ICHG; 10589167Ssam } 105916776Smckusick xp->i_flag &= ~IRENAME; 106016776Smckusick if (error == 0) /* XXX conservative */ 106110051Ssam error = u.u_error; 10629167Ssam } 10639167Ssam if (dp) 10649167Ssam iput(dp); 106516776Smckusick if (xp) 106616776Smckusick iput(xp); 106716776Smckusick irele(ip); 106816776Smckusick if (error) 106916776Smckusick u.u_error = error; 107016776Smckusick return; 10719167Ssam 10729167Ssam bad: 107310246Ssam iput(dp); 10749167Ssam bad1: 10759167Ssam if (xp) 107610246Ssam iput(xp); 10779167Ssam out: 10789167Ssam ip->i_nlink--; 10799167Ssam ip->i_flag |= ICHG; 10809167Ssam irele(ip); 108110051Ssam if (error) 108210051Ssam u.u_error = error; 10837701Ssam } 10847701Ssam 10857535Sroot /* 10867535Sroot * Make a new file. 10877535Sroot */ 10887535Sroot struct inode * 108916694Smckusick maknode(mode, ndp) 10907535Sroot int mode; 109116694Smckusick register struct nameidata *ndp; 10927535Sroot { 10937535Sroot register struct inode *ip; 109416694Smckusick register struct inode *pdir = ndp->ni_pdir; 10957535Sroot ino_t ipref; 10967535Sroot 10977535Sroot if ((mode & IFMT) == IFDIR) 109816694Smckusick ipref = dirpref(pdir->i_fs); 10997535Sroot else 110016694Smckusick ipref = pdir->i_number; 110116694Smckusick ip = ialloc(pdir, ipref, mode); 11027535Sroot if (ip == NULL) { 110316694Smckusick iput(pdir); 11047701Ssam return (NULL); 11057535Sroot } 11067701Ssam #ifdef QUOTA 11077535Sroot if (ip->i_dquot != NODQUOT) 11087535Sroot panic("maknode: dquot"); 11097535Sroot #endif 11107535Sroot ip->i_flag |= IACC|IUPD|ICHG; 11117535Sroot if ((mode & IFMT) == 0) 11127535Sroot mode |= IFREG; 11137535Sroot ip->i_mode = mode & ~u.u_cmask; 11147535Sroot ip->i_nlink = 1; 11157535Sroot ip->i_uid = u.u_uid; 111616694Smckusick ip->i_gid = pdir->i_gid; 111711811Ssam if (ip->i_mode & ISGID && !groupmember(ip->i_gid)) 111811811Ssam ip->i_mode &= ~ISGID; 11197701Ssam #ifdef QUOTA 11207535Sroot ip->i_dquot = inoquota(ip); 11217535Sroot #endif 11227535Sroot 11237535Sroot /* 11247535Sroot * Make sure inode goes to disk before directory entry. 11257535Sroot */ 11268673Sroot iupdat(ip, &time, &time, 1); 112716694Smckusick u.u_error = direnter(ip, ndp); 11287535Sroot if (u.u_error) { 11297535Sroot /* 113010850Ssam * Write error occurred trying to update directory 113110850Ssam * so must deallocate the inode. 11327535Sroot */ 11337535Sroot ip->i_nlink = 0; 11347535Sroot ip->i_flag |= ICHG; 11357535Sroot iput(ip); 11367701Ssam return (NULL); 11377535Sroot } 11387701Ssam return (ip); 11397535Sroot } 114012756Ssam 114112756Ssam /* 114212756Ssam * A virgin directory (no blushing please). 114312756Ssam */ 114412756Ssam struct dirtemplate mastertemplate = { 114512756Ssam 0, 12, 1, ".", 114612756Ssam 0, DIRBLKSIZ - 12, 2, ".." 114712756Ssam }; 114812756Ssam 114912756Ssam /* 115012756Ssam * Mkdir system call 115112756Ssam */ 115212756Ssam mkdir() 115312756Ssam { 115412756Ssam struct a { 115512756Ssam char *name; 115612756Ssam int dmode; 115716694Smckusick } *uap = (struct a *)u.u_ap; 115812756Ssam register struct inode *ip, *dp; 115912756Ssam struct dirtemplate dirtemplate; 116016694Smckusick register struct nameidata *ndp = &u.u_nd; 116112756Ssam 116216694Smckusick ndp->ni_nameiop = CREATE; 116316694Smckusick ndp->ni_segflg = UIO_USERSPACE; 116416694Smckusick ndp->ni_dirp = uap->name; 116516694Smckusick ip = namei(ndp); 116612756Ssam if (u.u_error) 116712756Ssam return; 116812756Ssam if (ip != NULL) { 116912756Ssam iput(ip); 117012756Ssam u.u_error = EEXIST; 117112756Ssam return; 117212756Ssam } 117316694Smckusick dp = ndp->ni_pdir; 117412756Ssam uap->dmode &= 0777; 117512756Ssam uap->dmode |= IFDIR; 117612756Ssam /* 117712756Ssam * Must simulate part of maknode here 117812756Ssam * in order to acquire the inode, but 117912756Ssam * not have it entered in the parent 118012756Ssam * directory. The entry is made later 118112756Ssam * after writing "." and ".." entries out. 118212756Ssam */ 118312756Ssam ip = ialloc(dp, dirpref(dp->i_fs), uap->dmode); 118412756Ssam if (ip == NULL) { 118512756Ssam iput(dp); 118612756Ssam return; 118712756Ssam } 118812756Ssam #ifdef QUOTA 118912756Ssam if (ip->i_dquot != NODQUOT) 119012756Ssam panic("mkdir: dquot"); 119112756Ssam #endif 119212756Ssam ip->i_flag |= IACC|IUPD|ICHG; 119312756Ssam ip->i_mode = uap->dmode & ~u.u_cmask; 119412756Ssam ip->i_nlink = 2; 119512756Ssam ip->i_uid = u.u_uid; 119612756Ssam ip->i_gid = dp->i_gid; 119712756Ssam #ifdef QUOTA 119812756Ssam ip->i_dquot = inoquota(ip); 119912756Ssam #endif 120012756Ssam iupdat(ip, &time, &time, 1); 120112756Ssam 120212756Ssam /* 120312756Ssam * Bump link count in parent directory 120412756Ssam * to reflect work done below. Should 120512756Ssam * be done before reference is created 120612756Ssam * so reparation is possible if we crash. 120712756Ssam */ 120812756Ssam dp->i_nlink++; 120912756Ssam dp->i_flag |= ICHG; 121012756Ssam iupdat(dp, &time, &time, 1); 121112756Ssam 121212756Ssam /* 121312756Ssam * Initialize directory with "." 121412756Ssam * and ".." from static template. 121512756Ssam */ 121612756Ssam dirtemplate = mastertemplate; 121712756Ssam dirtemplate.dot_ino = ip->i_number; 121812756Ssam dirtemplate.dotdot_ino = dp->i_number; 121912756Ssam u.u_error = rdwri(UIO_WRITE, ip, (caddr_t)&dirtemplate, 122012756Ssam sizeof (dirtemplate), (off_t)0, 1, (int *)0); 122112756Ssam if (u.u_error) { 122212756Ssam dp->i_nlink--; 122312756Ssam dp->i_flag |= ICHG; 122412756Ssam goto bad; 122512756Ssam } 122618103Smckusick if (DIRBLKSIZ > ip->i_fs->fs_fsize) 122718103Smckusick panic("mkdir: blksize"); /* XXX - should grow with bmap() */ 122818103Smckusick else 122918103Smckusick ip->i_size = DIRBLKSIZ; 123012756Ssam /* 123112756Ssam * Directory all set up, now 123212756Ssam * install the entry for it in 123312756Ssam * the parent directory. 123412756Ssam */ 123516694Smckusick u.u_error = direnter(ip, ndp); 123612756Ssam dp = NULL; 123712756Ssam if (u.u_error) { 123816694Smckusick ndp->ni_nameiop = LOOKUP | NOCACHE; 123916694Smckusick ndp->ni_segflg = UIO_USERSPACE; 124016694Smckusick ndp->ni_dirp = uap->name; 124116694Smckusick dp = namei(ndp); 124212756Ssam if (dp) { 124312756Ssam dp->i_nlink--; 124412756Ssam dp->i_flag |= ICHG; 124512756Ssam } 124612756Ssam } 124712756Ssam bad: 124812756Ssam /* 124912756Ssam * No need to do an explicit itrunc here, 125012756Ssam * irele will do this for us because we set 125112756Ssam * the link count to 0. 125212756Ssam */ 125312756Ssam if (u.u_error) { 125412756Ssam ip->i_nlink = 0; 125512756Ssam ip->i_flag |= ICHG; 125612756Ssam } 125712756Ssam if (dp) 125812756Ssam iput(dp); 125912756Ssam iput(ip); 126012756Ssam } 126112756Ssam 126212756Ssam /* 126312756Ssam * Rmdir system call. 126412756Ssam */ 126512756Ssam rmdir() 126612756Ssam { 126712756Ssam struct a { 126812756Ssam char *name; 126916694Smckusick } *uap = (struct a *)u.u_ap; 127012756Ssam register struct inode *ip, *dp; 127116694Smckusick register struct nameidata *ndp = &u.u_nd; 127212756Ssam 127316694Smckusick ndp->ni_nameiop = DELETE | LOCKPARENT; 127416694Smckusick ndp->ni_segflg = UIO_USERSPACE; 127516694Smckusick ndp->ni_dirp = uap->name; 127616694Smckusick ip = namei(ndp); 127712756Ssam if (ip == NULL) 127812756Ssam return; 127916694Smckusick dp = ndp->ni_pdir; 128012756Ssam /* 128112756Ssam * No rmdir "." please. 128212756Ssam */ 128312756Ssam if (dp == ip) { 128412756Ssam irele(dp); 128512756Ssam iput(ip); 128612756Ssam u.u_error = EINVAL; 128712756Ssam return; 128812756Ssam } 128912756Ssam if ((ip->i_mode&IFMT) != IFDIR) { 129012756Ssam u.u_error = ENOTDIR; 129112756Ssam goto out; 129212756Ssam } 129312756Ssam /* 129412756Ssam * Don't remove a mounted on directory. 129512756Ssam */ 129612756Ssam if (ip->i_dev != dp->i_dev) { 129712756Ssam u.u_error = EBUSY; 129812756Ssam goto out; 129912756Ssam } 130012756Ssam /* 130112756Ssam * Verify the directory is empty (and valid). 130212756Ssam * (Rmdir ".." won't be valid since 130312756Ssam * ".." will contain a reference to 130412756Ssam * the current directory and thus be 130512756Ssam * non-empty.) 130612756Ssam */ 130716776Smckusick if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number)) { 130812756Ssam u.u_error = ENOTEMPTY; 130912756Ssam goto out; 131012756Ssam } 131112756Ssam /* 131212756Ssam * Delete reference to directory before purging 131312756Ssam * inode. If we crash in between, the directory 131412756Ssam * will be reattached to lost+found, 131512756Ssam */ 131616694Smckusick if (dirremove(ndp) == 0) 131712756Ssam goto out; 131812756Ssam dp->i_nlink--; 131912756Ssam dp->i_flag |= ICHG; 132016776Smckusick cacheinval(dp); 132112756Ssam iput(dp); 132212756Ssam dp = NULL; 132312756Ssam /* 132412756Ssam * Truncate inode. The only stuff left 132512756Ssam * in the directory is "." and "..". The 132612756Ssam * "." reference is inconsequential since 132712756Ssam * we're quashing it. The ".." reference 132812756Ssam * has already been adjusted above. We've 132912756Ssam * removed the "." reference and the reference 133012756Ssam * in the parent directory, but there may be 133112756Ssam * other hard links so decrement by 2 and 133212756Ssam * worry about them later. 133312756Ssam */ 133412756Ssam ip->i_nlink -= 2; 133512756Ssam itrunc(ip, (u_long)0); 133616739Smckusick cacheinval(ip); 133712756Ssam out: 133812756Ssam if (dp) 133912756Ssam iput(dp); 134012756Ssam iput(ip); 134112756Ssam } 134212756Ssam 134312756Ssam struct file * 134412756Ssam getinode(fdes) 134512756Ssam int fdes; 134612756Ssam { 134716540Ssam struct file *fp; 134812756Ssam 134916540Ssam if ((unsigned)fdes >= NOFILE || (fp = u.u_ofile[fdes]) == NULL) { 135016540Ssam u.u_error = EBADF; 135116540Ssam return ((struct file *)0); 135216540Ssam } 135312756Ssam if (fp->f_type != DTYPE_INODE) { 135412756Ssam u.u_error = EINVAL; 135516540Ssam return ((struct file *)0); 135612756Ssam } 135712756Ssam return (fp); 135812756Ssam } 135912756Ssam 136012756Ssam /* 136112756Ssam * mode mask for creation of files 136212756Ssam */ 136312756Ssam umask() 136412756Ssam { 136512756Ssam register struct a { 136612756Ssam int mask; 136716694Smckusick } *uap = (struct a *)u.u_ap; 136812756Ssam 136912756Ssam u.u_r.r_val1 = u.u_cmask; 137012756Ssam u.u_cmask = uap->mask & 07777; 137112756Ssam } 1372