| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * Copyright (c) 1982, 1986, 1989, 1993, 1995 | |
| 3 | * The Regents of the University of California. All rights reserved. | |
| 4 | * (c) UNIX System Laboratories, Inc. | |
| 5 | * All or some portions of this file are derived from material licensed | |
| 6 | * to the University of California by American Telephone and Telegraph | |
| 7 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with | |
| 8 | * the permission of UNIX System Laboratories, Inc. | |
| 9 | * | |
| 10 | * Redistribution and use in source and binary forms, with or without | |
| 11 | * modification, are permitted provided that the following conditions | |
| 12 | * are met: | |
| 13 | * 1. Redistributions of source code must retain the above copyright | |
| 14 | * notice, this list of conditions and the following disclaimer. | |
| 15 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 16 | * notice, this list of conditions and the following disclaimer in the | |
| 17 | * documentation and/or other materials provided with the distribution. | |
| 18 | * 3. All advertising materials mentioning features or use of this software | |
| 19 | * must display the following acknowledgement: | |
| 20 | * This product includes software developed by the University of | |
| 21 | * California, Berkeley and its contributors. | |
| 22 | * 4. Neither the name of the University nor the names of its contributors | |
| 23 | * may be used to endorse or promote products derived from this software | |
| 24 | * without specific prior written permission. | |
| 25 | * | |
| 26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
| 27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
| 30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 36 | * SUCH DAMAGE. | |
| 37 | * | |
| 38 | * @(#)ufs_vnops.c 8.27 (Berkeley) 5/27/95 | |
| 39 | * $FreeBSD: src/sys/ufs/ufs/ufs_vnops.c,v 1.131.2.8 2003/01/02 17:26:19 bde Exp $ | |
| 64950f31 | 40 | * $DragonFly: src/sys/vfs/ufs/ufs_vnops.c,v 1.67 2008/09/28 05:04:22 dillon Exp $ |
| 984263bc MD |
41 | */ |
| 42 | ||
| 43 | #include "opt_quota.h" | |
| 44 | #include "opt_suiddir.h" | |
| 45 | #include "opt_ufs.h" | |
| 46 | ||
| 47 | #include <sys/param.h> | |
| 48 | #include <sys/systm.h> | |
| 984263bc MD |
49 | #include <sys/kernel.h> |
| 50 | #include <sys/fcntl.h> | |
| 51 | #include <sys/stat.h> | |
| 52 | #include <sys/buf.h> | |
| 53 | #include <sys/proc.h> | |
| 895c1f85 | 54 | #include <sys/priv.h> |
| dadab5e9 | 55 | #include <sys/namei.h> |
| 984263bc MD |
56 | #include <sys/mount.h> |
| 57 | #include <sys/unistd.h> | |
| 58 | #include <sys/vnode.h> | |
| 59 | #include <sys/malloc.h> | |
| 60 | #include <sys/dirent.h> | |
| 61 | #include <sys/lockf.h> | |
| 62 | #include <sys/event.h> | |
| 63 | #include <sys/conf.h> | |
| 64 | ||
| 65 | #include <sys/file.h> /* XXX */ | |
| 21c8b4bd | 66 | #include <sys/jail.h> |
| 984263bc MD |
67 | |
| 68 | #include <vm/vm.h> | |
| 69 | #include <vm/vm_extern.h> | |
| 70 | ||
| 1f2de5d4 | 71 | #include <vfs/fifofs/fifo.h> |
| 984263bc | 72 | |
| 1f2de5d4 MD |
73 | #include "quota.h" |
| 74 | #include "inode.h" | |
| 75 | #include "dir.h" | |
| 76 | #include "ufsmount.h" | |
| 77 | #include "ufs_extern.h" | |
| ac690a1d | 78 | #include "ffs_extern.h" |
| 984263bc | 79 | #ifdef UFS_DIRHASH |
| 1f2de5d4 | 80 | #include "dirhash.h" |
| 984263bc MD |
81 | #endif |
| 82 | ||
| a6ee311a RG |
83 | static int ufs_access (struct vop_access_args *); |
| 84 | static int ufs_advlock (struct vop_advlock_args *); | |
| 87de5057 MD |
85 | static int ufs_chmod (struct vnode *, int, struct ucred *); |
| 86 | static int ufs_chown (struct vnode *, uid_t, gid_t, struct ucred *); | |
| a6ee311a | 87 | static int ufs_close (struct vop_close_args *); |
| e62afb5f | 88 | static int ufs_create (struct vop_old_create_args *); |
| a6ee311a | 89 | static int ufs_getattr (struct vop_getattr_args *); |
| e62afb5f | 90 | static int ufs_link (struct vop_old_link_args *); |
| a6ee311a | 91 | static int ufs_makeinode (int mode, struct vnode *, struct vnode **, struct componentname *); |
| 349433c9 | 92 | static int ufs_markatime (struct vop_markatime_args *); |
| a6ee311a | 93 | static int ufs_missingop (struct vop_generic_args *ap); |
| e62afb5f MD |
94 | static int ufs_mkdir (struct vop_old_mkdir_args *); |
| 95 | static int ufs_mknod (struct vop_old_mknod_args *); | |
| a6ee311a | 96 | static int ufs_mmap (struct vop_mmap_args *); |
| a6ee311a RG |
97 | static int ufs_print (struct vop_print_args *); |
| 98 | static int ufs_readdir (struct vop_readdir_args *); | |
| 99 | static int ufs_readlink (struct vop_readlink_args *); | |
| e62afb5f MD |
100 | static int ufs_remove (struct vop_old_remove_args *); |
| 101 | static int ufs_rename (struct vop_old_rename_args *); | |
| 102 | static int ufs_rmdir (struct vop_old_rmdir_args *); | |
| a6ee311a RG |
103 | static int ufs_setattr (struct vop_setattr_args *); |
| 104 | static int ufs_strategy (struct vop_strategy_args *); | |
| e62afb5f MD |
105 | static int ufs_symlink (struct vop_old_symlink_args *); |
| 106 | static int ufs_whiteout (struct vop_old_whiteout_args *); | |
| a6ee311a RG |
107 | static int ufsfifo_close (struct vop_close_args *); |
| 108 | static int ufsfifo_kqfilter (struct vop_kqfilter_args *); | |
| 109 | static int ufsfifo_read (struct vop_read_args *); | |
| 110 | static int ufsfifo_write (struct vop_write_args *); | |
| 111 | static int ufsspec_close (struct vop_close_args *); | |
| 112 | static int ufsspec_read (struct vop_read_args *); | |
| 113 | static int ufsspec_write (struct vop_write_args *); | |
| 114 | static int filt_ufsread (struct knote *kn, long hint); | |
| 115 | static int filt_ufswrite (struct knote *kn, long hint); | |
| 116 | static int filt_ufsvnode (struct knote *kn, long hint); | |
| 117 | static void filt_ufsdetach (struct knote *kn); | |
| 118 | static int ufs_kqfilter (struct vop_kqfilter_args *ap); | |
| 984263bc MD |
119 | |
| 120 | union _qcvt { | |
| 121 | int64_t qcvt; | |
| 122 | int32_t val[2]; | |
| 123 | }; | |
| 124 | #define SETHIGH(q, h) { \ | |
| 125 | union _qcvt tmp; \ | |
| 126 | tmp.qcvt = (q); \ | |
| 127 | tmp.val[_QUAD_HIGHWORD] = (h); \ | |
| 128 | (q) = tmp.qcvt; \ | |
| 129 | } | |
| 130 | #define SETLOW(q, l) { \ | |
| 131 | union _qcvt tmp; \ | |
| 132 | tmp.qcvt = (q); \ | |
| 133 | tmp.val[_QUAD_LOWWORD] = (l); \ | |
| 134 | (q) = tmp.qcvt; \ | |
| 135 | } | |
| 136 | #define VN_KNOTE(vp, b) \ | |
| 137 | KNOTE(&vp->v_pollinfo.vpi_selinfo.si_note, (b)) | |
| 138 | ||
| e088dc32 JS |
139 | #define OFSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0) |
| 140 | ||
| 984263bc MD |
141 | /* |
| 142 | * A virgin directory (no blushing please). | |
| 143 | */ | |
| 144 | static struct dirtemplate mastertemplate = { | |
| 145 | 0, 12, DT_DIR, 1, ".", | |
| 146 | 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." | |
| 147 | }; | |
| 148 | static struct odirtemplate omastertemplate = { | |
| 149 | 0, 12, 1, ".", | |
| 150 | 0, DIRBLKSIZ - 12, 2, ".." | |
| 151 | }; | |
| 152 | ||
| 153 | void | |
| 0973c589 | 154 | ufs_itimes(struct vnode *vp) |
| 984263bc MD |
155 | { |
| 156 | struct inode *ip; | |
| 157 | struct timespec ts; | |
| 158 | ||
| 159 | ip = VTOI(vp); | |
| 160 | if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0) | |
| 161 | return; | |
| 162 | if ((vp->v_type == VBLK || vp->v_type == VCHR) && !DOINGSOFTDEP(vp)) | |
| 163 | ip->i_flag |= IN_LAZYMOD; | |
| 164 | else | |
| 165 | ip->i_flag |= IN_MODIFIED; | |
| 166 | if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { | |
| 167 | vfs_timestamp(&ts); | |
| 168 | if (ip->i_flag & IN_ACCESS) { | |
| 169 | ip->i_atime = ts.tv_sec; | |
| 170 | ip->i_atimensec = ts.tv_nsec; | |
| 171 | } | |
| 172 | if (ip->i_flag & IN_UPDATE) { | |
| 173 | ip->i_mtime = ts.tv_sec; | |
| 174 | ip->i_mtimensec = ts.tv_nsec; | |
| 175 | ip->i_modrev++; | |
| 176 | } | |
| 177 | if (ip->i_flag & IN_CHANGE) { | |
| 178 | ip->i_ctime = ts.tv_sec; | |
| 179 | ip->i_ctimensec = ts.tv_nsec; | |
| 180 | } | |
| 181 | } | |
| 182 | ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE); | |
| 183 | } | |
| 184 | ||
| 185 | /* | |
| 186 | * Create a regular file | |
| 0973c589 CP |
187 | * |
| 188 | * ufs_create(struct vnode *a_dvp, struct vnode **a_vpp, | |
| 189 | * struct componentname *a_cnp, struct vattr *a_vap) | |
| 984263bc | 190 | */ |
| 0961aa92 | 191 | static |
| 984263bc | 192 | int |
| e62afb5f | 193 | ufs_create(struct vop_old_create_args *ap) |
| 984263bc MD |
194 | { |
| 195 | int error; | |
| 196 | ||
| 197 | error = | |
| 198 | ufs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode), | |
| 199 | ap->a_dvp, ap->a_vpp, ap->a_cnp); | |
| 200 | if (error) | |
| 201 | return (error); | |
| 202 | VN_KNOTE(ap->a_dvp, NOTE_WRITE); | |
| 203 | return (0); | |
| 204 | } | |
| 205 | ||
| 206 | /* | |
| 207 | * Mknod vnode call | |
| 0973c589 CP |
208 | * |
| 209 | * ufs_mknod(struct vnode *a_dvp, struct vnode **a_vpp, | |
| 210 | * struct componentname *a_cnp, struct vattr *a_vap) | |
| 984263bc MD |
211 | */ |
| 212 | /* ARGSUSED */ | |
| 0961aa92 | 213 | static |
| 984263bc | 214 | int |
| e62afb5f | 215 | ufs_mknod(struct vop_old_mknod_args *ap) |
| 984263bc MD |
216 | { |
| 217 | struct vattr *vap = ap->a_vap; | |
| 218 | struct vnode **vpp = ap->a_vpp; | |
| 219 | struct inode *ip; | |
| 220 | ino_t ino; | |
| 221 | int error; | |
| 222 | ||
| 0e9b9130 MD |
223 | /* |
| 224 | * UFS cannot represent the entire major/minor range supported by | |
| 225 | * the kernel. | |
| 226 | */ | |
| 227 | if (vap->va_rmajor != VNOVAL && | |
| 228 | makeudev(vap->va_rmajor, vap->va_rminor) == NOUDEV) { | |
| 229 | return(EINVAL); | |
| 230 | } | |
| 231 | ||
| c0515afc MD |
232 | /* no special directory support */ |
| 233 | if (vap->va_type == VDIR) | |
| 234 | return(EINVAL); | |
| 235 | ||
| 984263bc MD |
236 | error = ufs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode), |
| 237 | ap->a_dvp, vpp, ap->a_cnp); | |
| 238 | if (error) | |
| 239 | return (error); | |
| 240 | VN_KNOTE(ap->a_dvp, NOTE_WRITE); | |
| 241 | ip = VTOI(*vpp); | |
| 242 | ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; | |
| 0e9b9130 | 243 | if (vap->va_rmajor != VNOVAL) { |
| 984263bc MD |
244 | /* |
| 245 | * Want to be able to use this to make badblock | |
| 246 | * inodes, so don't truncate the dev number. | |
| 247 | */ | |
| 0e9b9130 | 248 | ip->i_rdev = makeudev(vap->va_rmajor, vap->va_rminor); |
| 984263bc MD |
249 | } |
| 250 | /* | |
| 251 | * Remove inode, then reload it through VFS_VGET so it is | |
| 252 | * checked to see if it is an alias of an existing entry in | |
| 253 | * the inode cache. | |
| 254 | */ | |
| 984263bc MD |
255 | (*vpp)->v_type = VNON; |
| 256 | ino = ip->i_number; /* Save this before vgone() invalidates ip. */ | |
| 3c37c940 | 257 | vgone_vxlocked(*vpp); |
| 5fd012e0 | 258 | vput(*vpp); |
| 984263bc MD |
259 | error = VFS_VGET(ap->a_dvp->v_mount, ino, vpp); |
| 260 | if (error) { | |
| 261 | *vpp = NULL; | |
| 262 | return (error); | |
| 263 | } | |
| 264 | return (0); | |
| 265 | } | |
| 266 | ||
| 267 | /* | |
| 984263bc MD |
268 | * Close called. |
| 269 | * | |
| 270 | * Update the times on the inode. | |
| 0973c589 | 271 | * |
| b478fdce | 272 | * ufs_close(struct vnode *a_vp, int a_fflag) |
| 984263bc MD |
273 | */ |
| 274 | /* ARGSUSED */ | |
| 0961aa92 | 275 | static |
| 984263bc | 276 | int |
| 0973c589 | 277 | ufs_close(struct vop_close_args *ap) |
| 984263bc | 278 | { |
| 3ff2135f | 279 | struct vnode *vp = ap->a_vp; |
| 984263bc | 280 | |
| 3c37c940 | 281 | if (vp->v_sysref.refcnt > 1) |
| 984263bc | 282 | ufs_itimes(vp); |
| 8ddc6004 | 283 | return (vop_stdclose(ap)); |
| 984263bc MD |
284 | } |
| 285 | ||
| 0973c589 | 286 | /* |
| b478fdce | 287 | * ufs_access(struct vnode *a_vp, int a_mode, struct ucred *a_cred) |
| 0973c589 | 288 | */ |
| 0961aa92 | 289 | static |
| 984263bc | 290 | int |
| 0973c589 | 291 | ufs_access(struct vop_access_args *ap) |
| 984263bc MD |
292 | { |
| 293 | struct vnode *vp = ap->a_vp; | |
| 294 | struct inode *ip = VTOI(vp); | |
| 295 | struct ucred *cred = ap->a_cred; | |
| 296 | mode_t mask, mode = ap->a_mode; | |
| 3ff2135f | 297 | gid_t *gp; |
| 984263bc MD |
298 | int i; |
| 299 | #ifdef QUOTA | |
| 300 | int error; | |
| 301 | #endif | |
| 302 | ||
| 303 | /* | |
| f719c866 | 304 | * Disallow write attempts on read-only filesystems; |
| 984263bc | 305 | * unless the file is a socket, fifo, or a block or |
| f719c866 | 306 | * character device resident on the filesystem. |
| 984263bc MD |
307 | */ |
| 308 | if (mode & VWRITE) { | |
| 309 | switch (vp->v_type) { | |
| 310 | case VDIR: | |
| 311 | case VLNK: | |
| 312 | case VREG: | |
| 313 | if (vp->v_mount->mnt_flag & MNT_RDONLY) | |
| 314 | return (EROFS); | |
| 315 | #ifdef QUOTA | |
| 50e58362 | 316 | if ((error = ufs_getinoquota(ip)) != 0) |
| 984263bc MD |
317 | return (error); |
| 318 | #endif | |
| 319 | break; | |
| 320 | default: | |
| 321 | break; | |
| 322 | } | |
| 323 | } | |
| 324 | ||
| 3a907475 | 325 | #if 0 /* handled by kernel now */ |
| 984263bc MD |
326 | /* If immutable bit set, nobody gets to write it. */ |
| 327 | if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE)) | |
| 328 | return (EPERM); | |
| 3a907475 | 329 | #endif |
| 984263bc MD |
330 | |
| 331 | /* Otherwise, user id 0 always gets access. */ | |
| 332 | if (cred->cr_uid == 0) | |
| 333 | return (0); | |
| 334 | ||
| 335 | mask = 0; | |
| 336 | ||
| 337 | /* Otherwise, check the owner. */ | |
| 338 | if (cred->cr_uid == ip->i_uid) { | |
| 339 | if (mode & VEXEC) | |
| 340 | mask |= S_IXUSR; | |
| 341 | if (mode & VREAD) | |
| 342 | mask |= S_IRUSR; | |
| 343 | if (mode & VWRITE) | |
| 344 | mask |= S_IWUSR; | |
| 345 | return ((ip->i_mode & mask) == mask ? 0 : EACCES); | |
| 346 | } | |
| 347 | ||
| 348 | /* Otherwise, check the groups. */ | |
| 349 | for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) | |
| 350 | if (ip->i_gid == *gp) { | |
| 351 | if (mode & VEXEC) | |
| 352 | mask |= S_IXGRP; | |
| 353 | if (mode & VREAD) | |
| 354 | mask |= S_IRGRP; | |
| 355 | if (mode & VWRITE) | |
| 356 | mask |= S_IWGRP; | |
| 357 | return ((ip->i_mode & mask) == mask ? 0 : EACCES); | |
| 358 | } | |
| 359 | ||
| 360 | /* Otherwise, check everyone else. */ | |
| 361 | if (mode & VEXEC) | |
| 362 | mask |= S_IXOTH; | |
| 363 | if (mode & VREAD) | |
| 364 | mask |= S_IROTH; | |
| 365 | if (mode & VWRITE) | |
| 366 | mask |= S_IWOTH; | |
| 367 | return ((ip->i_mode & mask) == mask ? 0 : EACCES); | |
| 368 | } | |
| 369 | ||
| 0973c589 | 370 | /* |
| b478fdce | 371 | * ufs_getattr(struct vnode *a_vp, struct vattr *a_vap) |
| 0973c589 | 372 | */ |
| 984263bc | 373 | /* ARGSUSED */ |
| 0961aa92 | 374 | static |
| 984263bc | 375 | int |
| 0973c589 | 376 | ufs_getattr(struct vop_getattr_args *ap) |
| 984263bc | 377 | { |
| 3ff2135f RG |
378 | struct vnode *vp = ap->a_vp; |
| 379 | struct inode *ip = VTOI(vp); | |
| 380 | struct vattr *vap = ap->a_vap; | |
| 984263bc | 381 | |
| dc1be39c MD |
382 | /* |
| 383 | * This may cause i_fsmid to be updated even if no change (0) | |
| 384 | * is returned, but we should only write out the inode if non-zero | |
| 385 | * is returned and if the mount is read-write. | |
| 386 | */ | |
| 387 | if (cache_check_fsmid_vp(vp, &ip->i_fsmid) && | |
| 388 | (vp->v_mount->mnt_flag & MNT_RDONLY) == 0 | |
| 389 | ) { | |
| 390 | ip->i_flag |= IN_LAZYMOD; | |
| 391 | } | |
| 392 | ||
| 984263bc MD |
393 | ufs_itimes(vp); |
| 394 | /* | |
| 395 | * Copy from inode table | |
| 396 | */ | |
| 397 | vap->va_fsid = dev2udev(ip->i_dev); | |
| 398 | vap->va_fileid = ip->i_number; | |
| 399 | vap->va_mode = ip->i_mode & ~IFMT; | |
| 400 | vap->va_nlink = VFSTOUFS(vp->v_mount)->um_i_effnlink_valid ? | |
| 401 | ip->i_effnlink : ip->i_nlink; | |
| 402 | vap->va_uid = ip->i_uid; | |
| 403 | vap->va_gid = ip->i_gid; | |
| 0e9b9130 MD |
404 | vap->va_rmajor = umajor(ip->i_rdev); |
| 405 | vap->va_rminor = uminor(ip->i_rdev); | |
| 984263bc MD |
406 | vap->va_size = ip->i_din.di_size; |
| 407 | vap->va_atime.tv_sec = ip->i_atime; | |
| 408 | vap->va_atime.tv_nsec = ip->i_atimensec; | |
| 409 | vap->va_mtime.tv_sec = ip->i_mtime; | |
| 410 | vap->va_mtime.tv_nsec = ip->i_mtimensec; | |
| 411 | vap->va_ctime.tv_sec = ip->i_ctime; | |
| 412 | vap->va_ctime.tv_nsec = ip->i_ctimensec; | |
| 413 | vap->va_flags = ip->i_flags; | |
| 414 | vap->va_gen = ip->i_gen; | |
| 415 | vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; | |
| 416 | vap->va_bytes = dbtob((u_quad_t)ip->i_blocks); | |
| 417 | vap->va_type = IFTOVT(ip->i_mode); | |
| 418 | vap->va_filerev = ip->i_modrev; | |
| dc1be39c | 419 | vap->va_fsmid = ip->i_fsmid; |
| 984263bc MD |
420 | return (0); |
| 421 | } | |
| 422 | ||
| 349433c9 MD |
423 | static |
| 424 | int | |
| 425 | ufs_markatime(struct vop_markatime_args *ap) | |
| 426 | { | |
| 427 | struct vnode *vp = ap->a_vp; | |
| 428 | struct inode *ip = VTOI(vp); | |
| 429 | ||
| 430 | if (vp->v_mount->mnt_flag & MNT_RDONLY) | |
| 431 | return (EROFS); | |
| 432 | if (vp->v_mount->mnt_flag & MNT_NOATIME) | |
| 433 | return (0); | |
| 434 | ip->i_flag |= IN_ACCESS; | |
| 435 | VN_KNOTE(vp, NOTE_ATTRIB); | |
| 436 | return (0); | |
| 437 | } | |
| 438 | ||
| 984263bc MD |
439 | /* |
| 440 | * Set attribute vnode op. called from several syscalls | |
| 0973c589 CP |
441 | * |
| 442 | * ufs_setattr(struct vnode *a_vp, struct vattr *a_vap, | |
| b478fdce | 443 | * struct ucred *a_cred) |
| 984263bc | 444 | */ |
| 0961aa92 | 445 | static |
| 984263bc | 446 | int |
| 0973c589 | 447 | ufs_setattr(struct vop_setattr_args *ap) |
| 984263bc MD |
448 | { |
| 449 | struct vattr *vap = ap->a_vap; | |
| 450 | struct vnode *vp = ap->a_vp; | |
| 451 | struct inode *ip = VTOI(vp); | |
| 452 | struct ucred *cred = ap->a_cred; | |
| 984263bc MD |
453 | int error; |
| 454 | ||
| 455 | /* | |
| 456 | * Check for unsettable attributes. | |
| 457 | */ | |
| 458 | if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || | |
| 459 | (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || | |
| 0e9b9130 | 460 | (vap->va_blocksize != VNOVAL) || (vap->va_rmajor != VNOVAL) || |
| 984263bc MD |
461 | ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { |
| 462 | return (EINVAL); | |
| 463 | } | |
| 464 | if (vap->va_flags != VNOVAL) { | |
| 465 | if (vp->v_mount->mnt_flag & MNT_RDONLY) | |
| 466 | return (EROFS); | |
| 467 | if (cred->cr_uid != ip->i_uid && | |
| 895c1f85 | 468 | (error = priv_check_cred(cred, PRIV_ROOT, PRISON_ROOT))) |
| 984263bc | 469 | return (error); |
| 21c8b4bd MD |
470 | /* |
| 471 | * Note that a root chflags becomes a user chflags when | |
| 472 | * we are jailed, unless the jail.chflags_allowed sysctl | |
| 473 | * is set. | |
| 474 | */ | |
| 475 | if (cred->cr_uid == 0 && | |
| 476 | (!jailed(cred) || jail_chflags_allowed)) { | |
| 984263bc MD |
477 | if ((ip->i_flags |
| 478 | & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) && | |
| 479 | securelevel > 0) | |
| 480 | return (EPERM); | |
| 481 | ip->i_flags = vap->va_flags; | |
| 482 | } else { | |
| 483 | if (ip->i_flags | |
| 484 | & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) || | |
| 485 | (vap->va_flags & UF_SETTABLE) != vap->va_flags) | |
| 486 | return (EPERM); | |
| 487 | ip->i_flags &= SF_SETTABLE; | |
| 488 | ip->i_flags |= (vap->va_flags & UF_SETTABLE); | |
| 489 | } | |
| 490 | ip->i_flag |= IN_CHANGE; | |
| 491 | if (vap->va_flags & (IMMUTABLE | APPEND)) | |
| 492 | return (0); | |
| 493 | } | |
| 494 | if (ip->i_flags & (IMMUTABLE | APPEND)) | |
| 495 | return (EPERM); | |
| 496 | /* | |
| 497 | * Go through the fields and update iff not VNOVAL. | |
| 498 | */ | |
| 499 | if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { | |
| 500 | if (vp->v_mount->mnt_flag & MNT_RDONLY) | |
| 501 | return (EROFS); | |
| 87de5057 | 502 | if ((error = ufs_chown(vp, vap->va_uid, vap->va_gid, cred)) != 0) |
| 984263bc MD |
503 | return (error); |
| 504 | } | |
| 505 | if (vap->va_size != VNOVAL) { | |
| 506 | /* | |
| f719c866 | 507 | * Disallow write attempts on read-only filesystems; |
| 984263bc | 508 | * unless the file is a socket, fifo, or a block or |
| f719c866 | 509 | * character device resident on the filesystem. |
| 984263bc MD |
510 | */ |
| 511 | switch (vp->v_type) { | |
| 512 | case VDIR: | |
| 513 | return (EISDIR); | |
| 514 | case VLNK: | |
| 515 | case VREG: | |
| 516 | if (vp->v_mount->mnt_flag & MNT_RDONLY) | |
| 517 | return (EROFS); | |
| 518 | break; | |
| 519 | default: | |
| 520 | break; | |
| 521 | } | |
| ac690a1d | 522 | if ((error = ffs_truncate(vp, vap->va_size, 0, cred)) != 0) |
| 984263bc MD |
523 | return (error); |
| 524 | } | |
| 525 | ip = VTOI(vp); | |
| 526 | if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { | |
| 527 | if (vp->v_mount->mnt_flag & MNT_RDONLY) | |
| 528 | return (EROFS); | |
| 529 | if (cred->cr_uid != ip->i_uid && | |
| 895c1f85 | 530 | (error = priv_check_cred(cred, PRIV_ROOT, PRISON_ROOT)) && |
| 984263bc | 531 | ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || |
| 87de5057 | 532 | (error = VOP_ACCESS(vp, VWRITE, cred)))) |
| 984263bc MD |
533 | return (error); |
| 534 | if (vap->va_atime.tv_sec != VNOVAL) | |
| 535 | ip->i_flag |= IN_ACCESS; | |
| 536 | if (vap->va_mtime.tv_sec != VNOVAL) | |
| 537 | ip->i_flag |= IN_CHANGE | IN_UPDATE; | |
| 538 | ufs_itimes(vp); | |
| 539 | if (vap->va_atime.tv_sec != VNOVAL) { | |
| 540 | ip->i_atime = vap->va_atime.tv_sec; | |
| 541 | ip->i_atimensec = vap->va_atime.tv_nsec; | |
| 542 | } | |
| 543 | if (vap->va_mtime.tv_sec != VNOVAL) { | |
| 544 | ip->i_mtime = vap->va_mtime.tv_sec; | |
| 545 | ip->i_mtimensec = vap->va_mtime.tv_nsec; | |
| 546 | } | |
| ac690a1d | 547 | error = ffs_update(vp, 0); |
| 984263bc MD |
548 | if (error) |
| 549 | return (error); | |
| 550 | } | |
| 551 | error = 0; | |
| 552 | if (vap->va_mode != (mode_t)VNOVAL) { | |
| 553 | if (vp->v_mount->mnt_flag & MNT_RDONLY) | |
| 554 | return (EROFS); | |
| 87de5057 | 555 | error = ufs_chmod(vp, (int)vap->va_mode, cred); |
| 984263bc MD |
556 | } |
| 557 | VN_KNOTE(vp, NOTE_ATTRIB); | |
| 558 | return (error); | |
| 559 | } | |
| 560 | ||
| 561 | /* | |
| 562 | * Change the mode on a file. | |
| 563 | * Inode must be locked before calling. | |
| 564 | */ | |
| 565 | static int | |
| 87de5057 | 566 | ufs_chmod(struct vnode *vp, int mode, struct ucred *cred) |
| 984263bc | 567 | { |
| 3ff2135f | 568 | struct inode *ip = VTOI(vp); |
| 984263bc MD |
569 | int error; |
| 570 | ||
| 571 | if (cred->cr_uid != ip->i_uid) { | |
| 895c1f85 | 572 | error = priv_check_cred(cred, PRIV_ROOT, PRISON_ROOT); |
| 984263bc MD |
573 | if (error) |
| 574 | return (error); | |
| 575 | } | |
| 576 | if (cred->cr_uid) { | |
| 577 | if (vp->v_type != VDIR && (mode & S_ISTXT)) | |
| 578 | return (EFTYPE); | |
| 579 | if (!groupmember(ip->i_gid, cred) && (mode & ISGID)) | |
| 580 | return (EPERM); | |
| 581 | } | |
| 582 | ip->i_mode &= ~ALLPERMS; | |
| 583 | ip->i_mode |= (mode & ALLPERMS); | |
| 584 | ip->i_flag |= IN_CHANGE; | |
| 585 | return (0); | |
| 586 | } | |
| 587 | ||
| 588 | /* | |
| 589 | * Perform chown operation on inode ip; | |
| 590 | * inode must be locked prior to call. | |
| 591 | */ | |
| 592 | static int | |
| 87de5057 | 593 | ufs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred) |
| 984263bc | 594 | { |
| 3ff2135f | 595 | struct inode *ip = VTOI(vp); |
| 984263bc MD |
596 | uid_t ouid; |
| 597 | gid_t ogid; | |
| 598 | int error = 0; | |
| 599 | #ifdef QUOTA | |
| 3ff2135f | 600 | int i; |
| 984263bc MD |
601 | long change; |
| 602 | #endif | |
| 603 | ||
| 604 | if (uid == (uid_t)VNOVAL) | |
| 605 | uid = ip->i_uid; | |
| 606 | if (gid == (gid_t)VNOVAL) | |
| 607 | gid = ip->i_gid; | |
| 608 | /* | |
| 609 | * If we don't own the file, are trying to change the owner | |
| 610 | * of the file, or are not a member of the target group, | |
| 611 | * the caller must be superuser or the call fails. | |
| 612 | */ | |
| 613 | if ((cred->cr_uid != ip->i_uid || uid != ip->i_uid || | |
| a10e3626 DR |
614 | (gid != ip->i_gid && !(cred->cr_gid == gid || |
| 615 | groupmember((gid_t)gid, cred)))) && | |
| 895c1f85 | 616 | (error = priv_check_cred(cred, PRIV_ROOT, PRISON_ROOT))) |
| 984263bc MD |
617 | return (error); |
| 618 | ogid = ip->i_gid; | |
| 619 | ouid = ip->i_uid; | |
| 620 | #ifdef QUOTA | |
| 50e58362 | 621 | if ((error = ufs_getinoquota(ip)) != 0) |
| 984263bc MD |
622 | return (error); |
| 623 | if (ouid == uid) { | |
| 50e58362 | 624 | ufs_dqrele(vp, ip->i_dquot[USRQUOTA]); |
| 984263bc MD |
625 | ip->i_dquot[USRQUOTA] = NODQUOT; |
| 626 | } | |
| 627 | if (ogid == gid) { | |
| 50e58362 | 628 | ufs_dqrele(vp, ip->i_dquot[GRPQUOTA]); |
| 984263bc MD |
629 | ip->i_dquot[GRPQUOTA] = NODQUOT; |
| 630 | } | |
| 631 | change = ip->i_blocks; | |
| 50e58362 MD |
632 | (void) ufs_chkdq(ip, -change, cred, CHOWN); |
| 633 | (void) ufs_chkiq(ip, -1, cred, CHOWN); | |
| 984263bc | 634 | for (i = 0; i < MAXQUOTAS; i++) { |
| 50e58362 | 635 | ufs_dqrele(vp, ip->i_dquot[i]); |
| 984263bc MD |
636 | ip->i_dquot[i] = NODQUOT; |
| 637 | } | |
| 638 | #endif | |
| 639 | ip->i_gid = gid; | |
| 640 | ip->i_uid = uid; | |
| 641 | #ifdef QUOTA | |
| 50e58362 | 642 | if ((error = ufs_getinoquota(ip)) == 0) { |
| 984263bc | 643 | if (ouid == uid) { |
| 50e58362 | 644 | ufs_dqrele(vp, ip->i_dquot[USRQUOTA]); |
| 984263bc MD |
645 | ip->i_dquot[USRQUOTA] = NODQUOT; |
| 646 | } | |
| 647 | if (ogid == gid) { | |
| 50e58362 | 648 | ufs_dqrele(vp, ip->i_dquot[GRPQUOTA]); |
| 984263bc MD |
649 | ip->i_dquot[GRPQUOTA] = NODQUOT; |
| 650 | } | |
| 50e58362 MD |
651 | if ((error = ufs_chkdq(ip, change, cred, CHOWN)) == 0) { |
| 652 | if ((error = ufs_chkiq(ip, 1, cred, CHOWN)) == 0) | |
| 984263bc MD |
653 | goto good; |
| 654 | else | |
| 50e58362 | 655 | (void)ufs_chkdq(ip, -change, cred, CHOWN|FORCE); |
| 984263bc MD |
656 | } |
| 657 | for (i = 0; i < MAXQUOTAS; i++) { | |
| 50e58362 | 658 | ufs_dqrele(vp, ip->i_dquot[i]); |
| 984263bc MD |
659 | ip->i_dquot[i] = NODQUOT; |
| 660 | } | |
| 661 | } | |
| 662 | ip->i_gid = ogid; | |
| 663 | ip->i_uid = ouid; | |
| 50e58362 | 664 | if (ufs_getinoquota(ip) == 0) { |
| 984263bc | 665 | if (ouid == uid) { |
| 50e58362 | 666 | ufs_dqrele(vp, ip->i_dquot[USRQUOTA]); |
| 984263bc MD |
667 | ip->i_dquot[USRQUOTA] = NODQUOT; |
| 668 | } | |
| 669 | if (ogid == gid) { | |
| 50e58362 | 670 | ufs_dqrele(vp, ip->i_dquot[GRPQUOTA]); |
| 984263bc MD |
671 | ip->i_dquot[GRPQUOTA] = NODQUOT; |
| 672 | } | |
| 50e58362 MD |
673 | (void) ufs_chkdq(ip, change, cred, FORCE|CHOWN); |
| 674 | (void) ufs_chkiq(ip, 1, cred, FORCE|CHOWN); | |
| 675 | (void) ufs_getinoquota(ip); | |
| 984263bc MD |
676 | } |
| 677 | return (error); | |
| 678 | good: | |
| 50e58362 | 679 | if (ufs_getinoquota(ip)) |
| 984263bc MD |
680 | panic("ufs_chown: lost quota"); |
| 681 | #endif /* QUOTA */ | |
| 682 | ip->i_flag |= IN_CHANGE; | |
| 683 | if (cred->cr_uid != 0 && (ouid != uid || ogid != gid)) | |
| 684 | ip->i_mode &= ~(ISUID | ISGID); | |
| 685 | return (0); | |
| 686 | } | |
| 687 | ||
| 688 | /* | |
| 689 | * Mmap a file | |
| 690 | * | |
| 691 | * NB Currently unsupported. | |
| 0973c589 | 692 | * |
| b478fdce | 693 | * ufs_mmap(struct vnode *a_vp, int a_fflags, struct ucred *a_cred) |
| 984263bc MD |
694 | */ |
| 695 | /* ARGSUSED */ | |
| 0961aa92 | 696 | static |
| 984263bc | 697 | int |
| 0973c589 | 698 | ufs_mmap(struct vop_mmap_args *ap) |
| 984263bc | 699 | { |
| 984263bc MD |
700 | return (EINVAL); |
| 701 | } | |
| 702 | ||
| 0973c589 CP |
703 | /* |
| 704 | * ufs_remove(struct vnode *a_dvp, struct vnode *a_vp, | |
| 705 | * struct componentname *a_cnp) | |
| 706 | */ | |
| 0961aa92 | 707 | static |
| 984263bc | 708 | int |
| e62afb5f | 709 | ufs_remove(struct vop_old_remove_args *ap) |
| 984263bc MD |
710 | { |
| 711 | struct inode *ip; | |
| 712 | struct vnode *vp = ap->a_vp; | |
| 713 | struct vnode *dvp = ap->a_dvp; | |
| 714 | int error; | |
| 715 | ||
| 716 | ip = VTOI(vp); | |
| 3a907475 | 717 | #if 0 /* handled by kernel now */ |
| 984263bc MD |
718 | if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || |
| 719 | (VTOI(dvp)->i_flags & APPEND)) { | |
| 720 | error = EPERM; | |
| 721 | goto out; | |
| 722 | } | |
| 3a907475 | 723 | #endif |
| 984263bc MD |
724 | error = ufs_dirremove(dvp, ip, ap->a_cnp->cn_flags, 0); |
| 725 | VN_KNOTE(vp, NOTE_DELETE); | |
| 726 | VN_KNOTE(dvp, NOTE_WRITE); | |
| 3a907475 | 727 | #if 0 |
| 984263bc | 728 | out: |
| 3a907475 | 729 | #endif |
| 984263bc MD |
730 | return (error); |
| 731 | } | |
| 732 | ||
| 733 | /* | |
| 734 | * link vnode call | |
| 0973c589 CP |
735 | * |
| 736 | * ufs_link(struct vnode *a_tdvp, struct vnode *a_vp, | |
| 737 | * struct componentname *a_cnp) | |
| 984263bc | 738 | */ |
| 0961aa92 | 739 | static |
| 984263bc | 740 | int |
| e62afb5f | 741 | ufs_link(struct vop_old_link_args *ap) |
| 984263bc MD |
742 | { |
| 743 | struct vnode *vp = ap->a_vp; | |
| 744 | struct vnode *tdvp = ap->a_tdvp; | |
| 745 | struct componentname *cnp = ap->a_cnp; | |
| 984263bc MD |
746 | struct inode *ip; |
| 747 | struct direct newdir; | |
| 748 | int error; | |
| 749 | ||
| 984263bc MD |
750 | if (tdvp->v_mount != vp->v_mount) { |
| 751 | error = EXDEV; | |
| 752 | goto out2; | |
| 753 | } | |
| ca466bae | 754 | if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE))) { |
| 984263bc MD |
755 | goto out2; |
| 756 | } | |
| 757 | ip = VTOI(vp); | |
| 758 | if ((nlink_t)ip->i_nlink >= LINK_MAX) { | |
| 759 | error = EMLINK; | |
| 760 | goto out1; | |
| 761 | } | |
| 3a907475 | 762 | #if 0 /* handled by kernel now, also DragonFly allows this */ |
| 984263bc MD |
763 | if (ip->i_flags & (IMMUTABLE | APPEND)) { |
| 764 | error = EPERM; | |
| 765 | goto out1; | |
| 766 | } | |
| 3a907475 | 767 | #endif |
| 984263bc MD |
768 | ip->i_effnlink++; |
| 769 | ip->i_nlink++; | |
| 770 | ip->i_flag |= IN_CHANGE; | |
| 771 | if (DOINGSOFTDEP(vp)) | |
| 772 | softdep_change_linkcnt(ip); | |
| ac690a1d | 773 | error = ffs_update(vp, !(DOINGSOFTDEP(vp) | DOINGASYNC(vp))); |
| 984263bc MD |
774 | if (!error) { |
| 775 | ufs_makedirentry(ip, cnp, &newdir); | |
| 776 | error = ufs_direnter(tdvp, vp, &newdir, cnp, NULL); | |
| 777 | } | |
| 778 | ||
| 779 | if (error) { | |
| 780 | ip->i_effnlink--; | |
| 781 | ip->i_nlink--; | |
| 782 | ip->i_flag |= IN_CHANGE; | |
| 783 | if (DOINGSOFTDEP(vp)) | |
| 784 | softdep_change_linkcnt(ip); | |
| 785 | } | |
| 786 | out1: | |
| 787 | if (tdvp != vp) | |
| a11aaa81 | 788 | vn_unlock(vp); |
| 984263bc MD |
789 | out2: |
| 790 | VN_KNOTE(vp, NOTE_LINK); | |
| 791 | VN_KNOTE(tdvp, NOTE_WRITE); | |
| 792 | return (error); | |
| 793 | } | |
| 794 | ||
| 795 | /* | |
| 796 | * whiteout vnode call | |
| 0973c589 CP |
797 | * |
| 798 | * ufs_whiteout(struct vnode *a_dvp, struct componentname *a_cnp, int a_flags) | |
| 984263bc | 799 | */ |
| 0961aa92 | 800 | static |
| 984263bc | 801 | int |
| e62afb5f | 802 | ufs_whiteout(struct vop_old_whiteout_args *ap) |
| 984263bc MD |
803 | { |
| 804 | struct vnode *dvp = ap->a_dvp; | |
| 805 | struct componentname *cnp = ap->a_cnp; | |
| 806 | struct direct newdir; | |
| 807 | int error = 0; | |
| 808 | ||
| 809 | switch (ap->a_flags) { | |
| 2b69e610 | 810 | case NAMEI_LOOKUP: |
| 984263bc MD |
811 | /* 4.4 format directories support whiteout operations */ |
| 812 | if (dvp->v_mount->mnt_maxsymlinklen > 0) | |
| 813 | return (0); | |
| 814 | return (EOPNOTSUPP); | |
| 815 | ||
| 2b69e610 | 816 | case NAMEI_CREATE: |
| 984263bc MD |
817 | /* create a new directory whiteout */ |
| 818 | #ifdef DIAGNOSTIC | |
| 984263bc MD |
819 | if (dvp->v_mount->mnt_maxsymlinklen <= 0) |
| 820 | panic("ufs_whiteout: old format filesystem"); | |
| 821 | #endif | |
| 822 | ||
| 823 | newdir.d_ino = WINO; | |
| 824 | newdir.d_namlen = cnp->cn_namelen; | |
| 825 | bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1); | |
| 826 | newdir.d_type = DT_WHT; | |
| 827 | error = ufs_direnter(dvp, NULL, &newdir, cnp, NULL); | |
| 828 | break; | |
| 829 | ||
| 2b69e610 | 830 | case NAMEI_DELETE: |
| 984263bc MD |
831 | /* remove an existing directory whiteout */ |
| 832 | #ifdef DIAGNOSTIC | |
| 833 | if (dvp->v_mount->mnt_maxsymlinklen <= 0) | |
| 834 | panic("ufs_whiteout: old format filesystem"); | |
| 835 | #endif | |
| 836 | ||
| 2b69e610 | 837 | cnp->cn_flags &= ~CNP_DOWHITEOUT; |
| 984263bc MD |
838 | error = ufs_dirremove(dvp, NULL, cnp->cn_flags, 0); |
| 839 | break; | |
| 840 | default: | |
| 841 | panic("ufs_whiteout: unknown op"); | |
| 842 | } | |
| 843 | return (error); | |
| 844 | } | |
| 845 | ||
| 846 | /* | |
| 847 | * Rename system call. | |
| 848 | * rename("foo", "bar"); | |
| 849 | * is essentially | |
| 850 | * unlink("bar"); | |
| 851 | * link("foo", "bar"); | |
| 852 | * unlink("foo"); | |
| 853 | * but ``atomically''. Can't do full commit without saving state in the | |
| 854 | * inode on disk which isn't feasible at this time. Best we can do is | |
| 855 | * always guarantee the target exists. | |
| 856 | * | |
| 857 | * Basic algorithm is: | |
| 858 | * | |
| 859 | * 1) Bump link count on source while we're linking it to the | |
| 860 | * target. This also ensure the inode won't be deleted out | |
| 861 | * from underneath us while we work (it may be truncated by | |
| 862 | * a concurrent `trunc' or `open' for creation). | |
| 863 | * 2) Link source to destination. If destination already exists, | |
| 864 | * delete it first. | |
| 865 | * 3) Unlink source reference to inode if still around. If a | |
| 866 | * directory was moved and the parent of the destination | |
| 867 | * is different from the source, patch the ".." entry in the | |
| 868 | * directory. | |
| 0973c589 CP |
869 | * |
| 870 | * ufs_rename(struct vnode *a_fdvp, struct vnode *a_fvp, | |
| 871 | * struct componentname *a_fcnp, struct vnode *a_tdvp, | |
| 872 | * struct vnode *a_tvp, struct componentname *a_tcnp) | |
| 984263bc | 873 | */ |
| 0961aa92 | 874 | static |
| 984263bc | 875 | int |
| e62afb5f | 876 | ufs_rename(struct vop_old_rename_args *ap) |
| 984263bc MD |
877 | { |
| 878 | struct vnode *tvp = ap->a_tvp; | |
| 3ff2135f | 879 | struct vnode *tdvp = ap->a_tdvp; |
| 984263bc MD |
880 | struct vnode *fvp = ap->a_fvp; |
| 881 | struct vnode *fdvp = ap->a_fdvp; | |
| 882 | struct componentname *tcnp = ap->a_tcnp; | |
| 883 | struct componentname *fcnp = ap->a_fcnp; | |
| 984263bc MD |
884 | struct inode *ip, *xp, *dp; |
| 885 | struct direct newdir; | |
| 9ab06300 MD |
886 | ino_t oldparent = 0, newparent = 0; |
| 887 | int doingdirectory = 0; | |
| 984263bc MD |
888 | int error = 0, ioflag; |
| 889 | ||
| 984263bc MD |
890 | /* |
| 891 | * Check for cross-device rename. | |
| 892 | */ | |
| 893 | if ((fvp->v_mount != tdvp->v_mount) || | |
| 894 | (tvp && (fvp->v_mount != tvp->v_mount))) { | |
| 895 | error = EXDEV; | |
| 896 | abortit: | |
| 897 | if (tdvp == tvp) | |
| 898 | vrele(tdvp); | |
| 899 | else | |
| 900 | vput(tdvp); | |
| 901 | if (tvp) | |
| 902 | vput(tvp); | |
| 903 | vrele(fdvp); | |
| 904 | vrele(fvp); | |
| 905 | return (error); | |
| 906 | } | |
| 907 | ||
| 3a907475 | 908 | #if 0 /* handled by kernel now */ |
| 984263bc MD |
909 | if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || |
| 910 | (VTOI(tdvp)->i_flags & APPEND))) { | |
| 911 | error = EPERM; | |
| 912 | goto abortit; | |
| 913 | } | |
| 3a907475 | 914 | #endif |
| 984263bc MD |
915 | |
| 916 | /* | |
| 917 | * Renaming a file to itself has no effect. The upper layers should | |
| 918 | * not call us in that case. Temporarily just warn if they do. | |
| 919 | */ | |
| 920 | if (fvp == tvp) { | |
| 086c1d7e | 921 | kprintf("ufs_rename: fvp == tvp (can't happen)\n"); |
| 984263bc MD |
922 | error = 0; |
| 923 | goto abortit; | |
| 924 | } | |
| 925 | ||
| ca466bae | 926 | if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0) |
| 984263bc | 927 | goto abortit; |
| fad57d0e MD |
928 | |
| 929 | /* | |
| 930 | * Note: now that fvp is locked we have to be sure to unlock it before | |
| 931 | * using the 'abortit' target. | |
| 932 | */ | |
| 984263bc MD |
933 | dp = VTOI(fdvp); |
| 934 | ip = VTOI(fvp); | |
| 935 | if (ip->i_nlink >= LINK_MAX) { | |
| a11aaa81 | 936 | vn_unlock(fvp); |
| 984263bc MD |
937 | error = EMLINK; |
| 938 | goto abortit; | |
| 939 | } | |
| 3a907475 | 940 | #if 0 /* handled by kernel now */ |
| 984263bc MD |
941 | if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) |
| 942 | || (dp->i_flags & APPEND)) { | |
| a11aaa81 | 943 | vn_unlock(fvp); |
| 984263bc MD |
944 | error = EPERM; |
| 945 | goto abortit; | |
| 946 | } | |
| 3a907475 | 947 | #endif |
| 984263bc MD |
948 | if ((ip->i_mode & IFMT) == IFDIR) { |
| 949 | /* | |
| 950 | * Avoid ".", "..", and aliases of "." for obvious reasons. | |
| 951 | */ | |
| 952 | if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || | |
| 2b69e610 | 953 | dp == ip || (fcnp->cn_flags | tcnp->cn_flags) & CNP_ISDOTDOT || |
| 984263bc | 954 | (ip->i_flag & IN_RENAME)) { |
| a11aaa81 | 955 | vn_unlock(fvp); |
| 984263bc MD |
956 | error = EINVAL; |
| 957 | goto abortit; | |
| 958 | } | |
| 959 | ip->i_flag |= IN_RENAME; | |
| 960 | oldparent = dp->i_number; | |
| 961 | doingdirectory = 1; | |
| 962 | } | |
| 963 | VN_KNOTE(fdvp, NOTE_WRITE); /* XXX right place? */ | |
| 984263bc MD |
964 | |
| 965 | /* | |
| fad57d0e MD |
966 | * fvp still locked. ip->i_flag has IN_RENAME set if doingdirectory. |
| 967 | * Cleanup fvp requirements so we can unlock it. | |
| 968 | * | |
| 969 | * tvp and tdvp are locked. tvp may be NULL. Now that dp and xp | |
| 970 | * is setup we can use the 'bad' target if we unlock fvp. We cannot | |
| 971 | * use the abortit target anymore because of IN_RENAME. | |
| 984263bc MD |
972 | */ |
| 973 | dp = VTOI(tdvp); | |
| 984263bc MD |
974 | if (tvp) |
| 975 | xp = VTOI(tvp); | |
| fad57d0e MD |
976 | else |
| 977 | xp = NULL; | |
| 984263bc MD |
978 | |
| 979 | /* | |
| 980 | * 1) Bump link count while we're moving stuff | |
| 981 | * around. If we crash somewhere before | |
| 982 | * completing our work, the link count | |
| 983 | * may be wrong, but correctable. | |
| 984 | */ | |
| 985 | ip->i_effnlink++; | |
| 986 | ip->i_nlink++; | |
| 987 | ip->i_flag |= IN_CHANGE; | |
| 988 | if (DOINGSOFTDEP(fvp)) | |
| 989 | softdep_change_linkcnt(ip); | |
| ac690a1d | 990 | if ((error = ffs_update(fvp, !(DOINGSOFTDEP(fvp) | |
| 984263bc | 991 | DOINGASYNC(fvp)))) != 0) { |
| a11aaa81 | 992 | vn_unlock(fvp); |
| 984263bc MD |
993 | goto bad; |
| 994 | } | |
| 995 | ||
| 996 | /* | |
| 997 | * If ".." must be changed (ie the directory gets a new | |
| 998 | * parent) then the source directory must not be in the | |
| 999 | * directory heirarchy above the target, as this would | |
| 1000 | * orphan everything below the source directory. Also | |
| 1001 | * the user must have write permission in the source so | |
| 1002 | * as to be able to change "..". We must repeat the call | |
| 1003 | * to namei, as the parent directory is unlocked by the | |
| 1004 | * call to checkpath(). | |
| 1005 | */ | |
| 87de5057 | 1006 | error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred); |
| a11aaa81 | 1007 | vn_unlock(fvp); |
| fad57d0e MD |
1008 | |
| 1009 | /* | |
| 1010 | * We are now back to where we were in that fvp, fdvp are unlocked | |
| 1011 | * and tvp, tdvp are locked. tvp may be NULL. IN_RENAME may be | |
| 1012 | * set. Only the bad target or, if we clean up tvp and tdvp, the | |
| 1013 | * out target, may be used. | |
| 1014 | */ | |
| 984263bc MD |
1015 | if (oldparent != dp->i_number) |
| 1016 | newparent = dp->i_number; | |
| 1017 | if (doingdirectory && newparent) { | |
| 1018 | if (error) /* write access check above */ | |
| 1019 | goto bad; | |
| fad57d0e MD |
1020 | |
| 1021 | /* | |
| 1022 | * Once we start messing with tvp and tdvp we cannot use the | |
| 1023 | * 'bad' target, only finish cleaning tdvp and tvp up and | |
| 1024 | * use the 'out' target. | |
| 1025 | * | |
| 1026 | * This cleans up tvp. | |
| 1027 | */ | |
| 1028 | if (xp != NULL) { | |
| 984263bc | 1029 | vput(tvp); |
| fad57d0e MD |
1030 | xp = NULL; |
| 1031 | } | |
| 1032 | ||
| 1033 | /* | |
| 1034 | * This is a real mess. ufs_checkpath vput's the target | |
| 1035 | * directory so retain an extra ref and note that tdvp will | |
| 1036 | * lose its lock on return. This leaves us with one good | |
| 1037 | * ref after ufs_checkpath returns. | |
| 1038 | */ | |
| 1039 | vref(tdvp); | |
| 984263bc | 1040 | error = ufs_checkpath(ip, dp, tcnp->cn_cred); |
| fad57d0e MD |
1041 | tcnp->cn_flags |= CNP_PDIRUNLOCK; |
| 1042 | if (error) { | |
| 1043 | vrele(tdvp); | |
| 984263bc | 1044 | goto out; |
| fad57d0e MD |
1045 | } |
| 1046 | ||
| 1047 | /* | |
| 1048 | * relookup no longer messes with tdvp's refs. tdvp must be | |
| 1049 | * unlocked on entry and will be locked on a successful | |
| 1050 | * return. | |
| 1051 | */ | |
| 984263bc | 1052 | error = relookup(tdvp, &tvp, tcnp); |
| fad57d0e MD |
1053 | if (error) { |
| 1054 | if (tcnp->cn_flags & CNP_PDIRUNLOCK) | |
| 1055 | vrele(tdvp); | |
| 1056 | else | |
| 1057 | vput(tdvp); | |
| 984263bc | 1058 | goto out; |
| fad57d0e MD |
1059 | } |
| 1060 | KKASSERT((tcnp->cn_flags & CNP_PDIRUNLOCK) == 0); | |
| 984263bc | 1061 | dp = VTOI(tdvp); |
| 984263bc MD |
1062 | if (tvp) |
| 1063 | xp = VTOI(tvp); | |
| 1064 | } | |
| fad57d0e MD |
1065 | |
| 1066 | /* | |
| 1067 | * We are back to fvp, fdvp unlocked, tvp, tdvp locked. tvp may | |
| 1068 | * be NULL (xp will also be NULL in that case), and IN_RENAME will | |
| 1069 | * be set if doingdirectory. This means we can use the 'bad' target | |
| 1070 | * again. | |
| 1071 | */ | |
| 1072 | ||
| 984263bc MD |
1073 | /* |
| 1074 | * 2) If target doesn't exist, link the target | |
| 1075 | * to the source and unlink the source. | |
| 1076 | * Otherwise, rewrite the target directory | |
| 1077 | * entry to reference the source inode and | |
| 1078 | * expunge the original entry's existence. | |
| 1079 | */ | |
| 1080 | if (xp == NULL) { | |
| 1081 | if (dp->i_dev != ip->i_dev) | |
| 1082 | panic("ufs_rename: EXDEV"); | |
| 1083 | /* | |
| 1084 | * Account for ".." in new directory. | |
| 1085 | * When source and destination have the same | |
| 1086 | * parent we don't fool with the link count. | |
| 1087 | */ | |
| 1088 | if (doingdirectory && newparent) { | |
| 1089 | if ((nlink_t)dp->i_nlink >= LINK_MAX) { | |
| 1090 | error = EMLINK; | |
| 1091 | goto bad; | |
| 1092 | } | |
| 1093 | dp->i_effnlink++; | |
| 1094 | dp->i_nlink++; | |
| 1095 | dp->i_flag |= IN_CHANGE; | |
| 1096 | if (DOINGSOFTDEP(tdvp)) | |
| 1097 | softdep_change_linkcnt(dp); | |
| ac690a1d | 1098 | error = ffs_update(tdvp, !(DOINGSOFTDEP(tdvp) | |
| 984263bc MD |
1099 | DOINGASYNC(tdvp))); |
| 1100 | if (error) | |
| 1101 | goto bad; | |
| 1102 | } | |
| 1103 | ufs_makedirentry(ip, tcnp, &newdir); | |
| 1104 | error = ufs_direnter(tdvp, NULL, &newdir, tcnp, NULL); | |
| 1105 | if (error) { | |
| 1106 | if (doingdirectory && newparent) { | |
| 1107 | dp->i_effnlink--; | |
| 1108 | dp->i_nlink--; | |
| 1109 | dp->i_flag |= IN_CHANGE; | |
| 1110 | if (DOINGSOFTDEP(tdvp)) | |
| 1111 | softdep_change_linkcnt(dp); | |
| ac690a1d | 1112 | (void)ffs_update(tdvp, 1); |
| 984263bc MD |
1113 | } |
| 1114 | goto bad; | |
| 1115 | } | |
| 1116 | VN_KNOTE(tdvp, NOTE_WRITE); | |
| 1117 | vput(tdvp); | |
| 1118 | } else { | |
| 1119 | if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) | |
| 1120 | panic("ufs_rename: EXDEV"); | |
| 1121 | /* | |
| 1122 | * Short circuit rename(foo, foo). | |
| 1123 | */ | |
| 1124 | if (xp->i_number == ip->i_number) | |
| 1125 | panic("ufs_rename: same file"); | |
| 1126 | /* | |
| 1127 | * If the parent directory is "sticky", then the user must | |
| 1128 | * own the parent directory, or the destination of the rename, | |
| 1129 | * otherwise the destination may not be changed (except by | |
| 1130 | * root). This implements append-only directories. | |
| 1131 | */ | |
| 1132 | if ((dp->i_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 && | |
| 1133 | tcnp->cn_cred->cr_uid != dp->i_uid && | |
| 1134 | xp->i_uid != tcnp->cn_cred->cr_uid) { | |
| 1135 | error = EPERM; | |
| 1136 | goto bad; | |
| 1137 | } | |
| 1138 | /* | |
| 1139 | * Target must be empty if a directory and have no links | |
| 1140 | * to it. Also, ensure source and target are compatible | |
| 1141 | * (both directories, or both not directories). | |
| 8e005a45 MD |
1142 | * |
| 1143 | * Purge the file or directory being replaced from the | |
| 1144 | * nameccache. | |
| 984263bc MD |
1145 | */ |
| 1146 | if ((xp->i_mode&IFMT) == IFDIR) { | |
| 1147 | if ((xp->i_effnlink > 2) || | |
| 1148 | !ufs_dirempty(xp, dp->i_number, tcnp->cn_cred)) { | |
| 1149 | error = ENOTEMPTY; | |
| 1150 | goto bad; | |
| 1151 | } | |
| 1152 | if (!doingdirectory) { | |
| 1153 | error = ENOTDIR; | |
| 1154 | goto bad; | |
| 1155 | } | |
| fad57d0e | 1156 | /* cache_purge removed - handled by VFS compat layer */ |
| 8e005a45 | 1157 | } else if (doingdirectory == 0) { |
| fad57d0e | 1158 | /* cache_purge removed - handled by VFS compat layer */ |
| 8e005a45 | 1159 | } else { |
| 984263bc MD |
1160 | error = EISDIR; |
| 1161 | goto bad; | |
| 1162 | } | |
| 9ab06300 MD |
1163 | /* |
| 1164 | * note: inode passed to ufs_dirrewrite() is 0 for a | |
| 1165 | * non-directory file rename, 1 for a directory rename | |
| 1166 | * in the same directory, and > 1 for an inode representing | |
| 1167 | * the new directory. | |
| 1168 | */ | |
| 984263bc MD |
1169 | error = ufs_dirrewrite(dp, xp, ip->i_number, |
| 1170 | IFTODT(ip->i_mode), | |
| 9ab06300 MD |
1171 | (doingdirectory && newparent) ? |
| 1172 | newparent : (ino_t)doingdirectory); | |
| 984263bc MD |
1173 | if (error) |
| 1174 | goto bad; | |
| 1175 | if (doingdirectory) { | |
| 1176 | if (!newparent) { | |
| 1177 | dp->i_effnlink--; | |
| 1178 | if (DOINGSOFTDEP(tdvp)) | |
| 1179 | softdep_change_linkcnt(dp); | |
| 1180 | } | |
| 1181 | xp->i_effnlink--; | |
| 1182 | if (DOINGSOFTDEP(tvp)) | |
| 1183 | softdep_change_linkcnt(xp); | |
| 1184 | } | |
| 1185 | if (doingdirectory && !DOINGSOFTDEP(tvp)) { | |
| 1186 | /* | |
| 1187 | * Truncate inode. The only stuff left in the directory | |
| 1188 | * is "." and "..". The "." reference is inconsequential | |
| 1189 | * since we are quashing it. We have removed the "." | |
| 1190 | * reference and the reference in the parent directory, | |
| 1191 | * but there may be other hard links. The soft | |
| 1192 | * dependency code will arrange to do these operations | |
| 1193 | * after the parent directory entry has been deleted on | |
| 1194 | * disk, so when running with that code we avoid doing | |
| 1195 | * them now. | |
| 1196 | */ | |
| 1197 | if (!newparent) { | |
| 1198 | dp->i_nlink--; | |
| 1199 | dp->i_flag |= IN_CHANGE; | |
| 1200 | } | |
| 1201 | xp->i_nlink--; | |
| 1202 | xp->i_flag |= IN_CHANGE; | |
| 1203 | ioflag = DOINGASYNC(tvp) ? 0 : IO_SYNC; | |
| ac690a1d | 1204 | error = ffs_truncate(tvp, (off_t)0, ioflag, |
| 87de5057 MD |
1205 | tcnp->cn_cred); |
| 1206 | if (error) | |
| 984263bc MD |
1207 | goto bad; |
| 1208 | } | |
| 1209 | VN_KNOTE(tdvp, NOTE_WRITE); | |
| 1210 | vput(tdvp); | |
| 1211 | VN_KNOTE(tvp, NOTE_DELETE); | |
| 1212 | vput(tvp); | |
| 1213 | xp = NULL; | |
| 1214 | } | |
| 1215 | ||
| 1216 | /* | |
| fad57d0e MD |
1217 | * tvp and tdvp have been cleaned up. only fvp and fdvp (both |
| 1218 | * unlocked) remain. We are about to overwrite fvp but we have to | |
| 1219 | * keep 'ip' intact so we cannot release the old fvp, which is still | |
| 1220 | * refd and accessible via ap->a_fvp. | |
| 1221 | * | |
| 1222 | * This means we cannot use either 'bad' or 'out' to cleanup any | |
| 1223 | * more. | |
| 1224 | */ | |
| 1225 | ||
| 1226 | /* | |
| 984263bc MD |
1227 | * 3) Unlink the source. |
| 1228 | */ | |
| 2b69e610 | 1229 | fcnp->cn_flags &= ~CNP_MODMASK; |
| fad57d0e | 1230 | fcnp->cn_flags |= CNP_LOCKPARENT; |
| 984263bc | 1231 | error = relookup(fdvp, &fvp, fcnp); |
| fad57d0e | 1232 | if (error || fvp == NULL) { |
| 984263bc | 1233 | /* |
| fad57d0e MD |
1234 | * From name has disappeared. IN_RENAME will not be set if |
| 1235 | * we get past the panic so we don't have to clean it up. | |
| 984263bc MD |
1236 | */ |
| 1237 | if (doingdirectory) | |
| 1238 | panic("ufs_rename: lost dir entry"); | |
| 1239 | vrele(ap->a_fvp); | |
| fad57d0e MD |
1240 | if (fcnp->cn_flags & CNP_PDIRUNLOCK) |
| 1241 | vrele(fdvp); | |
| 1242 | else | |
| 1243 | vput(fdvp); | |
| 1244 | return(0); | |
| 984263bc | 1245 | } |
| fad57d0e MD |
1246 | KKASSERT((fcnp->cn_flags & CNP_PDIRUNLOCK) == 0); |
| 1247 | ||
| 1248 | /* | |
| 1249 | * fdvp and fvp are locked. | |
| 1250 | */ | |
| 1251 | xp = VTOI(fvp); | |
| 1252 | dp = VTOI(fdvp); | |
| 1253 | ||
| 984263bc MD |
1254 | /* |
| 1255 | * Ensure that the directory entry still exists and has not | |
| 1256 | * changed while the new name has been entered. If the source is | |
| 1257 | * a file then the entry may have been unlinked or renamed. In | |
| 1258 | * either case there is no further work to be done. If the source | |
| 1259 | * is a directory then it cannot have been rmdir'ed; the IN_RENAME | |
| 1260 | * flag ensures that it cannot be moved by another rename or removed | |
| fad57d0e | 1261 | * by a rmdir. Cleanup IN_RENAME. |
| 984263bc MD |
1262 | */ |
| 1263 | if (xp != ip) { | |
| 1264 | if (doingdirectory) | |
| 1265 | panic("ufs_rename: lost dir entry"); | |
| 1266 | } else { | |
| 1267 | /* | |
| 1268 | * If the source is a directory with a | |
| 1269 | * new parent, the link count of the old | |
| 1270 | * parent directory must be decremented | |
| 1271 | * and ".." set to point to the new parent. | |
| 1272 | */ | |
| 1273 | if (doingdirectory && newparent) { | |
| 1274 | xp->i_offset = mastertemplate.dot_reclen; | |
| 1275 | ufs_dirrewrite(xp, dp, newparent, DT_DIR, 0); | |
| fad57d0e | 1276 | /* cache_purge removed - handled by VFS compat layer */ |
| 984263bc MD |
1277 | } |
| 1278 | error = ufs_dirremove(fdvp, xp, fcnp->cn_flags, 0); | |
| 1279 | xp->i_flag &= ~IN_RENAME; | |
| 1280 | } | |
| 8e005a45 | 1281 | |
| 984263bc | 1282 | VN_KNOTE(fvp, NOTE_RENAME); |
| fad57d0e MD |
1283 | vput(fdvp); |
| 1284 | vput(fvp); | |
| 984263bc MD |
1285 | vrele(ap->a_fvp); |
| 1286 | return (error); | |
| 1287 | ||
| 1288 | bad: | |
| 1289 | if (xp) | |
| 1290 | vput(ITOV(xp)); | |
| 1291 | vput(ITOV(dp)); | |
| 1292 | out: | |
| 1293 | if (doingdirectory) | |
| 1294 | ip->i_flag &= ~IN_RENAME; | |
| ca466bae | 1295 | if (vn_lock(fvp, LK_EXCLUSIVE) == 0) { |
| 984263bc MD |
1296 | ip->i_effnlink--; |
| 1297 | ip->i_nlink--; | |
| 1298 | ip->i_flag |= IN_CHANGE; | |
| 1299 | ip->i_flag &= ~IN_RENAME; | |
| 1300 | if (DOINGSOFTDEP(fvp)) | |
| 1301 | softdep_change_linkcnt(ip); | |
| 1302 | vput(fvp); | |
| fad57d0e | 1303 | } else { |
| 984263bc | 1304 | vrele(fvp); |
| fad57d0e | 1305 | } |
| 984263bc MD |
1306 | return (error); |
| 1307 | } | |
| 1308 | ||
| 1309 | /* | |
| 1310 | * Mkdir system call | |
| 0973c589 CP |
1311 | * |
| 1312 | * ufs_mkdir(struct vnode *a_dvp, struct vnode **a_vpp, | |
| 1313 | * struct componentname *a_cnp, struct vattr *a_vap) | |
| 984263bc | 1314 | */ |
| 0961aa92 | 1315 | static |
| 984263bc | 1316 | int |
| e62afb5f | 1317 | ufs_mkdir(struct vop_old_mkdir_args *ap) |
| 984263bc | 1318 | { |
| 3ff2135f RG |
1319 | struct vnode *dvp = ap->a_dvp; |
| 1320 | struct vattr *vap = ap->a_vap; | |
| 1321 | struct componentname *cnp = ap->a_cnp; | |
| 1322 | struct inode *ip, *dp; | |
| 984263bc MD |
1323 | struct vnode *tvp; |
| 1324 | struct buf *bp; | |
| 1325 | struct dirtemplate dirtemplate, *dtp; | |
| 1326 | struct direct newdir; | |
| 1327 | int error, dmode; | |
| 1328 | long blkoff; | |
| 1329 | ||
| 984263bc MD |
1330 | dp = VTOI(dvp); |
| 1331 | if ((nlink_t)dp->i_nlink >= LINK_MAX) { | |
| 1332 | error = EMLINK; | |
| 1333 | goto out; | |
| 1334 | } | |
| 1335 | dmode = vap->va_mode & 0777; | |
| 1336 | dmode |= IFDIR; | |
| 1337 | /* | |
| 1338 | * Must simulate part of ufs_makeinode here to acquire the inode, | |
| 1339 | * but not have it entered in the parent directory. The entry is | |
| 1340 | * made later after writing "." and ".." entries. | |
| 1341 | */ | |
| ac690a1d | 1342 | error = ffs_valloc(dvp, dmode, cnp->cn_cred, &tvp); |
| 984263bc MD |
1343 | if (error) |
| 1344 | goto out; | |
| 1345 | ip = VTOI(tvp); | |
| 1346 | ip->i_gid = dp->i_gid; | |
| 1347 | #ifdef SUIDDIR | |
| 1348 | { | |
| 1349 | #ifdef QUOTA | |
| 1350 | struct ucred ucred, *ucp; | |
| 1351 | ucp = cnp->cn_cred; | |
| 1352 | #endif | |
| 1353 | /* | |
| 1354 | * If we are hacking owners here, (only do this where told to) | |
| 1355 | * and we are not giving it TO root, (would subvert quotas) | |
| 1356 | * then go ahead and give it to the other user. | |
| 1357 | * The new directory also inherits the SUID bit. | |
| 1358 | * If user's UID and dir UID are the same, | |
| 1359 | * 'give it away' so that the SUID is still forced on. | |
| 1360 | */ | |
| 1361 | if ((dvp->v_mount->mnt_flag & MNT_SUIDDIR) && | |
| 1362 | (dp->i_mode & ISUID) && dp->i_uid) { | |
| 1363 | dmode |= ISUID; | |
| 1364 | ip->i_uid = dp->i_uid; | |
| 1365 | #ifdef QUOTA | |
| 1366 | if (dp->i_uid != cnp->cn_cred->cr_uid) { | |
| 1367 | /* | |
| 1368 | * Make sure the correct user gets charged | |
| 1369 | * for the space. | |
| 1370 | * Make a dummy credential for the victim. | |
| 1371 | * XXX This seems to never be accessed out of | |
| 1372 | * our context so a stack variable is ok. | |
| 1373 | */ | |
| 1374 | ucred.cr_ref = 1; | |
| 1375 | ucred.cr_uid = ip->i_uid; | |
| 1376 | ucred.cr_ngroups = 1; | |
| 1377 | ucred.cr_groups[0] = dp->i_gid; | |
| 1378 | ucp = &ucred; | |
| 1379 | } | |
| 1380 | #endif | |
| 1381 | } else | |
| 1382 | ip->i_uid = cnp->cn_cred->cr_uid; | |
| 1383 | #ifdef QUOTA | |
| 50e58362 MD |
1384 | if ((error = ufs_getinoquota(ip)) || |
| 1385 | (error = ufs_chkiq(ip, 1, ucp, 0))) { | |
| ac690a1d | 1386 | ffs_vfree(tvp, ip->i_number, dmode); |
| 984263bc MD |
1387 | vput(tvp); |
| 1388 | return (error); | |
| 1389 | } | |
| 1390 | #endif | |
| 1391 | } | |
| 1392 | #else /* !SUIDDIR */ | |
| 1393 | ip->i_uid = cnp->cn_cred->cr_uid; | |
| 1394 | #ifdef QUOTA | |
| 50e58362 MD |
1395 | if ((error = ufs_getinoquota(ip)) || |
| 1396 | (error = ufs_chkiq(ip, 1, cnp->cn_cred, 0))) { | |
| ac690a1d | 1397 | ffs_vfree(tvp, ip->i_number, dmode); |
| 984263bc MD |
1398 | vput(tvp); |
| 1399 | return (error); | |
| 1400 | } | |
| 1401 | #endif | |
| 1402 | #endif /* !SUIDDIR */ | |
| 1403 | ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; | |
| 1404 | ip->i_mode = dmode; | |
| 1405 | tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */ | |
| 1406 | ip->i_effnlink = 2; | |
| 1407 | ip->i_nlink = 2; | |
| 1408 | if (DOINGSOFTDEP(tvp)) | |
| 1409 | softdep_change_linkcnt(ip); | |
| 2b69e610 | 1410 | if (cnp->cn_flags & CNP_ISWHITEOUT) |
| 984263bc MD |
1411 | ip->i_flags |= UF_OPAQUE; |
| 1412 | ||
| 1413 | /* | |
| 1414 | * Bump link count in parent directory to reflect work done below. | |
| 1415 | * Should be done before reference is created so cleanup is | |
| 1416 | * possible if we crash. | |
| 1417 | */ | |
| 1418 | dp->i_effnlink++; | |
| 1419 | dp->i_nlink++; | |
| 1420 | dp->i_flag |= IN_CHANGE; | |
| 1421 | if (DOINGSOFTDEP(dvp)) | |
| 1422 | softdep_change_linkcnt(dp); | |
| ac690a1d | 1423 | error = ffs_update(tvp, !(DOINGSOFTDEP(dvp) | DOINGASYNC(dvp))); |
| 984263bc MD |
1424 | if (error) |
| 1425 | goto bad; | |
| 1426 | ||
| 1427 | /* | |
| 83a5effe MD |
1428 | * The vnode must have a VM object in order to issue buffer cache |
| 1429 | * ops on it. | |
| 1430 | */ | |
| 1c843a13 | 1431 | vinitvmio(tvp, DIRBLKSIZ); |
| 83a5effe MD |
1432 | |
| 1433 | /* | |
| 984263bc MD |
1434 | * Initialize directory with "." and ".." from static template. |
| 1435 | */ | |
| 83a5effe | 1436 | if (dvp->v_mount->mnt_maxsymlinklen > 0) |
| 984263bc MD |
1437 | dtp = &mastertemplate; |
| 1438 | else | |
| 1439 | dtp = (struct dirtemplate *)&omastertemplate; | |
| 1440 | dirtemplate = *dtp; | |
| 1441 | dirtemplate.dot_ino = ip->i_number; | |
| 1442 | dirtemplate.dotdot_ino = dp->i_number; | |
| d37b14c8 | 1443 | vnode_pager_setsize(tvp, DIRBLKSIZ); |
| 83a5effe MD |
1444 | error = VOP_BALLOC(tvp, 0LL, DIRBLKSIZ, cnp->cn_cred, B_CLRBUF, &bp); |
| 1445 | if (error) | |
| 984263bc MD |
1446 | goto bad; |
| 1447 | ip->i_size = DIRBLKSIZ; | |
| 1448 | ip->i_flag |= IN_CHANGE | IN_UPDATE; | |
| 984263bc MD |
1449 | bcopy((caddr_t)&dirtemplate, (caddr_t)bp->b_data, sizeof dirtemplate); |
| 1450 | if (DOINGSOFTDEP(tvp)) { | |
| 1451 | /* | |
| 1452 | * Ensure that the entire newly allocated block is a | |
| 1453 | * valid directory so that future growth within the | |
| 1454 | * block does not have to ensure that the block is | |
| 1455 | * written before the inode. | |
| 1456 | */ | |
| 1457 | blkoff = DIRBLKSIZ; | |
| 1458 | while (blkoff < bp->b_bcount) { | |
| 1459 | ((struct direct *) | |
| 1460 | (bp->b_data + blkoff))->d_reclen = DIRBLKSIZ; | |
| 1461 | blkoff += DIRBLKSIZ; | |
| 1462 | } | |
| 1463 | } | |
| ac690a1d | 1464 | if ((error = ffs_update(tvp, !(DOINGSOFTDEP(tvp) | |
| 984263bc | 1465 | DOINGASYNC(tvp)))) != 0) { |
| 62cfda27 | 1466 | bwrite(bp); |
| 984263bc MD |
1467 | goto bad; |
| 1468 | } | |
| 1469 | /* | |
| 1470 | * Directory set up, now install its entry in the parent directory. | |
| 1471 | * | |
| 1472 | * If we are not doing soft dependencies, then we must write out the | |
| 1473 | * buffer containing the new directory body before entering the new | |
| 1474 | * name in the parent. If we are doing soft dependencies, then the | |
| 1475 | * buffer containing the new directory body will be passed to and | |
| 1476 | * released in the soft dependency code after the code has attached | |
| 1477 | * an appropriate ordering dependency to the buffer which ensures that | |
| 1478 | * the buffer is written before the new name is written in the parent. | |
| 1479 | */ | |
| 1480 | if (DOINGASYNC(dvp)) | |
| 1481 | bdwrite(bp); | |
| 62cfda27 | 1482 | else if (!DOINGSOFTDEP(dvp) && (error = bwrite(bp)) != 0) |
| 984263bc MD |
1483 | goto bad; |
| 1484 | ufs_makedirentry(ip, cnp, &newdir); | |
| 1485 | error = ufs_direnter(dvp, tvp, &newdir, cnp, bp); | |
| 1486 | ||
| 1487 | bad: | |
| 1488 | if (error == 0) { | |
| 1489 | VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); | |
| 1490 | *ap->a_vpp = tvp; | |
| 1491 | } else { | |
| 1492 | dp->i_effnlink--; | |
| 1493 | dp->i_nlink--; | |
| 1494 | dp->i_flag |= IN_CHANGE; | |
| 1495 | if (DOINGSOFTDEP(dvp)) | |
| 1496 | softdep_change_linkcnt(dp); | |
| 1497 | /* | |
| 1498 | * No need to do an explicit VOP_TRUNCATE here, vrele will | |
| 1499 | * do this for us because we set the link count to 0. | |
| 1500 | */ | |
| 1501 | ip->i_effnlink = 0; | |
| 1502 | ip->i_nlink = 0; | |
| 1503 | ip->i_flag |= IN_CHANGE; | |
| 1504 | if (DOINGSOFTDEP(tvp)) | |
| 1505 | softdep_change_linkcnt(ip); | |
| 1506 | vput(tvp); | |
| 1507 | } | |
| 1508 | out: | |
| 1509 | return (error); | |
| 1510 | } | |
| 1511 | ||
| 1512 | /* | |
| 1513 | * Rmdir system call. | |
| 0973c589 CP |
1514 | * |
| 1515 | * ufs_rmdir(struct vnode *a_dvp, struct vnode *a_vp, | |
| 1516 | * struct componentname *a_cnp) | |
| 984263bc | 1517 | */ |
| 0961aa92 | 1518 | static |
| 984263bc | 1519 | int |
| e62afb5f | 1520 | ufs_rmdir(struct vop_old_rmdir_args *ap) |
| 984263bc MD |
1521 | { |
| 1522 | struct vnode *vp = ap->a_vp; | |
| 1523 | struct vnode *dvp = ap->a_dvp; | |
| 1524 | struct componentname *cnp = ap->a_cnp; | |
| 1525 | struct inode *ip, *dp; | |
| 1526 | int error, ioflag; | |
| 1527 | ||
| 1528 | ip = VTOI(vp); | |
| 1529 | dp = VTOI(dvp); | |
| 1530 | ||
| 1531 | /* | |
| 1532 | * Do not remove a directory that is in the process of being renamed. | |
| 1533 | * Verify the directory is empty (and valid). Rmdir ".." will not be | |
| 1534 | * valid since ".." will contain a reference to the current directory | |
| 1535 | * and thus be non-empty. Do not allow the removal of mounted on | |
| 1536 | * directories (this can happen when an NFS exported filesystem | |
| 1537 | * tries to remove a locally mounted on directory). | |
| 1538 | */ | |
| 1539 | error = 0; | |
| 1540 | if (ip->i_flag & IN_RENAME) { | |
| 1541 | error = EINVAL; | |
| 1542 | goto out; | |
| 1543 | } | |
| 1544 | if (ip->i_effnlink != 2 || | |
| 1545 | !ufs_dirempty(ip, dp->i_number, cnp->cn_cred)) { | |
| 1546 | error = ENOTEMPTY; | |
| 1547 | goto out; | |
| 1548 | } | |
| 3a907475 | 1549 | #if 0 /* handled by kernel now */ |
| 984263bc MD |
1550 | if ((dp->i_flags & APPEND) |
| 1551 | || (ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))) { | |
| 1552 | error = EPERM; | |
| 1553 | goto out; | |
| 1554 | } | |
| 3a907475 | 1555 | #endif |
| 984263bc MD |
1556 | /* |
| 1557 | * Delete reference to directory before purging | |
| 1558 | * inode. If we crash in between, the directory | |
| 1559 | * will be reattached to lost+found, | |
| 1560 | */ | |
| 1561 | dp->i_effnlink--; | |
| 1562 | ip->i_effnlink--; | |
| 1563 | if (DOINGSOFTDEP(vp)) { | |
| 1564 | softdep_change_linkcnt(dp); | |
| 1565 | softdep_change_linkcnt(ip); | |
| 1566 | } | |
| 1567 | error = ufs_dirremove(dvp, ip, cnp->cn_flags, 1); | |
| 1568 | if (error) { | |
| 1569 | dp->i_effnlink++; | |
| 1570 | ip->i_effnlink++; | |
| 1571 | if (DOINGSOFTDEP(vp)) { | |
| 1572 | softdep_change_linkcnt(dp); | |
| 1573 | softdep_change_linkcnt(ip); | |
| 1574 | } | |
| 1575 | goto out; | |
| 1576 | } | |
| 1577 | VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); | |
| 984263bc MD |
1578 | /* |
| 1579 | * Truncate inode. The only stuff left in the directory is "." and | |
| 1580 | * "..". The "." reference is inconsequential since we are quashing | |
| 1581 | * it. The soft dependency code will arrange to do these operations | |
| 1582 | * after the parent directory entry has been deleted on disk, so | |
| 1583 | * when running with that code we avoid doing them now. | |
| 1584 | */ | |
| 1585 | if (!DOINGSOFTDEP(vp)) { | |
| 1586 | dp->i_nlink--; | |
| 1587 | dp->i_flag |= IN_CHANGE; | |
| 1588 | ip->i_nlink--; | |
| 1589 | ip->i_flag |= IN_CHANGE; | |
| 1590 | ioflag = DOINGASYNC(vp) ? 0 : IO_SYNC; | |
| ac690a1d | 1591 | error = ffs_truncate(vp, (off_t)0, ioflag, cnp->cn_cred); |
| 984263bc | 1592 | } |
| fad57d0e | 1593 | /* cache_purge removed - handled by VFS compat layer */ |
| 984263bc MD |
1594 | #ifdef UFS_DIRHASH |
| 1595 | /* Kill any active hash; i_effnlink == 0, so it will not come back. */ | |
| 1596 | if (ip->i_dirhash != NULL) | |
| 1597 | ufsdirhash_free(ip); | |
| 1598 | #endif | |
| 1599 | out: | |
| 1600 | VN_KNOTE(vp, NOTE_DELETE); | |
| 1601 | return (error); | |
| 1602 | } | |
| 1603 | ||
| 1604 | /* | |
| 1605 | * symlink -- make a symbolic link | |
| 0973c589 CP |
1606 | * |
| 1607 | * ufs_symlink(struct vnode *a_dvp, struct vnode **a_vpp, | |
| 1608 | * struct componentname *a_cnp, struct vattr *a_vap, | |
| 1609 | * char *a_target) | |
| 984263bc | 1610 | */ |
| 0961aa92 | 1611 | static |
| 984263bc | 1612 | int |
| e62afb5f | 1613 | ufs_symlink(struct vop_old_symlink_args *ap) |
| 984263bc | 1614 | { |
| 3ff2135f RG |
1615 | struct vnode *vp, **vpp = ap->a_vpp; |
| 1616 | struct inode *ip; | |
| 984263bc MD |
1617 | int len, error; |
| 1618 | ||
| 1619 | error = ufs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp, | |
| 83a5effe | 1620 | vpp, ap->a_cnp); |
| 984263bc MD |
1621 | if (error) |
| 1622 | return (error); | |
| 1623 | VN_KNOTE(ap->a_dvp, NOTE_WRITE); | |
| 1624 | vp = *vpp; | |
| 1625 | len = strlen(ap->a_target); | |
| 1626 | if (len < vp->v_mount->mnt_maxsymlinklen) { | |
| 1627 | ip = VTOI(vp); | |
| 1628 | bcopy(ap->a_target, (char *)ip->i_shortlink, len); | |
| 1629 | ip->i_size = len; | |
| 1630 | ip->i_flag |= IN_CHANGE | IN_UPDATE; | |
| 83a5effe MD |
1631 | } else { |
| 1632 | /* | |
| 1633 | * Make sure we have a VM object in order to use | |
| 1634 | * the buffer cache. | |
| 1635 | */ | |
| 1636 | if (vp->v_object == NULL) | |
| 1c843a13 | 1637 | vinitvmio(vp, 0); |
| 984263bc | 1638 | error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0, |
| 83a5effe | 1639 | UIO_SYSSPACE, IO_NODELOCKED, |
| 87de5057 | 1640 | ap->a_cnp->cn_cred, NULL); |
| 83a5effe | 1641 | } |
| 984263bc MD |
1642 | if (error) |
| 1643 | vput(vp); | |
| 1644 | return (error); | |
| 1645 | } | |
| 1646 | ||
| 1647 | /* | |
| 1648 | * Vnode op for reading directories. | |
| 1649 | * | |
| 0973c589 | 1650 | * ufs_readdir(struct vnode *a_vp, struct uio *a_uio, struct ucred *a_cred, |
| 84009d92 | 1651 | * int *a_eofflag, int *ncookies, off_t **a_cookies) |
| 984263bc | 1652 | */ |
| 0961aa92 | 1653 | static |
| 984263bc | 1654 | int |
| 0973c589 | 1655 | ufs_readdir(struct vop_readdir_args *ap) |
| 984263bc | 1656 | { |
| 3ff2135f | 1657 | struct uio *uio = ap->a_uio; |
| 7f8e5940 | 1658 | struct vnode *vp = ap->a_vp; |
| fb0466c9 MD |
1659 | struct direct *dp; |
| 1660 | struct buf *bp; | |
| 1661 | int retval; | |
| 1662 | int error; | |
| 1663 | int offset; /* offset into buffer cache buffer */ | |
| 1664 | int eoffset; /* end of buffer clipped to file EOF */ | |
| 1665 | int pickup; /* pickup point */ | |
| 7f8e5940 | 1666 | int ncookies; |
| fb0466c9 | 1667 | int cookie_index; |
| 84009d92 | 1668 | off_t *cookies; |
| 984263bc | 1669 | |
| fb0466c9 MD |
1670 | if (uio->uio_offset < 0) |
| 1671 | return (EINVAL); | |
| e088dc32 | 1672 | /* |
| fb0466c9 MD |
1673 | * Guess the number of cookies needed. Make sure we compute at |
| 1674 | * least 1, and no more then a reasonable limit. | |
| e088dc32 | 1675 | */ |
| fb0466c9 MD |
1676 | if (ap->a_ncookies) { |
| 1677 | ncookies = uio->uio_resid / 16 + 1; | |
| 1678 | if (ncookies > 1024) | |
| 1679 | ncookies = 1024; | |
| 84009d92 | 1680 | cookies = kmalloc(ncookies * sizeof(off_t), M_TEMP, M_WAITOK); |
| fb0466c9 MD |
1681 | } else { |
| 1682 | ncookies = -1; /* force conditionals below */ | |
| 1683 | cookies = NULL; | |
| 1684 | } | |
| 1685 | cookie_index = 0; | |
| 1686 | ||
| 885ecb13 MD |
1687 | if ((error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY)) != 0) |
| 1688 | return (error); | |
| 1689 | ||
| fb0466c9 MD |
1690 | /* |
| 1691 | * Past or at EOF | |
| 1692 | */ | |
| 1693 | if (uio->uio_offset >= VTOI(vp)->i_size) { | |
| 1694 | if (ap->a_eofflag) | |
| 1695 | *ap->a_eofflag = 1; | |
| 1696 | if (ap->a_ncookies) { | |
| 1697 | *ap->a_ncookies = cookie_index; | |
| 1698 | *ap->a_cookies = cookies; | |
| 1699 | } | |
| 885ecb13 | 1700 | goto done; |
| fb0466c9 MD |
1701 | } |
| 1702 | ||
| 1703 | /* | |
| 1704 | * Loop until we run out of cookies, we run out of user buffer, | |
| 1705 | * or we hit the directory EOF. | |
| 1706 | * | |
| 1707 | * Always start scans at the beginning of the buffer, don't trust | |
| 1708 | * the offset supplied by userland. | |
| 1709 | */ | |
| 9ba4b517 | 1710 | while ((error = ffs_blkatoff_ra(vp, uio->uio_offset, NULL, &bp, 2)) == 0) { |
| fb0466c9 MD |
1711 | pickup = (int)(uio->uio_offset - bp->b_loffset); |
| 1712 | offset = 0; | |
| 1713 | retval = 0; | |
| 1714 | if (bp->b_loffset + bp->b_bcount > VTOI(vp)->i_size) | |
| 1715 | eoffset = (int)(VTOI(vp)->i_size - bp->b_loffset); | |
| 1716 | else | |
| 1717 | eoffset = bp->b_bcount; | |
| 1718 | ||
| 1719 | while (offset < eoffset) { | |
| 1720 | dp = (struct direct *)(bp->b_data + offset); | |
| 1721 | if (dp->d_reclen <= 0 || (dp->d_reclen & 3) || | |
| 1722 | offset + dp->d_reclen > bp->b_bcount) { | |
| 82fd1984 JS |
1723 | error = EIO; |
| 1724 | break; | |
| 1725 | } | |
| fb0466c9 MD |
1726 | if (offsetof(struct direct, d_name[dp->d_namlen]) > dp->d_reclen) { |
| 1727 | error = EIO; | |
| 1728 | break; | |
| 1729 | } | |
| 1730 | if (offset < pickup) { | |
| 1731 | offset += dp->d_reclen; | |
| 1732 | continue; | |
| 1733 | } | |
| e088dc32 | 1734 | #if BYTE_ORDER == LITTLE_ENDIAN |
| 7f8e5940 | 1735 | if (OFSFMT(vp)) { |
| 82fd1984 JS |
1736 | retval = vop_write_dirent(&error, uio, |
| 1737 | dp->d_ino, dp->d_namlen, dp->d_type, | |
| 1738 | dp->d_name); | |
| e088dc32 JS |
1739 | } else |
| 1740 | #endif | |
| 1741 | { | |
| 82fd1984 JS |
1742 | retval = vop_write_dirent(&error, uio, |
| 1743 | dp->d_ino, dp->d_type, dp->d_namlen, | |
| 1744 | dp->d_name); | |
| e088dc32 | 1745 | } |
| 82fd1984 | 1746 | if (retval) |
| e088dc32 | 1747 | break; |
| 84009d92 MD |
1748 | if (cookies) |
| 1749 | cookies[cookie_index] = bp->b_loffset + offset; | |
| fb0466c9 MD |
1750 | ++cookie_index; |
| 1751 | offset += dp->d_reclen; | |
| 1752 | if (cookie_index == ncookies) | |
| 1753 | break; | |
| 1754 | } | |
| 1755 | ||
| 1756 | /* | |
| 1757 | * This will align the next loop to the beginning of the | |
| 1758 | * next block, and pickup will calculate to 0. | |
| 1759 | */ | |
| 1760 | uio->uio_offset = bp->b_loffset + offset; | |
| 1761 | brelse(bp); | |
| 1762 | ||
| 1763 | if (retval || error || cookie_index == ncookies || | |
| 1764 | uio->uio_offset >= VTOI(vp)->i_size) { | |
| 1765 | break; | |
| 984263bc | 1766 | } |
| 984263bc | 1767 | } |
| 984263bc | 1768 | if (ap->a_eofflag) |
| 7f8e5940 | 1769 | *ap->a_eofflag = VTOI(vp)->i_size <= uio->uio_offset; |
| fb0466c9 MD |
1770 | |
| 1771 | /* | |
| 1772 | * Report errors only if we didn't manage to read anything | |
| 1773 | */ | |
| 1774 | if (error && cookie_index == 0) { | |
| 1775 | if (cookies) { | |
| efda3bd0 | 1776 | kfree(cookies, M_TEMP); |
| fb0466c9 MD |
1777 | *ap->a_ncookies = 0; |
| 1778 | *ap->a_cookies = NULL; | |
| 1779 | } | |
| 1780 | } else { | |
| 1781 | error = 0; | |
| 1782 | if (cookies) { | |
| 1783 | *ap->a_ncookies = cookie_index; | |
| 1784 | *ap->a_cookies = cookies; | |
| 1785 | } | |
| 1786 | } | |
| 885ecb13 MD |
1787 | done: |
| 1788 | vn_unlock(vp); | |
| e088dc32 | 1789 | return (error); |
| 984263bc MD |
1790 | } |
| 1791 | ||
| 1792 | /* | |
| 1793 | * Return target name of a symbolic link | |
| 0973c589 CP |
1794 | * |
| 1795 | * ufs_readlink(struct vnode *a_vp, struct uio *a_uio, struct ucred *a_cred) | |
| 984263bc | 1796 | */ |
| 0961aa92 | 1797 | static |
| 984263bc | 1798 | int |
| 0973c589 | 1799 | ufs_readlink(struct vop_readlink_args *ap) |
| 984263bc | 1800 | { |
| 3ff2135f RG |
1801 | struct vnode *vp = ap->a_vp; |
| 1802 | struct inode *ip = VTOI(vp); | |
| 984263bc MD |
1803 | int isize; |
| 1804 | ||
| 1805 | isize = ip->i_size; | |
| 1806 | if ((isize < vp->v_mount->mnt_maxsymlinklen) || | |
| f719c866 | 1807 | (ip->i_din.di_blocks == 0)) { /* XXX - for old fastlink support */ |
| 984263bc MD |
1808 | uiomove((char *)ip->i_shortlink, isize, ap->a_uio); |
| 1809 | return (0); | |
| 1810 | } | |
| 83a5effe MD |
1811 | |
| 1812 | /* | |
| 1813 | * Perform the equivalent of an OPEN on vp so we can issue a | |
| 1814 | * VOP_READ. | |
| 1815 | */ | |
| 984263bc MD |
1816 | return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); |
| 1817 | } | |
| 1818 | ||
| 1819 | /* | |
| 1820 | * Calculate the logical to physical mapping if not done already, | |
| 1821 | * then call the device strategy routine. | |
| 1822 | * | |
| 1823 | * In order to be able to swap to a file, the VOP_BMAP operation may not | |
| 1824 | * deadlock on memory. See ufs_bmap() for details. | |
| 0973c589 | 1825 | * |
| 81b5c339 | 1826 | * ufs_strategy(struct vnode *a_vp, struct bio *a_bio) |
| 984263bc | 1827 | */ |
| 0961aa92 | 1828 | static |
| 984263bc | 1829 | int |
| 0973c589 | 1830 | ufs_strategy(struct vop_strategy_args *ap) |
| 984263bc | 1831 | { |
| 81b5c339 MD |
1832 | struct bio *bio = ap->a_bio; |
| 1833 | struct bio *nbio; | |
| 1834 | struct buf *bp = bio->bio_buf; | |
| 3ff2135f RG |
1835 | struct vnode *vp = ap->a_vp; |
| 1836 | struct inode *ip; | |
| 984263bc MD |
1837 | int error; |
| 1838 | ||
| 1839 | ip = VTOI(vp); | |
| 1840 | if (vp->v_type == VBLK || vp->v_type == VCHR) | |
| 1841 | panic("ufs_strategy: spec"); | |
| 81b5c339 | 1842 | nbio = push_bio(bio); |
| 54078292 | 1843 | if (nbio->bio_offset == NOOFFSET) { |
| 08daea96 | 1844 | error = VOP_BMAP(vp, bio->bio_offset, &nbio->bio_offset, |
| e92ca23a | 1845 | NULL, NULL, bp->b_cmd); |
| 984263bc MD |
1846 | if (error) { |
| 1847 | bp->b_error = error; | |
| 1848 | bp->b_flags |= B_ERROR; | |
| 81b5c339 MD |
1849 | /* I/O was never started on nbio, must biodone(bio) */ |
| 1850 | biodone(bio); | |
| 984263bc MD |
1851 | return (error); |
| 1852 | } | |
| 54078292 | 1853 | if (nbio->bio_offset == NOOFFSET) |
| 984263bc MD |
1854 | vfs_bio_clrbuf(bp); |
| 1855 | } | |
| 54078292 | 1856 | if (nbio->bio_offset == NOOFFSET) { |
| 1f97d073 MD |
1857 | /* |
| 1858 | * We hit a hole in the file. The buffer has been zero-filled | |
| 1859 | * so just biodone() it. | |
| 1860 | */ | |
| 81b5c339 | 1861 | biodone(bio); |
| 1f97d073 MD |
1862 | } else { |
| 1863 | vn_strategy(ip->i_devvp, nbio); | |
| 984263bc | 1864 | } |
| 984263bc MD |
1865 | return (0); |
| 1866 | } | |
| 1867 | ||
| 1868 | /* | |
| 1869 | * Print out the contents of an inode. | |
| 0973c589 CP |
1870 | * |
| 1871 | * ufs_print(struct vnode *a_vp) | |
| 984263bc | 1872 | */ |
| 0961aa92 | 1873 | static |
| 984263bc | 1874 | int |
| 0973c589 | 1875 | ufs_print(struct vop_print_args *ap) |
| 984263bc | 1876 | { |
| 3ff2135f RG |
1877 | struct vnode *vp = ap->a_vp; |
| 1878 | struct inode *ip = VTOI(vp); | |
| 984263bc | 1879 | |
| 086c1d7e | 1880 | kprintf("tag VT_UFS, ino %lu, on dev %s (%d, %d)", |
| 984263bc MD |
1881 | (u_long)ip->i_number, devtoname(ip->i_dev), major(ip->i_dev), |
| 1882 | minor(ip->i_dev)); | |
| 1883 | if (vp->v_type == VFIFO) | |
| 1884 | fifo_printinfo(vp); | |
| 3446c007 | 1885 | lockmgr_printinfo(&vp->v_lock); |
| 086c1d7e | 1886 | kprintf("\n"); |
| 984263bc MD |
1887 | return (0); |
| 1888 | } | |
| 1889 | ||
| 1890 | /* | |
| 1891 | * Read wrapper for special devices. | |
| 0973c589 CP |
1892 | * |
| 1893 | * ufsspec_read(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, | |
| 1894 | * struct ucred *a_cred) | |
| 984263bc | 1895 | */ |
| 0961aa92 | 1896 | static |
| 984263bc | 1897 | int |
| 0973c589 | 1898 | ufsspec_read(struct vop_read_args *ap) |
| 984263bc MD |
1899 | { |
| 1900 | int error, resid; | |
| 1901 | struct inode *ip; | |
| 1902 | struct uio *uio; | |
| 1903 | ||
| 1904 | uio = ap->a_uio; | |
| 1905 | resid = uio->uio_resid; | |
| 66a1ddf5 | 1906 | error = VOCALL(&spec_vnode_vops, &ap->a_head); |
| 984263bc MD |
1907 | /* |
| 1908 | * The inode may have been revoked during the call, so it must not | |
| 1909 | * be accessed blindly here or in the other wrapper functions. | |
| 1910 | */ | |
| 1911 | ip = VTOI(ap->a_vp); | |
| 1912 | if (ip != NULL && (uio->uio_resid != resid || (error == 0 && resid != 0))) | |
| 1913 | ip->i_flag |= IN_ACCESS; | |
| 1914 | return (error); | |
| 1915 | } | |
| 1916 | ||
| 1917 | /* | |
| 1918 | * Write wrapper for special devices. | |
| 0973c589 CP |
1919 | * |
| 1920 | * ufsspec_write(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, | |
| 1921 | * struct ucred *a_cred) | |
| 984263bc | 1922 | */ |
| 0961aa92 | 1923 | static |
| 984263bc | 1924 | int |
| 0973c589 | 1925 | ufsspec_write(struct vop_write_args *ap) |
| 984263bc MD |
1926 | { |
| 1927 | int error, resid; | |
| 1928 | struct inode *ip; | |
| 1929 | struct uio *uio; | |
| 1930 | ||
| 1931 | uio = ap->a_uio; | |
| 1932 | resid = uio->uio_resid; | |
| 66a1ddf5 | 1933 | error = VOCALL(&spec_vnode_vops, &ap->a_head); |
| 984263bc MD |
1934 | ip = VTOI(ap->a_vp); |
| 1935 | if (ip != NULL && (uio->uio_resid != resid || (error == 0 && resid != 0))) | |
| 1936 | VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE; | |
| 1937 | return (error); | |
| 1938 | } | |
| 1939 | ||
| 1940 | /* | |
| 1941 | * Close wrapper for special devices. | |
| 1942 | * | |
| 1943 | * Update the times on the inode then do device close. | |
| 0973c589 | 1944 | * |
| b478fdce | 1945 | * ufsspec_close(struct vnode *a_vp, int a_fflag) |
| 984263bc | 1946 | */ |
| 0961aa92 | 1947 | static |
| 984263bc | 1948 | int |
| 0973c589 | 1949 | ufsspec_close(struct vop_close_args *ap) |
| 984263bc MD |
1950 | { |
| 1951 | struct vnode *vp = ap->a_vp; | |
| 1952 | ||
| 3c37c940 | 1953 | if (vp->v_sysref.refcnt > 1) |
| 984263bc | 1954 | ufs_itimes(vp); |
| 66a1ddf5 | 1955 | return (VOCALL(&spec_vnode_vops, &ap->a_head)); |
| 984263bc MD |
1956 | } |
| 1957 | ||
| 1958 | /* | |
| 1959 | * Read wrapper for fifos. | |
| 0973c589 CP |
1960 | * |
| 1961 | * ufsfifo_read(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, | |
| 1962 | * struct ucred *a_cred) | |
| 984263bc | 1963 | */ |
| 0961aa92 | 1964 | static |
| 984263bc | 1965 | int |
| 0973c589 | 1966 | ufsfifo_read(struct vop_read_args *ap) |
| 984263bc MD |
1967 | { |
| 1968 | int error, resid; | |
| 1969 | struct inode *ip; | |
| 1970 | struct uio *uio; | |
| 1971 | ||
| 1972 | uio = ap->a_uio; | |
| 1973 | resid = uio->uio_resid; | |
| 66a1ddf5 | 1974 | error = VOCALL(&fifo_vnode_vops, &ap->a_head); |
| 984263bc MD |
1975 | ip = VTOI(ap->a_vp); |
| 1976 | if ((ap->a_vp->v_mount->mnt_flag & MNT_NOATIME) == 0 && ip != NULL && | |
| 1977 | (uio->uio_resid != resid || (error == 0 && resid != 0))) | |
| 1978 | VTOI(ap->a_vp)->i_flag |= IN_ACCESS; | |
| 1979 | return (error); | |
| 1980 | } | |
| 1981 | ||
| 1982 | /* | |
| 1983 | * Write wrapper for fifos. | |
| 0973c589 CP |
1984 | * |
| 1985 | * ufsfifo_write(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, | |
| 1986 | * struct ucred *a_cred) | |
| 984263bc | 1987 | */ |
| 0961aa92 | 1988 | static |
| 984263bc | 1989 | int |
| 0973c589 | 1990 | ufsfifo_write(struct vop_write_args *ap) |
| 984263bc MD |
1991 | { |
| 1992 | int error, resid; | |
| 1993 | struct inode *ip; | |
| 1994 | struct uio *uio; | |
| 1995 | ||
| 1996 | uio = ap->a_uio; | |
| 1997 | resid = uio->uio_resid; | |
| 66a1ddf5 | 1998 | error = VOCALL(&fifo_vnode_vops, &ap->a_head); |
| 984263bc MD |
1999 | ip = VTOI(ap->a_vp); |
| 2000 | if (ip != NULL && (uio->uio_resid != resid || (error == 0 && resid != 0))) | |
| 2001 | VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE; | |
| 2002 | return (error); | |
| 2003 | } | |
| 2004 | ||
| 2005 | /* | |
| 2006 | * Close wrapper for fifos. | |
| 2007 | * | |
| 2008 | * Update the times on the inode then do device close. | |
| 0973c589 | 2009 | * |
| b478fdce | 2010 | * ufsfifo_close(struct vnode *a_vp, int a_fflag) |
| 984263bc | 2011 | */ |
| 0961aa92 | 2012 | static |
| 984263bc | 2013 | int |
| 0973c589 | 2014 | ufsfifo_close(struct vop_close_args *ap) |
| 984263bc MD |
2015 | { |
| 2016 | struct vnode *vp = ap->a_vp; | |
| 2017 | ||
| 3c37c940 | 2018 | if (vp->v_sysref.refcnt > 1) |
| 984263bc | 2019 | ufs_itimes(vp); |
| 66a1ddf5 | 2020 | return (VOCALL(&fifo_vnode_vops, &ap->a_head)); |
| 984263bc MD |
2021 | } |
| 2022 | ||
| 2023 | /* | |
| 2024 | * Kqfilter wrapper for fifos. | |
| 2025 | * | |
| 2026 | * Fall through to ufs kqfilter routines if needed | |
| 2027 | */ | |
| 0961aa92 | 2028 | static |
| 984263bc | 2029 | int |
| 0973c589 | 2030 | ufsfifo_kqfilter(struct vop_kqfilter_args *ap) |
| 984263bc MD |
2031 | { |
| 2032 | int error; | |
| 2033 | ||
| 66a1ddf5 | 2034 | error = VOCALL(&fifo_vnode_vops, &ap->a_head); |
| 984263bc MD |
2035 | if (error) |
| 2036 | error = ufs_kqfilter(ap); | |
| 2037 | return (error); | |
| 2038 | } | |
| 2039 | ||
| 2040 | /* | |
| 984263bc | 2041 | * Advisory record locking support |
| 0973c589 CP |
2042 | * |
| 2043 | * ufs_advlock(struct vnode *a_vp, caddr_t a_id, int a_op, struct flock *a_fl, | |
| 2044 | * int a_flags) | |
| 984263bc | 2045 | */ |
| 0961aa92 | 2046 | static |
| 984263bc | 2047 | int |
| 0973c589 | 2048 | ufs_advlock(struct vop_advlock_args *ap) |
| 984263bc | 2049 | { |
| 3ff2135f | 2050 | struct inode *ip = VTOI(ap->a_vp); |
| 984263bc MD |
2051 | |
| 2052 | return (lf_advlock(ap, &(ip->i_lockf), ip->i_size)); | |
| 2053 | } | |
| 2054 | ||
| 2055 | /* | |
| 2056 | * Initialize the vnode associated with a new inode, handle aliased | |
| 2057 | * vnodes. | |
| 3e72e117 MD |
2058 | * |
| 2059 | * Make sure directories have their VM object now rather then later, | |
| 2060 | * saving us from having to check on all the myrid directory VOPs | |
| 2061 | * that might be executed without a VOP_OPEN being performed. | |
| 984263bc MD |
2062 | */ |
| 2063 | int | |
| 0961aa92 | 2064 | ufs_vinit(struct mount *mntp, struct vnode **vpp) |
| 984263bc MD |
2065 | { |
| 2066 | struct inode *ip; | |
| 2067 | struct vnode *vp; | |
| 2068 | struct timeval tv; | |
| 2069 | ||
| 2070 | vp = *vpp; | |
| 2071 | ip = VTOI(vp); | |
| 57f7b636 | 2072 | |
| 1c843a13 MD |
2073 | vp->v_type = IFTOVT(ip->i_mode); |
| 2074 | ||
| 2075 | switch(vp->v_type) { | |
| 984263bc MD |
2076 | case VCHR: |
| 2077 | case VBLK: | |
| 6ddb7618 | 2078 | vp->v_ops = &mntp->mnt_vn_spec_ops; |
| 0e9b9130 | 2079 | addaliasu(vp, umajor(ip->i_rdev), uminor(ip->i_rdev)); |
| 984263bc MD |
2080 | break; |
| 2081 | case VFIFO: | |
| 6ddb7618 | 2082 | vp->v_ops = &mntp->mnt_vn_fifo_ops; |
| 984263bc | 2083 | break; |
| 3e72e117 | 2084 | case VDIR: |
| 1c843a13 MD |
2085 | case VREG: |
| 2086 | vinitvmio(vp, ip->i_size); | |
| 2087 | break; | |
| 2088 | case VLNK: | |
| 2089 | if (ip->i_size >= vp->v_mount->mnt_maxsymlinklen) | |
| 2090 | vinitvmio(vp, ip->i_size); | |
| 3e72e117 | 2091 | break; |
| 984263bc MD |
2092 | default: |
| 2093 | break; | |
| 2094 | ||
| 2095 | } | |
| 57f7b636 | 2096 | |
| 984263bc MD |
2097 | if (ip->i_number == ROOTINO) |
| 2098 | vp->v_flag |= VROOT; | |
| 2099 | /* | |
| 2100 | * Initialize modrev times | |
| 2101 | */ | |
| 2102 | getmicrouptime(&tv); | |
| 2103 | SETHIGH(ip->i_modrev, tv.tv_sec); | |
| 2104 | SETLOW(ip->i_modrev, tv.tv_usec * 4294); | |
| 2105 | *vpp = vp; | |
| 2106 | return (0); | |
| 2107 | } | |
| 2108 | ||
| 2109 | /* | |
| 2110 | * Allocate a new inode. | |
| 2111 | */ | |
| 0961aa92 | 2112 | static |
| 984263bc | 2113 | int |
| 0973c589 CP |
2114 | ufs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp, |
| 2115 | struct componentname *cnp) | |
| 984263bc | 2116 | { |
| 3ff2135f | 2117 | struct inode *ip, *pdir; |
| 984263bc MD |
2118 | struct direct newdir; |
| 2119 | struct vnode *tvp; | |
| 2120 | int error; | |
| 2121 | ||
| 2122 | pdir = VTOI(dvp); | |
| 984263bc MD |
2123 | *vpp = NULL; |
| 2124 | if ((mode & IFMT) == 0) | |
| 2125 | mode |= IFREG; | |
| 2126 | ||
| ac690a1d | 2127 | error = ffs_valloc(dvp, mode, cnp->cn_cred, &tvp); |
| 984263bc MD |
2128 | if (error) |
| 2129 | return (error); | |
| 2130 | ip = VTOI(tvp); | |
| 33036abf | 2131 | ip->i_flags = pdir->i_flags & (SF_NOHISTORY|UF_NOHISTORY|UF_NODUMP); |
| 984263bc MD |
2132 | ip->i_gid = pdir->i_gid; |
| 2133 | #ifdef SUIDDIR | |
| 2134 | { | |
| 2135 | #ifdef QUOTA | |
| 2136 | struct ucred ucred, *ucp; | |
| 2137 | ucp = cnp->cn_cred; | |
| 2138 | #endif | |
| 2139 | /* | |
| 2140 | * If we are not the owner of the directory, | |
| 2141 | * and we are hacking owners here, (only do this where told to) | |
| 2142 | * and we are not giving it TO root, (would subvert quotas) | |
| 2143 | * then go ahead and give it to the other user. | |
| 2144 | * Note that this drops off the execute bits for security. | |
| 2145 | */ | |
| 2146 | if ((dvp->v_mount->mnt_flag & MNT_SUIDDIR) && | |
| 2147 | (pdir->i_mode & ISUID) && | |
| 2148 | (pdir->i_uid != cnp->cn_cred->cr_uid) && pdir->i_uid) { | |
| 2149 | ip->i_uid = pdir->i_uid; | |
| 2150 | mode &= ~07111; | |
| 2151 | #ifdef QUOTA | |
| 2152 | /* | |
| 2153 | * Make sure the correct user gets charged | |
| 2154 | * for the space. | |
| 2155 | * Quickly knock up a dummy credential for the victim. | |
| 2156 | * XXX This seems to never be accessed out of our | |
| 2157 | * context so a stack variable is ok. | |
| 2158 | */ | |
| 2159 | ucred.cr_ref = 1; | |
| 2160 | ucred.cr_uid = ip->i_uid; | |
| 2161 | ucred.cr_ngroups = 1; | |
| 2162 | ucred.cr_groups[0] = pdir->i_gid; | |
| 2163 | ucp = &ucred; | |
| 2164 | #endif | |
| 2165 | } else | |
| 2166 | ip->i_uid = cnp->cn_cred->cr_uid; | |
| 2167 | ||
| 2168 | #ifdef QUOTA | |
| 50e58362 MD |
2169 | if ((error = ufs_getinoquota(ip)) || |
| 2170 | (error = ufs_chkiq(ip, 1, ucp, 0))) { | |
| ac690a1d | 2171 | ffs_vfree(tvp, ip->i_number, mode); |
| 984263bc MD |
2172 | vput(tvp); |
| 2173 | return (error); | |
| 2174 | } | |
| 2175 | #endif | |
| 2176 | } | |
| 2177 | #else /* !SUIDDIR */ | |
| 2178 | ip->i_uid = cnp->cn_cred->cr_uid; | |
| 2179 | #ifdef QUOTA | |
| 50e58362 MD |
2180 | if ((error = ufs_getinoquota(ip)) || |
| 2181 | (error = ufs_chkiq(ip, 1, cnp->cn_cred, 0))) { | |
| ac690a1d | 2182 | ffs_vfree(tvp, ip->i_number, mode); |
| 984263bc MD |
2183 | vput(tvp); |
| 2184 | return (error); | |
| 2185 | } | |
| 2186 | #endif | |
| 2187 | #endif /* !SUIDDIR */ | |
| 6b008938 | 2188 | ip->i_fsmid = cache_getnewfsmid(); |
| 984263bc MD |
2189 | ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; |
| 2190 | ip->i_mode = mode; | |
| 2191 | tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */ | |
| 2192 | ip->i_effnlink = 1; | |
| 2193 | ip->i_nlink = 1; | |
| 2194 | if (DOINGSOFTDEP(tvp)) | |
| 2195 | softdep_change_linkcnt(ip); | |
| 2196 | if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred) && | |
| 895c1f85 | 2197 | priv_check_cred(cnp->cn_cred, PRIV_ROOT, 0)) { |
| 984263bc | 2198 | ip->i_mode &= ~ISGID; |
| 41c20dac | 2199 | } |
| 984263bc | 2200 | |
| 2b69e610 | 2201 | if (cnp->cn_flags & CNP_ISWHITEOUT) |
| 984263bc MD |
2202 | ip->i_flags |= UF_OPAQUE; |
| 2203 | ||
| 2204 | /* | |
| 1c843a13 MD |
2205 | * Regular files and directories need VM objects. Softlinks do |
| 2206 | * not (not immediately anyway). | |
| 2207 | */ | |
| 2208 | if (tvp->v_type == VREG || tvp->v_type == VDIR) | |
| 2209 | vinitvmio(tvp, 0); | |
| 2210 | ||
| 2211 | /* | |
| 984263bc MD |
2212 | * Make sure inode goes to disk before directory entry. |
| 2213 | */ | |
| ac690a1d | 2214 | error = ffs_update(tvp, !(DOINGSOFTDEP(tvp) | DOINGASYNC(tvp))); |
| 984263bc MD |
2215 | if (error) |
| 2216 | goto bad; | |
| 2217 | ufs_makedirentry(ip, cnp, &newdir); | |
| 2218 | error = ufs_direnter(dvp, tvp, &newdir, cnp, NULL); | |
| 2219 | if (error) | |
| 2220 | goto bad; | |
| 2221 | *vpp = tvp; | |
| 2222 | return (0); | |
| 2223 | ||
| 2224 | bad: | |
| 2225 | /* | |
| 2226 | * Write error occurred trying to update the inode | |
| 2227 | * or the directory so must deallocate the inode. | |
| 2228 | */ | |
| 2229 | ip->i_effnlink = 0; | |
| 2230 | ip->i_nlink = 0; | |
| 2231 | ip->i_flag |= IN_CHANGE; | |
| 2232 | if (DOINGSOFTDEP(tvp)) | |
| 2233 | softdep_change_linkcnt(ip); | |
| 2234 | vput(tvp); | |
| 2235 | return (error); | |
| 2236 | } | |
| 2237 | ||
| 2238 | static int | |
| 0973c589 | 2239 | ufs_missingop(struct vop_generic_args *ap) |
| 984263bc | 2240 | { |
| 31bd717a | 2241 | panic("no vop function for %s in ufs child", ap->a_desc->sd_name); |
| 984263bc MD |
2242 | return (EOPNOTSUPP); |
| 2243 | } | |
| 2244 | ||
| 2245 | static struct filterops ufsread_filtops = | |
| 2246 | { 1, NULL, filt_ufsdetach, filt_ufsread }; | |
| 2247 | static struct filterops ufswrite_filtops = | |
| 2248 | { 1, NULL, filt_ufsdetach, filt_ufswrite }; | |
| 2249 | static struct filterops ufsvnode_filtops = | |
| 2250 | { 1, NULL, filt_ufsdetach, filt_ufsvnode }; | |
| 2251 | ||
| 0973c589 CP |
2252 | /* |
| 2253 | * ufs_kqfilter(struct vnode *a_vp, struct knote *a_kn) | |
| 2254 | */ | |
| 984263bc | 2255 | static int |
| 0973c589 | 2256 | ufs_kqfilter(struct vop_kqfilter_args *ap) |
| 984263bc MD |
2257 | { |
| 2258 | struct vnode *vp = ap->a_vp; | |
| 2259 | struct knote *kn = ap->a_kn; | |
| 41a01a4d | 2260 | lwkt_tokref ilock; |
| 984263bc MD |
2261 | |
| 2262 | switch (kn->kn_filter) { | |
| 2263 | case EVFILT_READ: | |
| 2264 | kn->kn_fop = &ufsread_filtops; | |
| 2265 | break; | |
| 2266 | case EVFILT_WRITE: | |
| 2267 | kn->kn_fop = &ufswrite_filtops; | |
| 2268 | break; | |
| 2269 | case EVFILT_VNODE: | |
| 2270 | kn->kn_fop = &ufsvnode_filtops; | |
| 2271 | break; | |
| 2272 | default: | |
| 2273 | return (1); | |
| 2274 | } | |
| 2275 | ||
| 2276 | kn->kn_hook = (caddr_t)vp; | |
| 2277 | ||
| 41a01a4d | 2278 | lwkt_gettoken(&ilock, &vp->v_pollinfo.vpi_token); |
| 984263bc | 2279 | SLIST_INSERT_HEAD(&vp->v_pollinfo.vpi_selinfo.si_note, kn, kn_selnext); |
| 41a01a4d | 2280 | lwkt_reltoken(&ilock); |
| 984263bc MD |
2281 | |
| 2282 | return (0); | |
| 2283 | } | |
| 2284 | ||
| 2285 | static void | |
| 2286 | filt_ufsdetach(struct knote *kn) | |
| 2287 | { | |
| 2288 | struct vnode *vp = (struct vnode *)kn->kn_hook; | |
| 41a01a4d | 2289 | lwkt_tokref ilock; |
| 984263bc | 2290 | |
| 41a01a4d | 2291 | lwkt_gettoken(&ilock, &vp->v_pollinfo.vpi_token); |
| 984263bc MD |
2292 | SLIST_REMOVE(&vp->v_pollinfo.vpi_selinfo.si_note, |
| 2293 | kn, knote, kn_selnext); | |
| 41a01a4d | 2294 | lwkt_reltoken(&ilock); |
| 984263bc MD |
2295 | } |
| 2296 | ||
| 2297 | /*ARGSUSED*/ | |
| 2298 | static int | |
| 2299 | filt_ufsread(struct knote *kn, long hint) | |
| 2300 | { | |
| 2301 | struct vnode *vp = (struct vnode *)kn->kn_hook; | |
| 2302 | struct inode *ip = VTOI(vp); | |
| 2303 | ||
| 2304 | /* | |
| 2305 | * filesystem is gone, so set the EOF flag and schedule | |
| 2306 | * the knote for deletion. | |
| 2307 | */ | |
| 2308 | if (hint == NOTE_REVOKE) { | |
| 2309 | kn->kn_flags |= (EV_EOF | EV_ONESHOT); | |
| 2310 | return (1); | |
| 2311 | } | |
| 2312 | ||
| 2313 | kn->kn_data = ip->i_size - kn->kn_fp->f_offset; | |
| 2314 | return (kn->kn_data != 0); | |
| 2315 | } | |
| 2316 | ||
| 2317 | /*ARGSUSED*/ | |
| 2318 | static int | |
| 2319 | filt_ufswrite(struct knote *kn, long hint) | |
| 2320 | { | |
| 984263bc MD |
2321 | /* |
| 2322 | * filesystem is gone, so set the EOF flag and schedule | |
| 2323 | * the knote for deletion. | |
| 2324 | */ | |
| 2325 | if (hint == NOTE_REVOKE) | |
| 2326 | kn->kn_flags |= (EV_EOF | EV_ONESHOT); | |
| 2327 | ||
| 2328 | kn->kn_data = 0; | |
| 2329 | return (1); | |
| 2330 | } | |
| 2331 | ||
| 2332 | static int | |
| 2333 | filt_ufsvnode(struct knote *kn, long hint) | |
| 2334 | { | |
| 984263bc MD |
2335 | if (kn->kn_sfflags & hint) |
| 2336 | kn->kn_fflags |= hint; | |
| 2337 | if (hint == NOTE_REVOKE) { | |
| 2338 | kn->kn_flags |= EV_EOF; | |
| 2339 | return (1); | |
| 2340 | } | |
| 2341 | return (kn->kn_fflags != 0); | |
| 2342 | } | |
| 2343 | ||
| 2344 | /* Global vfs data structures for ufs. */ | |
| 66a1ddf5 MD |
2345 | static struct vop_ops ufs_vnode_vops = { |
| 2346 | .vop_default = vop_defaultop, | |
| 2347 | .vop_fsync = (void *)ufs_missingop, | |
| 2348 | .vop_read = (void *)ufs_missingop, | |
| 2349 | .vop_reallocblks = (void *)ufs_missingop, | |
| 2350 | .vop_write = (void *)ufs_missingop, | |
| 2351 | .vop_access = ufs_access, | |
| 2352 | .vop_advlock = ufs_advlock, | |
| 2353 | .vop_bmap = ufs_bmap, | |
| 2354 | .vop_old_lookup = ufs_lookup, | |
| 2355 | .vop_close = ufs_close, | |
| 2356 | .vop_old_create = ufs_create, | |
| 2357 | .vop_getattr = ufs_getattr, | |
| 2358 | .vop_inactive = ufs_inactive, | |
| 66a1ddf5 | 2359 | .vop_old_link = ufs_link, |
| 64950f31 MD |
2360 | .vop_old_mkdir = ufs_mkdir, |
| 2361 | .vop_old_mknod = ufs_mknod, | |
| 66a1ddf5 | 2362 | .vop_mmap = ufs_mmap, |
| 3a907475 | 2363 | .vop_open = vop_stdopen, |
| 64950f31 | 2364 | .vop_pathconf = vop_stdpathconf, |
| 66a1ddf5 MD |
2365 | .vop_poll = vop_stdpoll, |
| 2366 | .vop_kqfilter = ufs_kqfilter, | |
| 2367 | .vop_print = ufs_print, | |
| 2368 | .vop_readdir = ufs_readdir, | |
| 2369 | .vop_readlink = ufs_readlink, | |
| 2370 | .vop_reclaim = ufs_reclaim, | |
| 2371 | .vop_old_remove = ufs_remove, | |
| 2372 | .vop_old_rename = ufs_rename, | |
| 2373 | .vop_old_rmdir = ufs_rmdir, | |
| 2374 | .vop_setattr = ufs_setattr, | |
| 349433c9 | 2375 | .vop_markatime = ufs_markatime, |
| 66a1ddf5 MD |
2376 | .vop_strategy = ufs_strategy, |
| 2377 | .vop_old_symlink = ufs_symlink, | |
| 66a1ddf5 | 2378 | .vop_old_whiteout = ufs_whiteout |
| 984263bc | 2379 | }; |
| 66a1ddf5 MD |
2380 | |
| 2381 | static struct vop_ops ufs_spec_vops = { | |
| 2382 | .vop_default = spec_vnoperate, | |
| 2383 | .vop_fsync = (void *)ufs_missingop, | |
| 2384 | .vop_access = ufs_access, | |
| 2385 | .vop_close = ufsspec_close, | |
| 2386 | .vop_getattr = ufs_getattr, | |
| 2387 | .vop_inactive = ufs_inactive, | |
| 66a1ddf5 MD |
2388 | .vop_print = ufs_print, |
| 2389 | .vop_read = ufsspec_read, | |
| 2390 | .vop_reclaim = ufs_reclaim, | |
| 2391 | .vop_setattr = ufs_setattr, | |
| 349433c9 | 2392 | .vop_markatime = ufs_markatime, |
| 66a1ddf5 | 2393 | .vop_write = ufsspec_write |
| 984263bc | 2394 | }; |
| 66a1ddf5 MD |
2395 | |
| 2396 | static struct vop_ops ufs_fifo_vops = { | |
| 2397 | .vop_default = fifo_vnoperate, | |
| 2398 | .vop_fsync = (void *)ufs_missingop, | |
| 2399 | .vop_access = ufs_access, | |
| 2400 | .vop_close = ufsfifo_close, | |
| 2401 | .vop_getattr = ufs_getattr, | |
| 2402 | .vop_inactive = ufs_inactive, | |
| 66a1ddf5 | 2403 | .vop_kqfilter = ufsfifo_kqfilter, |
| 66a1ddf5 MD |
2404 | .vop_print = ufs_print, |
| 2405 | .vop_read = ufsfifo_read, | |
| 2406 | .vop_reclaim = ufs_reclaim, | |
| 2407 | .vop_setattr = ufs_setattr, | |
| 349433c9 | 2408 | .vop_markatime = ufs_markatime, |
| 66a1ddf5 | 2409 | .vop_write = ufsfifo_write |
| 984263bc | 2410 | }; |
| 984263bc | 2411 | |
| 66a1ddf5 MD |
2412 | VNODEOP_SET(ufs_vnode_vops); |
| 2413 | VNODEOP_SET(ufs_spec_vops); | |
| 2414 | VNODEOP_SET(ufs_fifo_vops); | |
| 984263bc | 2415 | |
| 0973c589 | 2416 | /* |
| 31bd717a | 2417 | * ufs_vnoperate() |
| 0973c589 | 2418 | */ |
| 984263bc | 2419 | int |
| 0973c589 | 2420 | ufs_vnoperate(struct vop_generic_args *ap) |
| 984263bc | 2421 | { |
| 66a1ddf5 | 2422 | return (VOCALL(&ufs_vnode_vops, ap)); |
| 984263bc MD |
2423 | } |
| 2424 | ||
| 0973c589 | 2425 | /* |
| 31bd717a | 2426 | * ufs_vnoperatefifo() |
| 0973c589 | 2427 | */ |
| 984263bc | 2428 | int |
| 0973c589 | 2429 | ufs_vnoperatefifo(struct vop_generic_args *ap) |
| 984263bc | 2430 | { |
| 66a1ddf5 | 2431 | return (VOCALL(&ufs_fifo_vops, ap)); |
| 984263bc MD |
2432 | } |
| 2433 | ||
| 0973c589 | 2434 | /* |
| 31bd717a | 2435 | * ufs_vnoperatespec() |
| 0973c589 | 2436 | */ |
| 984263bc | 2437 | int |
| 0973c589 | 2438 | ufs_vnoperatespec(struct vop_generic_args *ap) |
| 984263bc | 2439 | { |
| 66a1ddf5 | 2440 | return (VOCALL(&ufs_spec_vops, ap)); |
| 984263bc | 2441 | } |