1*12646Ssam /* vfs_syscalls.c 4.56 83/05/21 */ 237Sbill 337Sbill #include "../h/param.h" 437Sbill #include "../h/systm.h" 537Sbill #include "../h/dir.h" 637Sbill #include "../h/user.h" 78040Sroot #include "../h/kernel.h" 86254Sroot #include "../h/file.h" 96574Smckusic #include "../h/stat.h" 1037Sbill #include "../h/inode.h" 116574Smckusic #include "../h/fs.h" 126254Sroot #include "../h/buf.h" 136254Sroot #include "../h/proc.h" 147482Skre #include "../h/quota.h" 157505Sroot #include "../h/descrip.h" 167826Sroot #include "../h/uio.h" 177826Sroot #include "../h/socket.h" 188632Sroot #include "../h/socketvar.h" 199167Ssam #include "../h/nami.h" 2037Sbill 219167Ssam /* 229167Ssam * Change current working directory (``.''). 239167Ssam */ 246254Sroot chdir() 256254Sroot { 266254Sroot 276254Sroot chdirec(&u.u_cdir); 286254Sroot } 296254Sroot 309167Ssam /* 319167Ssam * Change notion of root (``/'') directory. 329167Ssam */ 336254Sroot chroot() 346254Sroot { 356254Sroot 366254Sroot if (suser()) 376254Sroot chdirec(&u.u_rdir); 386254Sroot } 396254Sroot 409167Ssam /* 419167Ssam * Common routine for chroot and chdir. 429167Ssam */ 436254Sroot chdirec(ipp) 447701Ssam register struct inode **ipp; 456254Sroot { 466254Sroot register struct inode *ip; 476254Sroot struct a { 486254Sroot char *fname; 496254Sroot }; 506254Sroot 519167Ssam ip = namei(uchar, LOOKUP, 1); 529167Ssam if (ip == NULL) 536254Sroot return; 549167Ssam if ((ip->i_mode&IFMT) != IFDIR) { 556254Sroot u.u_error = ENOTDIR; 566254Sroot goto bad; 576254Sroot } 589167Ssam if (access(ip, IEXEC)) 596254Sroot goto bad; 607122Smckusick iunlock(ip); 617142Smckusick if (*ipp) 627142Smckusick irele(*ipp); 636254Sroot *ipp = ip; 646254Sroot return; 656254Sroot 666254Sroot bad: 676254Sroot iput(ip); 686254Sroot } 696254Sroot 7037Sbill /* 716254Sroot * Open system call. 726254Sroot */ 736254Sroot open() 746254Sroot { 756254Sroot register struct inode *ip; 766254Sroot register struct a { 776254Sroot char *fname; 787701Ssam int flags; 797701Ssam int mode; 806254Sroot } *uap; 819167Ssam int checkpermissions = 1, flags; 826254Sroot 836254Sroot uap = (struct a *)u.u_ap; 849167Ssam flags = uap->flags + 1; 859167Ssam if ((flags&FTRUNCATE) && (flags&FWRITE) == 0) { 869167Ssam u.u_error = EINVAL; 879167Ssam return; 889167Ssam } 899167Ssam if (flags&FCREATE) { 909167Ssam ip = namei(uchar, CREATE, 1); 917701Ssam if (ip == NULL) { 927701Ssam if (u.u_error) 937701Ssam return; 947701Ssam ip = maknode(uap->mode&07777&(~ISVTX)); 957701Ssam checkpermissions = 0; 969167Ssam flags &= ~FTRUNCATE; 977701Ssam } 987701Ssam } else 999167Ssam ip = namei(uchar, LOOKUP, 1); 1006254Sroot if (ip == NULL) 1016254Sroot return; 1029167Ssam open1(ip, flags, checkpermissions); 1036254Sroot } 1046254Sroot 1057701Ssam #ifndef NOCOMPAT 1066254Sroot /* 1076254Sroot * Creat system call. 1086254Sroot */ 1097505Sroot ocreat() 1106254Sroot { 1116254Sroot register struct inode *ip; 1126254Sroot register struct a { 1136254Sroot char *fname; 1146254Sroot int fmode; 1156254Sroot } *uap; 1166254Sroot 1176254Sroot uap = (struct a *)u.u_ap; 1189167Ssam ip = namei(uchar, CREATE, 1); 1196254Sroot if (ip == NULL) { 1206254Sroot if (u.u_error) 1216254Sroot return; 1226254Sroot ip = maknode(uap->fmode&07777&(~ISVTX)); 1237701Ssam if (ip == NULL) 1246254Sroot return; 1257701Ssam open1(ip, FWRITE, 0); 1266254Sroot } else 1279167Ssam open1(ip, FWRITE|FTRUNCATE, 1); 1286254Sroot } 1297701Ssam #endif 1306254Sroot 1316254Sroot /* 1326254Sroot * Common code for open and creat. 1337701Ssam * Check permissions (if we haven't done so already), 1347701Ssam * allocate an open file structure, and call 1357701Ssam * the device open routine, if any. 1366254Sroot */ 1377701Ssam open1(ip, mode, checkpermissions) 1386254Sroot register struct inode *ip; 1396254Sroot register mode; 1406254Sroot { 1416254Sroot register struct file *fp; 1427701Ssam int i, flags; 1436254Sroot 1447701Ssam if (checkpermissions) { 1456254Sroot if (mode&FREAD) 1467701Ssam if (access(ip, IREAD)) 1477701Ssam goto bad; 1486254Sroot if (mode&FWRITE) { 1497701Ssam if (access(ip, IWRITE)) 1507701Ssam goto bad; 1517701Ssam if ((ip->i_mode&IFMT) == IFDIR) { 1526254Sroot u.u_error = EISDIR; 1537701Ssam goto bad; 1547701Ssam } 1556254Sroot } 1566254Sroot } 1577701Ssam 1587701Ssam /* 1597701Ssam * Check locking on inode. Release "inode lock" 1607701Ssam * while doing so in case we block inside flocki. 1617701Ssam */ 1627701Ssam flags = 0; 1639167Ssam if (mode&(FSHLOCK|FEXLOCK)) { 1647701Ssam iunlock(ip); 1657701Ssam flags = flocki(ip, 0, mode); 1667701Ssam ilock(ip); 1677701Ssam if (u.u_error) 1687701Ssam goto bad; 1697142Smckusick } 1707701Ssam if (mode&FTRUNCATE) 1719167Ssam itrunc(ip, (u_long)0); 1727122Smckusick iunlock(ip); 1736254Sroot if ((fp = falloc()) == NULL) 1746254Sroot goto out; 1757701Ssam fp->f_flag = mode & FMODES; 1767505Sroot fp->f_type = DTYPE_FILE; 1776254Sroot i = u.u_r.r_val1; 1786254Sroot fp->f_inode = ip; 1798559Sroot u.u_error = openi(ip, mode); 1807701Ssam if (u.u_error == 0) { 1817701Ssam u.u_pofile[i] = flags; 1826254Sroot return; 1837701Ssam } 1846254Sroot u.u_ofile[i] = NULL; 1856254Sroot fp->f_count--; 1866254Sroot out: 1877142Smckusick irele(ip); 1887701Ssam return; 1897701Ssam bad: 1907701Ssam iput(ip); 1916254Sroot } 1926254Sroot 1936254Sroot /* 1946254Sroot * Mknod system call 1956254Sroot */ 1966254Sroot mknod() 1976254Sroot { 1986254Sroot register struct inode *ip; 1996254Sroot register struct a { 2006254Sroot char *fname; 2016254Sroot int fmode; 2026254Sroot int dev; 2036254Sroot } *uap; 2046254Sroot 2056254Sroot uap = (struct a *)u.u_ap; 2066254Sroot if (suser()) { 2079167Ssam ip = namei(uchar, CREATE, 0); 2086254Sroot if (ip != NULL) { 2096254Sroot u.u_error = EEXIST; 2106254Sroot goto out; 2116254Sroot } 2126254Sroot } 2136254Sroot if (u.u_error) 2146254Sroot return; 2156254Sroot ip = maknode(uap->fmode); 2166254Sroot if (ip == NULL) 2176254Sroot return; 2186254Sroot if (uap->dev) { 2196254Sroot /* 2206254Sroot * Want to be able to use this to make badblock 2216254Sroot * inodes, so don't truncate the dev number. 2226254Sroot */ 2236574Smckusic ip->i_rdev = uap->dev; 2246254Sroot ip->i_flag |= IACC|IUPD|ICHG; 2256254Sroot } 2266254Sroot 2276254Sroot out: 2286254Sroot iput(ip); 2296254Sroot } 2306254Sroot 2316254Sroot /* 2326254Sroot * link system call 2336254Sroot */ 2346254Sroot link() 2356254Sroot { 2366254Sroot register struct inode *ip, *xp; 2376254Sroot register struct a { 2386254Sroot char *target; 2396254Sroot char *linkname; 2406254Sroot } *uap; 2416254Sroot 2426254Sroot uap = (struct a *)u.u_ap; 2439167Ssam ip = namei(uchar, LOOKUP, 1); /* well, this routine is doomed anyhow */ 2446254Sroot if (ip == NULL) 2456254Sroot return; 2469167Ssam if ((ip->i_mode&IFMT) == IFDIR && !suser()) { 2477439Sroot iput(ip); 2487439Sroot return; 2497439Sroot } 2506254Sroot ip->i_nlink++; 2516254Sroot ip->i_flag |= ICHG; 2528673Sroot iupdat(ip, &time, &time, 1); 2537122Smckusick iunlock(ip); 2546254Sroot u.u_dirp = (caddr_t)uap->linkname; 2559167Ssam xp = namei(uchar, CREATE, 0); 2566254Sroot if (xp != NULL) { 2576254Sroot u.u_error = EEXIST; 2586254Sroot iput(xp); 2596254Sroot goto out; 2606254Sroot } 2616254Sroot if (u.u_error) 2626254Sroot goto out; 2636254Sroot if (u.u_pdir->i_dev != ip->i_dev) { 2646254Sroot iput(u.u_pdir); 2656254Sroot u.u_error = EXDEV; 2666254Sroot goto out; 2676254Sroot } 26810850Ssam u.u_error = direnter(ip); 2696254Sroot out: 2706254Sroot if (u.u_error) { 2716254Sroot ip->i_nlink--; 2726254Sroot ip->i_flag |= ICHG; 2736254Sroot } 2747142Smckusick irele(ip); 2756254Sroot } 2766254Sroot 2776254Sroot /* 2786254Sroot * symlink -- make a symbolic link 2796254Sroot */ 2806254Sroot symlink() 2816254Sroot { 2826254Sroot register struct a { 2836254Sroot char *target; 2846254Sroot char *linkname; 2856254Sroot } *uap; 2866254Sroot register struct inode *ip; 2876254Sroot register char *tp; 2886254Sroot register c, nc; 2896254Sroot 2906254Sroot uap = (struct a *)u.u_ap; 2916254Sroot tp = uap->target; 2926254Sroot nc = 0; 2936254Sroot while (c = fubyte(tp)) { 2946254Sroot if (c < 0) { 2956254Sroot u.u_error = EFAULT; 2966254Sroot return; 2976254Sroot } 2986254Sroot tp++; 2996254Sroot nc++; 3006254Sroot } 3016254Sroot u.u_dirp = uap->linkname; 3029167Ssam ip = namei(uchar, CREATE, 0); 3036254Sroot if (ip) { 3046254Sroot iput(ip); 3056254Sroot u.u_error = EEXIST; 3066254Sroot return; 3076254Sroot } 3086254Sroot if (u.u_error) 3096254Sroot return; 3106254Sroot ip = maknode(IFLNK | 0777); 3116254Sroot if (ip == NULL) 3126254Sroot return; 3137826Sroot u.u_error = rdwri(UIO_WRITE, ip, uap->target, nc, 0, 0, (int *)0); 3149167Ssam /* handle u.u_error != 0 */ 3156254Sroot iput(ip); 3166254Sroot } 3176254Sroot 3186254Sroot /* 3196254Sroot * Unlink system call. 3206254Sroot * Hard to avoid races here, especially 3216254Sroot * in unlinking directories. 3226254Sroot */ 3236254Sroot unlink() 3246254Sroot { 3256254Sroot struct a { 3266254Sroot char *fname; 3276254Sroot }; 3289167Ssam register struct inode *ip, *dp; 3296254Sroot 3309167Ssam ip = namei(uchar, DELETE | LOCKPARENT, 0); 3319167Ssam if (ip == NULL) 3326254Sroot return; 3339167Ssam dp = u.u_pdir; 3349167Ssam if ((ip->i_mode&IFMT) == IFDIR && !suser()) 3356254Sroot goto out; 3366254Sroot /* 3376254Sroot * Don't unlink a mounted file. 3386254Sroot */ 3399167Ssam if (ip->i_dev != dp->i_dev) { 3406254Sroot u.u_error = EBUSY; 3416254Sroot goto out; 3426254Sroot } 3436254Sroot if (ip->i_flag&ITEXT) 3446254Sroot xrele(ip); /* try once to free text */ 3457535Sroot if (dirremove()) { 3467535Sroot ip->i_nlink--; 3477535Sroot ip->i_flag |= ICHG; 3486254Sroot } 3496254Sroot out: 3509167Ssam if (dp == ip) 3517142Smckusick irele(ip); 3527142Smckusick else 3537142Smckusick iput(ip); 3549167Ssam iput(dp); 3556254Sroot } 3566254Sroot 3576254Sroot /* 3586254Sroot * Seek system call 3596254Sroot */ 3608040Sroot lseek() 3616254Sroot { 3626254Sroot register struct file *fp; 3636254Sroot register struct a { 3647701Ssam int fd; 3656254Sroot off_t off; 3666254Sroot int sbase; 3676254Sroot } *uap; 3686254Sroot 3696254Sroot uap = (struct a *)u.u_ap; 3707701Ssam fp = getf(uap->fd); 3716254Sroot if (fp == NULL) 3726254Sroot return; 3737505Sroot if (fp->f_type == DTYPE_SOCKET) { 3746254Sroot u.u_error = ESPIPE; 3756254Sroot return; 3766254Sroot } 3777701Ssam if (uap->sbase == FSEEK_RELATIVE) 3786254Sroot uap->off += fp->f_offset; 3797701Ssam else if (uap->sbase == FSEEK_EOF) 3806254Sroot uap->off += fp->f_inode->i_size; 3816254Sroot fp->f_offset = uap->off; 3826254Sroot u.u_r.r_off = uap->off; 3836254Sroot } 3846254Sroot 3856254Sroot /* 3866254Sroot * Access system call 3876254Sroot */ 3886254Sroot saccess() 3896254Sroot { 3906254Sroot register svuid, svgid; 3916254Sroot register struct inode *ip; 3926254Sroot register struct a { 3936254Sroot char *fname; 3946254Sroot int fmode; 3956254Sroot } *uap; 3966254Sroot 3976254Sroot uap = (struct a *)u.u_ap; 3986254Sroot svuid = u.u_uid; 3996254Sroot svgid = u.u_gid; 4006254Sroot u.u_uid = u.u_ruid; 4016254Sroot u.u_gid = u.u_rgid; 4029167Ssam ip = namei(uchar, LOOKUP, 1); 4036254Sroot if (ip != NULL) { 4049167Ssam if ((uap->fmode&FACCESS_READ) && access(ip, IREAD)) 4057701Ssam goto done; 4069167Ssam if ((uap->fmode&FACCESS_WRITE) && access(ip, IWRITE)) 4077701Ssam goto done; 4089167Ssam if ((uap->fmode&FACCESS_EXECUTE) && access(ip, IEXEC)) 4097701Ssam goto done; 4107701Ssam done: 4116254Sroot iput(ip); 4126254Sroot } 4136254Sroot u.u_uid = svuid; 4146254Sroot u.u_gid = svgid; 4156254Sroot } 4166254Sroot 4176254Sroot /* 41837Sbill * the fstat system call. 41937Sbill */ 42037Sbill fstat() 42137Sbill { 42237Sbill register struct file *fp; 42337Sbill register struct a { 4247701Ssam int fd; 42537Sbill struct stat *sb; 42637Sbill } *uap; 42737Sbill 42837Sbill uap = (struct a *)u.u_ap; 4297701Ssam fp = getf(uap->fd); 4304828Swnj if (fp == NULL) 43137Sbill return; 4327505Sroot if (fp->f_type == DTYPE_SOCKET) 4334891Swnj u.u_error = sostat(fp->f_socket, uap->sb); 4344828Swnj else 4354828Swnj stat1(fp->f_inode, uap->sb); 43637Sbill } 43737Sbill 43837Sbill /* 4396574Smckusic * Stat system call. This version follows links. 44037Sbill */ 44137Sbill stat() 44237Sbill { 44337Sbill register struct inode *ip; 44437Sbill register struct a { 44537Sbill char *fname; 44637Sbill struct stat *sb; 44737Sbill } *uap; 44837Sbill 44937Sbill uap = (struct a *)u.u_ap; 4509167Ssam ip = namei(uchar, LOOKUP, 1); 4514828Swnj if (ip == NULL) 45237Sbill return; 4533624Sroot stat1(ip, uap->sb); 45437Sbill iput(ip); 45537Sbill } 45637Sbill 45737Sbill /* 4586574Smckusic * Lstat system call. This version does not follow links. 4595992Swnj */ 4605992Swnj lstat() 4615992Swnj { 4625992Swnj register struct inode *ip; 4635992Swnj register struct a { 4645992Swnj char *fname; 4655992Swnj struct stat *sb; 4665992Swnj } *uap; 4675992Swnj 4685992Swnj uap = (struct a *)u.u_ap; 4699167Ssam ip = namei(uchar, LOOKUP, 0); 4705992Swnj if (ip == NULL) 4715992Swnj return; 4726153Ssam stat1(ip, uap->sb); 4735992Swnj iput(ip); 4745992Swnj } 4755992Swnj 4765992Swnj /* 47737Sbill * The basic routine for fstat and stat: 47837Sbill * get the inode and pass appropriate parts back. 47937Sbill */ 4803624Sroot stat1(ip, ub) 4814828Swnj register struct inode *ip; 4824828Swnj struct stat *ub; 48337Sbill { 48437Sbill struct stat ds; 48537Sbill 4868673Sroot IUPDAT(ip, &time, &time, 0); 48737Sbill /* 4887023Smckusick * Copy from inode table 48937Sbill */ 49037Sbill ds.st_dev = ip->i_dev; 49137Sbill ds.st_ino = ip->i_number; 49237Sbill ds.st_mode = ip->i_mode; 49337Sbill ds.st_nlink = ip->i_nlink; 49437Sbill ds.st_uid = ip->i_uid; 49537Sbill ds.st_gid = ip->i_gid; 4966574Smckusic ds.st_rdev = (dev_t)ip->i_rdev; 4973624Sroot ds.st_size = ip->i_size; 4986574Smckusic ds.st_atime = ip->i_atime; 4999903Ssam ds.st_spare1 = 0; 5006574Smckusic ds.st_mtime = ip->i_mtime; 5019903Ssam ds.st_spare2 = 0; 5026574Smckusic ds.st_ctime = ip->i_ctime; 5039903Ssam ds.st_spare3 = 0; 5047701Ssam /* this doesn't belong here */ 5057701Ssam if ((ip->i_mode&IFMT) == IFBLK) 5067701Ssam ds.st_blksize = BLKDEV_IOSIZE; 5077701Ssam else if ((ip->i_mode&IFMT) == IFCHR) 5087701Ssam ds.st_blksize = MAXBSIZE; 5097701Ssam else 5107701Ssam ds.st_blksize = ip->i_fs->fs_bsize; 511*12646Ssam ds.st_blocks = ip->i_blocks; 512*12646Ssam ds.st_spare4[0] = ds.st_spare4[1] = 0; 51310001Ssam u.u_error = copyout((caddr_t)&ds, (caddr_t)ub, sizeof(ds)); 51437Sbill } 51537Sbill 51637Sbill /* 5175992Swnj * Return target name of a symbolic link 51837Sbill */ 5195992Swnj readlink() 5205992Swnj { 5215992Swnj register struct inode *ip; 5225992Swnj register struct a { 5235992Swnj char *name; 5245992Swnj char *buf; 5255992Swnj int count; 5267826Sroot } *uap = (struct a *)u.u_ap; 5277826Sroot int resid; 5285992Swnj 5299167Ssam ip = namei(uchar, LOOKUP, 0); 5305992Swnj if (ip == NULL) 5315992Swnj return; 5325992Swnj if ((ip->i_mode&IFMT) != IFLNK) { 5335992Swnj u.u_error = ENXIO; 5345992Swnj goto out; 5355992Swnj } 5367826Sroot u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, 0, 0, &resid); 5375992Swnj out: 5385992Swnj iput(ip); 5397826Sroot u.u_r.r_val1 = uap->count - resid; 5405992Swnj } 5415992Swnj 5429167Ssam /* 5439167Ssam * Change mode of a file given path name. 5449167Ssam */ 5456254Sroot chmod() 5465992Swnj { 5477701Ssam struct inode *ip; 5487701Ssam struct a { 5496254Sroot char *fname; 5506254Sroot int fmode; 5515992Swnj } *uap; 5525992Swnj 5535992Swnj uap = (struct a *)u.u_ap; 5546254Sroot if ((ip = owner(1)) == NULL) 5555992Swnj return; 5567701Ssam chmod1(ip, uap->fmode); 5579167Ssam iput(ip); 5587701Ssam } 5597439Sroot 5609167Ssam /* 5619167Ssam * Change mode of a file given a file descriptor. 5629167Ssam */ 5637701Ssam fchmod() 5647701Ssam { 5657701Ssam struct a { 5667701Ssam int fd; 5677701Ssam int fmode; 5687701Ssam } *uap; 5697701Ssam register struct inode *ip; 5707701Ssam register struct file *fp; 5717701Ssam 5727701Ssam uap = (struct a *)u.u_ap; 5737701Ssam fp = getf(uap->fd); 5747701Ssam if (fp == NULL) 5757701Ssam return; 5767701Ssam if (fp->f_type == DTYPE_SOCKET) { 5777701Ssam u.u_error = EINVAL; 5787701Ssam return; 5797439Sroot } 5807701Ssam ip = fp->f_inode; 5819167Ssam if (u.u_uid != ip->i_uid && !suser()) 5829167Ssam return; 5837701Ssam ilock(ip); 5847701Ssam chmod1(ip, uap->fmode); 5859167Ssam iunlock(ip); 5867701Ssam } 5877701Ssam 5889167Ssam /* 5899167Ssam * Change the mode on a file. 5909167Ssam * Inode must be locked before calling. 5919167Ssam */ 5927701Ssam chmod1(ip, mode) 5937701Ssam register struct inode *ip; 5947701Ssam register int mode; 5957701Ssam { 5967868Sroot register int *gp; 5977868Sroot 5986254Sroot ip->i_mode &= ~07777; 5997439Sroot if (u.u_uid) { 6007701Ssam mode &= ~ISVTX; 60111811Ssam if (!groupmember(ip->i_gid)) 60211811Ssam mode &= ~ISGID; 6037439Sroot } 6047701Ssam ip->i_mode |= mode&07777; 6056254Sroot ip->i_flag |= ICHG; 6066254Sroot if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0) 6076254Sroot xrele(ip); 6085992Swnj } 6095992Swnj 6109167Ssam /* 6119167Ssam * Set ownership given a path name. 6129167Ssam */ 6136254Sroot chown() 61437Sbill { 6157701Ssam struct inode *ip; 6167701Ssam struct a { 6176254Sroot char *fname; 6186254Sroot int uid; 6196254Sroot int gid; 62037Sbill } *uap; 62137Sbill 62237Sbill uap = (struct a *)u.u_ap; 62311821Ssam if (!suser() || (ip = owner(0)) == NULL) 62437Sbill return; 62511811Ssam u.u_error = chown1(ip, uap->uid, uap->gid); 6269167Ssam iput(ip); 6277701Ssam } 6287439Sroot 6299167Ssam /* 6309167Ssam * Set ownership given a file descriptor. 6319167Ssam */ 6327701Ssam fchown() 6337701Ssam { 6347701Ssam struct a { 6357701Ssam int fd; 6367701Ssam int uid; 6377701Ssam int gid; 6387701Ssam } *uap; 6397701Ssam register struct inode *ip; 6407701Ssam register struct file *fp; 6417701Ssam 6427701Ssam uap = (struct a *)u.u_ap; 6437701Ssam fp = getf(uap->fd); 6447701Ssam if (fp == NULL) 6457701Ssam return; 6467701Ssam if (fp->f_type == DTYPE_SOCKET) { 6477701Ssam u.u_error = EINVAL; 6487701Ssam return; 6497439Sroot } 6507701Ssam ip = fp->f_inode; 65111821Ssam if (!suser()) 6529167Ssam return; 6537701Ssam ilock(ip); 65411811Ssam u.u_error = chown1(ip, uap->uid, uap->gid); 6559167Ssam iunlock(ip); 6567701Ssam } 6577701Ssam 6587701Ssam /* 6597701Ssam * Perform chown operation on inode ip; 6607701Ssam * inode must be locked prior to call. 6617701Ssam */ 6627701Ssam chown1(ip, uid, gid) 6637701Ssam register struct inode *ip; 6647701Ssam int uid, gid; 6657701Ssam { 6667701Ssam #ifdef QUOTA 6677701Ssam register long change; 66811811Ssam #endif 6697701Ssam 67011811Ssam if (uid == -1) 67111811Ssam uid = ip->i_uid; 67211811Ssam if (gid == -1) 67311811Ssam gid = ip->i_gid; 67411811Ssam #ifdef QUOTA 675*12646Ssam if (ip->i_uid != uid) /* this just speeds things a little */ 6767482Skre change = 0; 677*12646Ssam else 678*12646Ssam change = ip->i_blocks; 679*12646Ssam (void) chkdq(ip, -change, 1); 680*12646Ssam (void) chkiq(ip->i_dev, ip, ip->i_uid, 1); 6817482Skre dqrele(ip->i_dquot); 6827482Skre #endif 68311811Ssam ip->i_uid = uid; 68411811Ssam ip->i_gid = gid; 6856254Sroot ip->i_flag |= ICHG; 6866254Sroot if (u.u_ruid != 0) 6876254Sroot ip->i_mode &= ~(ISUID|ISGID); 6887701Ssam #ifdef QUOTA 6897482Skre ip->i_dquot = inoquota(ip); 690*12646Ssam (void) chkdq(ip, change, 1); 691*12646Ssam (void) chkiq(ip->i_dev, (struct inode *)NULL, uid, 1); 692*12646Ssam return (u.u_error); /* should == 0 ALWAYS !! */ 693*12646Ssam #else 694*12646Ssam return (0); 6957482Skre #endif 69637Sbill } 69737Sbill 69811811Ssam #ifndef NOCOMPAT 69937Sbill /* 7006254Sroot * Set IUPD and IACC times on file. 7016254Sroot * Can't set ICHG. 70237Sbill */ 7038107Sroot outime() 7044828Swnj { 70537Sbill register struct a { 7066254Sroot char *fname; 7076254Sroot time_t *tptr; 70811811Ssam } *uap = (struct a *)u.u_ap; 7096254Sroot register struct inode *ip; 7106254Sroot time_t tv[2]; 7118632Sroot struct timeval tv0, tv1; 71237Sbill 7136254Sroot if ((ip = owner(1)) == NULL) 71437Sbill return; 71511811Ssam u.u_error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv)); 71610001Ssam if (u.u_error == 0) { 7176254Sroot ip->i_flag |= IACC|IUPD|ICHG; 7188632Sroot tv0.tv_sec = tv[0]; tv0.tv_usec = 0; 7198632Sroot tv1.tv_sec = tv[1]; tv1.tv_usec = 0; 7208632Sroot iupdat(ip, &tv0, &tv1, 0); 72137Sbill } 72237Sbill iput(ip); 72337Sbill } 72411811Ssam #endif 72537Sbill 72611811Ssam utimes() 72711811Ssam { 72811811Ssam register struct a { 72911811Ssam char *fname; 73011811Ssam struct timeval *tptr; 73111811Ssam } *uap = (struct a *)u.u_ap; 73211811Ssam register struct inode *ip; 73311811Ssam struct timeval tv[2]; 73411811Ssam 73511811Ssam if ((ip = owner(1)) == NULL) 73611811Ssam return; 73711811Ssam u.u_error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv)); 73811811Ssam if (u.u_error == 0) { 73911811Ssam ip->i_flag |= IACC|IUPD|ICHG; 74011811Ssam iupdat(ip, &tv[0], &tv[1], 0); 74111811Ssam } 74211811Ssam iput(ip); 74311811Ssam } 74411811Ssam 7459167Ssam /* 7469167Ssam * Flush any pending I/O. 7479167Ssam */ 7486254Sroot sync() 74937Sbill { 75037Sbill 7518673Sroot update(); 75237Sbill } 7537535Sroot 7549167Ssam /* 7559167Ssam * Apply an advisory lock on a file descriptor. 7569167Ssam */ 7577701Ssam flock() 7587701Ssam { 7597701Ssam struct a { 7607701Ssam int fd; 7617701Ssam int how; 7627701Ssam } *uap; 7637701Ssam register struct file *fp; 7647701Ssam register int cmd, flags; 7657701Ssam 7667701Ssam uap = (struct a *)u.u_ap; 7677701Ssam fp = getf(uap->fd); 7687701Ssam if (fp == NULL) 7697701Ssam return; 7707701Ssam if (fp->f_type == DTYPE_SOCKET) { /* XXX */ 7717701Ssam u.u_error = EINVAL; 7727701Ssam return; 7737701Ssam } 7747701Ssam cmd = uap->how; 7759593Ssam flags = u.u_pofile[uap->fd] & (UF_SHLOCK|UF_EXLOCK); 7767701Ssam if (cmd&FUNLOCK) { 7777701Ssam if (flags == 0) { 7787701Ssam u.u_error = EINVAL; 7797701Ssam return; 7807701Ssam } 7817701Ssam funlocki(fp->f_inode, flags); 7829593Ssam u.u_pofile[uap->fd] &= ~(UF_SHLOCK|UF_EXLOCK); 7837701Ssam return; 7847701Ssam } 7857701Ssam /* 7867701Ssam * No reason to write lock a file we've already 7877701Ssam * write locked, similarly with a read lock. 7887701Ssam */ 7899593Ssam if ((flags&UF_EXLOCK) && (cmd&FEXLOCK) || 7909593Ssam (flags&UF_SHLOCK) && (cmd&FSHLOCK)) 7917701Ssam return; 7927701Ssam u.u_pofile[uap->fd] = flocki(fp->f_inode, u.u_pofile[uap->fd], cmd); 7937701Ssam } 7947701Ssam 7959167Ssam /* 7969167Ssam * Truncate a file given its path name. 7979167Ssam */ 7987701Ssam truncate() 7997701Ssam { 8007701Ssam struct a { 8017701Ssam char *fname; 8029167Ssam u_long length; 8037826Sroot } *uap = (struct a *)u.u_ap; 8047701Ssam struct inode *ip; 8057701Ssam 8069167Ssam ip = namei(uchar, LOOKUP, 1); 8077701Ssam if (ip == NULL) 8087701Ssam return; 8097701Ssam if (access(ip, IWRITE)) 8107701Ssam goto bad; 8117701Ssam if ((ip->i_mode&IFMT) == IFDIR) { 8127701Ssam u.u_error = EISDIR; 8137701Ssam goto bad; 8147701Ssam } 8157701Ssam itrunc(ip, uap->length); 8167701Ssam bad: 8177701Ssam iput(ip); 8187701Ssam } 8197701Ssam 8209167Ssam /* 8219167Ssam * Truncate a file given a file descriptor. 8229167Ssam */ 8237701Ssam ftruncate() 8247701Ssam { 8257701Ssam struct a { 8267701Ssam int fd; 8279167Ssam u_long length; 8287826Sroot } *uap = (struct a *)u.u_ap; 8297701Ssam struct inode *ip; 8307701Ssam struct file *fp; 8317701Ssam 8327701Ssam fp = getf(uap->fd); 8337701Ssam if (fp == NULL) 8347701Ssam return; 8357701Ssam if (fp->f_type == DTYPE_SOCKET) { 8367701Ssam u.u_error = EINVAL; 8377701Ssam return; 8387701Ssam } 8397701Ssam if ((fp->f_flag&FWRITE) == 0) { 8407701Ssam u.u_error = EINVAL; 8417701Ssam return; 8427701Ssam } 8437701Ssam ip = fp->f_inode; 8447701Ssam ilock(ip); 8457701Ssam itrunc(ip, uap->length); 8469167Ssam iunlock(ip); 8477701Ssam } 8487701Ssam 8499167Ssam /* 8509167Ssam * Synch an open file. 8519167Ssam */ 8529167Ssam fsync() 8539167Ssam { 8549167Ssam struct a { 8559167Ssam int fd; 8569167Ssam } *uap = (struct a *)u.u_ap; 8579167Ssam struct inode *ip; 8589167Ssam struct file *fp; 8599167Ssam 8609167Ssam fp = getf(uap->fd); 8619167Ssam if (fp == NULL) 8629167Ssam return; 8639167Ssam if (fp->f_type == DTYPE_SOCKET) { 8649167Ssam u.u_error = EINVAL; 8659167Ssam return; 8669167Ssam } 8679167Ssam ip = fp->f_inode; 8689167Ssam ilock(ip); 8699167Ssam syncip(ip); 8709167Ssam iunlock(ip); 8719167Ssam } 8729167Ssam 8739167Ssam /* 8749167Ssam * Rename system call. 8759167Ssam * rename("foo", "bar"); 8769167Ssam * is essentially 8779167Ssam * unlink("bar"); 8789167Ssam * link("foo", "bar"); 8799167Ssam * unlink("foo"); 8809167Ssam * but ``atomically''. Can't do full commit without saving state in the 8819167Ssam * inode on disk which isn't feasible at this time. Best we can do is 8829167Ssam * always guarantee the target exists. 8839167Ssam * 8849167Ssam * Basic algorithm is: 8859167Ssam * 8869167Ssam * 1) Bump link count on source while we're linking it to the 8879167Ssam * target. This also insure the inode won't be deleted out 8889167Ssam * from underneath us while we work. 8899167Ssam * 2) Link source to destination. If destination already exists, 8909167Ssam * delete it first. 8919167Ssam * 3) Unlink source reference to inode if still around. 8929167Ssam * 4) If a directory was moved and the parent of the destination 8939167Ssam * is different from the source, patch the ".." entry in the 8949167Ssam * directory. 8959167Ssam * 8969167Ssam * Source and destination must either both be directories, or both 8979167Ssam * not be directories. If target is a directory, it must be empty. 8989167Ssam */ 8997701Ssam rename() 9007701Ssam { 9017701Ssam struct a { 9027701Ssam char *from; 9037701Ssam char *to; 9047701Ssam } *uap; 9059167Ssam register struct inode *ip, *xp, *dp; 9069167Ssam int oldparent, parentdifferent, doingdirectory; 90710051Ssam int error = 0; 9087701Ssam 9099167Ssam uap = (struct a *)u.u_ap; 91011641Ssam ip = namei(uchar, DELETE | LOCKPARENT, 0); 9119167Ssam if (ip == NULL) 9129167Ssam return; 9139167Ssam dp = u.u_pdir; 9149167Ssam oldparent = 0, doingdirectory = 0; 9159167Ssam if ((ip->i_mode&IFMT) == IFDIR) { 9169167Ssam register struct direct *d; 9179167Ssam 9189167Ssam d = &u.u_dent; 9199167Ssam /* 92011641Ssam * Avoid ".", "..", and aliases of "." for obvious reasons. 9219167Ssam */ 92211641Ssam if ((d->d_namlen == 1 && d->d_name[0] == '.') || 92311641Ssam (d->d_namlen == 2 && bcmp(d->d_name, "..", 2) == 0) || 92411641Ssam (dp == ip)) { 92511641Ssam iput(dp); 92611641Ssam if (dp == ip) 92711641Ssam irele(ip); 92811641Ssam else 92910051Ssam iput(ip); 93011641Ssam u.u_error = EINVAL; 93111641Ssam return; 9329167Ssam } 9339167Ssam oldparent = dp->i_number; 9349167Ssam doingdirectory++; 9359167Ssam } 93611641Ssam iput(dp); 9379167Ssam 9389167Ssam /* 9399167Ssam * 1) Bump link count while we're moving stuff 9409167Ssam * around. If we crash somewhere before 9419167Ssam * completing our work, the link count 9429167Ssam * may be wrong, but correctable. 9439167Ssam */ 9449167Ssam ip->i_nlink++; 9459167Ssam ip->i_flag |= ICHG; 9469167Ssam iupdat(ip, &time, &time, 1); 9479167Ssam iunlock(ip); 9489167Ssam 9499167Ssam /* 9509167Ssam * When the target exists, both the directory 9519167Ssam * and target inodes are returned locked. 9529167Ssam */ 9539167Ssam u.u_dirp = (caddr_t)uap->to; 9549167Ssam xp = namei(uchar, CREATE | LOCKPARENT, 0); 95510051Ssam if (u.u_error) { 95610051Ssam error = u.u_error; 9579167Ssam goto out; 95810051Ssam } 9599167Ssam dp = u.u_pdir; 9609167Ssam /* 96111641Ssam * If ".." must be changed (ie the directory gets a new 96211641Ssam * parent) then the user must have write permission. 96311641Ssam */ 96411641Ssam parentdifferent = oldparent != dp->i_number; 96511643Ssam if (doingdirectory && parentdifferent && access(ip, IWRITE)) 96611641Ssam goto bad; 96711641Ssam /* 9689167Ssam * 2) If target doesn't exist, link the target 9699167Ssam * to the source and unlink the source. 9709167Ssam * Otherwise, rewrite the target directory 9719167Ssam * entry to reference the source inode and 9729167Ssam * expunge the original entry's existence. 9739167Ssam */ 9749167Ssam if (xp == NULL) { 9759167Ssam if (dp->i_dev != ip->i_dev) { 97610051Ssam error = EXDEV; 9779167Ssam goto bad; 9789167Ssam } 9799167Ssam /* 98010590Ssam * Disallow rename(foo, foo/bar). 98110590Ssam */ 98210590Ssam if (dp->i_number == ip->i_number) { 98310590Ssam error = EEXIST; 98410590Ssam goto bad; 98510590Ssam } 98610590Ssam /* 9879167Ssam * Account for ".." in directory. 9889167Ssam * When source and destination have the 9899167Ssam * same parent we don't fool with the 9909167Ssam * link count -- this isn't required 9919167Ssam * because we do a similar check below. 9929167Ssam */ 9939167Ssam if (doingdirectory && parentdifferent) { 9949167Ssam dp->i_nlink++; 9959167Ssam dp->i_flag |= ICHG; 9969167Ssam iupdat(dp, &time, &time, 1); 9979167Ssam } 99810850Ssam error = direnter(ip); 99910850Ssam if (error) 10009167Ssam goto out; 10019167Ssam } else { 10029167Ssam if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) { 100310051Ssam error = EXDEV; 10049167Ssam goto bad; 10059167Ssam } 10069167Ssam /* 100710590Ssam * Short circuit rename(foo, foo). 100810590Ssam */ 100910590Ssam if (xp->i_number == ip->i_number) 101010590Ssam goto bad; 101110590Ssam /* 101210051Ssam * Target must be empty if a directory 101310051Ssam * and have no links to it. 10149167Ssam * Also, insure source and target are 10159167Ssam * compatible (both directories, or both 10169167Ssam * not directories). 10179167Ssam */ 10189167Ssam if ((xp->i_mode&IFMT) == IFDIR) { 101910051Ssam if (!dirempty(xp) || xp->i_nlink > 2) { 102010051Ssam error = ENOTEMPTY; 10219167Ssam goto bad; 10229167Ssam } 10239167Ssam if (!doingdirectory) { 102410051Ssam error = ENOTDIR; 10259167Ssam goto bad; 10269167Ssam } 10279167Ssam } else if (doingdirectory) { 102810051Ssam error = EISDIR; 10299167Ssam goto bad; 10309167Ssam } 10319167Ssam dirrewrite(dp, ip); 103210051Ssam if (u.u_error) { 103310051Ssam error = u.u_error; 10349167Ssam goto bad1; 103510051Ssam } 10369167Ssam /* 103710051Ssam * Adjust the link count of the target to 103810051Ssam * reflect the dirrewrite above. If this is 103910051Ssam * a directory it is empty and there are 104010051Ssam * no links to it, so we can squash the inode and 104110051Ssam * any space associated with it. We disallowed 104210051Ssam * renaming over top of a directory with links to 104310051Ssam * it above, as we've no way to determine if 104410051Ssam * we've got a link or the directory itself, and 104510051Ssam * if we get a link, then ".." will be screwed up. 10469167Ssam */ 104710051Ssam xp->i_nlink--; 10489167Ssam if (doingdirectory) { 104910051Ssam if (--xp->i_nlink != 0) 105010051Ssam panic("rename: linked directory"); 10519167Ssam itrunc(xp, (u_long)0); 105210051Ssam } 10539167Ssam xp->i_flag |= ICHG; 10549167Ssam iput(xp); 105510246Ssam xp = NULL; 10569167Ssam } 10579167Ssam 10589167Ssam /* 10599167Ssam * 3) Unlink the source. 10609167Ssam */ 10619167Ssam u.u_dirp = uap->from; 10629167Ssam dp = namei(uchar, DELETE, 0); 10639167Ssam /* 10649167Ssam * Insure directory entry still exists and 10659167Ssam * has not changed since the start of all 10669167Ssam * this. If either has occured, forget about 10679167Ssam * about deleting the original entry and just 10689167Ssam * adjust the link count in the inode. 10699167Ssam */ 10709167Ssam if (dp == NULL || u.u_dent.d_ino != ip->i_number) { 10719167Ssam ip->i_nlink--; 10729167Ssam ip->i_flag |= ICHG; 10739167Ssam } else { 10749167Ssam /* 10759167Ssam * If source is a directory, must adjust 10769167Ssam * link count of parent directory also. 10779167Ssam * If target didn't exist and source and 10789167Ssam * target have the same parent, then we 10799167Ssam * needn't touch the link count, it all 10809167Ssam * balances out in the end. Otherwise, we 10819167Ssam * must do so to reflect deletion of ".." 10829167Ssam * done above. 10839167Ssam */ 10849167Ssam if (doingdirectory && (xp != NULL || parentdifferent)) { 10859167Ssam dp->i_nlink--; 10869167Ssam dp->i_flag |= ICHG; 10879167Ssam } 10889167Ssam if (dirremove()) { 10899167Ssam ip->i_nlink--; 10909167Ssam ip->i_flag |= ICHG; 10919167Ssam } 109210051Ssam if (error == 0) /* conservative */ 109310051Ssam error = u.u_error; 10949167Ssam } 10959167Ssam irele(ip); 10969167Ssam if (dp) 10979167Ssam iput(dp); 10989167Ssam 10999167Ssam /* 11009167Ssam * 4) Renaming a directory with the parent 11019167Ssam * different requires ".." to be rewritten. 11029167Ssam * The window is still there for ".." to 11039167Ssam * be inconsistent, but this is unavoidable, 11049167Ssam * and a lot shorter than when it was done 11059167Ssam * in a user process. 11069167Ssam */ 110710051Ssam if (doingdirectory && parentdifferent && error == 0) { 11089167Ssam struct dirtemplate dirbuf; 11099167Ssam 11109167Ssam u.u_dirp = uap->to; 11119167Ssam ip = namei(uchar, LOOKUP | LOCKPARENT, 0); 11129167Ssam if (ip == NULL) { 11139167Ssam printf("rename: .. went away\n"); 11149167Ssam return; 11159167Ssam } 11169167Ssam dp = u.u_pdir; 11179167Ssam if ((ip->i_mode&IFMT) != IFDIR) { 11189167Ssam printf("rename: .. not a directory\n"); 11199167Ssam goto stuck; 11209167Ssam } 112110051Ssam error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf, 11229167Ssam sizeof (struct dirtemplate), (off_t)0, 1, (int *)0); 112310051Ssam if (error == 0) { 11249167Ssam dirbuf.dotdot_ino = dp->i_number; 11259167Ssam (void) rdwri(UIO_WRITE, ip, (caddr_t)&dirbuf, 11269167Ssam sizeof (struct dirtemplate), (off_t)0, 1, (int *)0); 11279167Ssam } 11289167Ssam stuck: 11299167Ssam irele(dp); 11309167Ssam iput(ip); 11319167Ssam } 113210051Ssam goto done; 113310051Ssam 11349167Ssam bad: 113510246Ssam iput(dp); 11369167Ssam bad1: 11379167Ssam if (xp) 113810246Ssam iput(xp); 11399167Ssam out: 11409167Ssam ip->i_nlink--; 11419167Ssam ip->i_flag |= ICHG; 11429167Ssam irele(ip); 114310051Ssam done: 114410051Ssam if (error) 114510051Ssam u.u_error = error; 11467701Ssam } 11477701Ssam 11487535Sroot /* 11497535Sroot * Make a new file. 11507535Sroot */ 11517535Sroot struct inode * 11527535Sroot maknode(mode) 11537535Sroot int mode; 11547535Sroot { 11557535Sroot register struct inode *ip; 11567535Sroot ino_t ipref; 11577535Sroot 11587535Sroot if ((mode & IFMT) == IFDIR) 11597535Sroot ipref = dirpref(u.u_pdir->i_fs); 11607535Sroot else 11617535Sroot ipref = u.u_pdir->i_number; 11627535Sroot ip = ialloc(u.u_pdir, ipref, mode); 11637535Sroot if (ip == NULL) { 11647535Sroot iput(u.u_pdir); 11657701Ssam return (NULL); 11667535Sroot } 11677701Ssam #ifdef QUOTA 11687535Sroot if (ip->i_dquot != NODQUOT) 11697535Sroot panic("maknode: dquot"); 11707535Sroot #endif 11717535Sroot ip->i_flag |= IACC|IUPD|ICHG; 11727535Sroot if ((mode & IFMT) == 0) 11737535Sroot mode |= IFREG; 11747535Sroot ip->i_mode = mode & ~u.u_cmask; 11757535Sroot ip->i_nlink = 1; 11767535Sroot ip->i_uid = u.u_uid; 11777535Sroot ip->i_gid = u.u_pdir->i_gid; 117811811Ssam if (ip->i_mode & ISGID && !groupmember(ip->i_gid)) 117911811Ssam ip->i_mode &= ~ISGID; 11807701Ssam #ifdef QUOTA 11817535Sroot ip->i_dquot = inoquota(ip); 11827535Sroot #endif 11837535Sroot 11847535Sroot /* 11857535Sroot * Make sure inode goes to disk before directory entry. 11867535Sroot */ 11878673Sroot iupdat(ip, &time, &time, 1); 118810850Ssam u.u_error = direnter(ip); 11897535Sroot if (u.u_error) { 11907535Sroot /* 119110850Ssam * Write error occurred trying to update directory 119210850Ssam * so must deallocate the inode. 11937535Sroot */ 11947535Sroot ip->i_nlink = 0; 11957535Sroot ip->i_flag |= ICHG; 11967535Sroot iput(ip); 11977701Ssam return (NULL); 11987535Sroot } 11997701Ssam return (ip); 12007535Sroot } 1201