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 | ||
984263bc MD |
267 | /* |
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 | ||
fad57d0e MD |
1216 | /* |
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 | ||
984263bc MD |
1226 | /* |
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 | ||
83a5effe MD |
1427 | /* |
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 | 1432 | |
984263bc MD |
1433 | /* |
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 | ||
984263bc MD |
2040 | /* |
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 | ||
1c843a13 MD |
2204 | /* |
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 | ||
984263bc MD |
2211 | /* |
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 | } |