1 | /* $NetBSD: ufs_vnops.c,v 1.234 2016/11/09 04:12:55 dholland Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Wasabi Systems, 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 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * Copyright (c) 1982, 1986, 1989, 1993, 1995 |
34 | * The Regents of the University of California. All rights reserved. |
35 | * (c) UNIX System Laboratories, Inc. |
36 | * All or some portions of this file are derived from material licensed |
37 | * to the University of California by American Telephone and Telegraph |
38 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with |
39 | * the permission of UNIX System Laboratories, Inc. |
40 | * |
41 | * Redistribution and use in source and binary forms, with or without |
42 | * modification, are permitted provided that the following conditions |
43 | * are met: |
44 | * 1. Redistributions of source code must retain the above copyright |
45 | * notice, this list of conditions and the following disclaimer. |
46 | * 2. Redistributions in binary form must reproduce the above copyright |
47 | * notice, this list of conditions and the following disclaimer in the |
48 | * documentation and/or other materials provided with the distribution. |
49 | * 3. Neither the name of the University nor the names of its contributors |
50 | * may be used to endorse or promote products derived from this software |
51 | * without specific prior written permission. |
52 | * |
53 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
54 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
55 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
56 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
57 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
58 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
59 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
60 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
61 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
62 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
63 | * SUCH DAMAGE. |
64 | * |
65 | * @(#)ufs_vnops.c 8.28 (Berkeley) 7/31/95 |
66 | */ |
67 | |
68 | #include <sys/cdefs.h> |
69 | __KERNEL_RCSID(0, "$NetBSD: ufs_vnops.c,v 1.234 2016/11/09 04:12:55 dholland Exp $" ); |
70 | |
71 | #if defined(_KERNEL_OPT) |
72 | #include "opt_ffs.h" |
73 | #include "opt_quota.h" |
74 | #endif |
75 | |
76 | #include <sys/param.h> |
77 | #include <sys/systm.h> |
78 | #include <sys/namei.h> |
79 | #include <sys/resourcevar.h> |
80 | #include <sys/kernel.h> |
81 | #include <sys/file.h> |
82 | #include <sys/stat.h> |
83 | #include <sys/buf.h> |
84 | #include <sys/proc.h> |
85 | #include <sys/mount.h> |
86 | #include <sys/vnode.h> |
87 | #include <sys/kmem.h> |
88 | #include <sys/malloc.h> |
89 | #include <sys/dirent.h> |
90 | #include <sys/lockf.h> |
91 | #include <sys/kauth.h> |
92 | #include <sys/wapbl.h> |
93 | #include <sys/fstrans.h> |
94 | |
95 | #include <miscfs/specfs/specdev.h> |
96 | #include <miscfs/fifofs/fifo.h> |
97 | #include <miscfs/genfs/genfs.h> |
98 | |
99 | #include <ufs/ufs/inode.h> |
100 | #include <ufs/ufs/dir.h> |
101 | #include <ufs/ufs/ufsmount.h> |
102 | #include <ufs/ufs/ufs_bswap.h> |
103 | #include <ufs/ufs/ufs_extern.h> |
104 | #include <ufs/ufs/ufs_wapbl.h> |
105 | #ifdef UFS_DIRHASH |
106 | #include <ufs/ufs/dirhash.h> |
107 | #endif |
108 | #include <ufs/ext2fs/ext2fs_extern.h> |
109 | #include <ufs/ext2fs/ext2fs_dir.h> |
110 | #include <ufs/ffs/ffs_extern.h> |
111 | #include <ufs/lfs/lfs_extern.h> |
112 | #include <ufs/lfs/lfs.h> |
113 | |
114 | #include <uvm/uvm.h> |
115 | |
116 | __CTASSERT(EXT2FS_MAXNAMLEN == FFS_MAXNAMLEN); |
117 | __CTASSERT(LFS_MAXNAMLEN == FFS_MAXNAMLEN); |
118 | |
119 | static int ufs_chmod(struct vnode *, int, kauth_cred_t, struct lwp *); |
120 | static int ufs_chown(struct vnode *, uid_t, gid_t, kauth_cred_t, |
121 | struct lwp *); |
122 | static int ufs_makeinode(struct vattr *, struct vnode *, |
123 | const struct ufs_lookup_results *, struct vnode **, struct componentname *); |
124 | |
125 | /* |
126 | * A virgin directory (no blushing please). |
127 | */ |
128 | static const struct dirtemplate mastertemplate = { |
129 | 0, 12, DT_DIR, 1, "." , |
130 | 0, UFS_DIRBLKSIZ - 12, DT_DIR, 2, ".." |
131 | }; |
132 | |
133 | /* |
134 | * Create a regular file |
135 | */ |
136 | int |
137 | ufs_create(void *v) |
138 | { |
139 | struct vop_create_v3_args /* { |
140 | struct vnode *a_dvp; |
141 | struct vnode **a_vpp; |
142 | struct componentname *a_cnp; |
143 | struct vattr *a_vap; |
144 | } */ *ap = v; |
145 | int error; |
146 | struct vnode *dvp = ap->a_dvp; |
147 | struct ufs_lookup_results *ulr; |
148 | |
149 | /* XXX should handle this material another way */ |
150 | ulr = &VTOI(dvp)->i_crap; |
151 | UFS_CHECK_CRAPCOUNTER(VTOI(dvp)); |
152 | |
153 | /* |
154 | * UFS_WAPBL_BEGIN(dvp->v_mount) performed by successful |
155 | * ufs_makeinode |
156 | */ |
157 | fstrans_start(dvp->v_mount, FSTRANS_SHARED); |
158 | error = ufs_makeinode(ap->a_vap, dvp, ulr, ap->a_vpp, ap->a_cnp); |
159 | if (error) { |
160 | fstrans_done(dvp->v_mount); |
161 | return (error); |
162 | } |
163 | UFS_WAPBL_END(dvp->v_mount); |
164 | fstrans_done(dvp->v_mount); |
165 | VN_KNOTE(dvp, NOTE_WRITE); |
166 | VOP_UNLOCK(*ap->a_vpp); |
167 | return (0); |
168 | } |
169 | |
170 | /* |
171 | * Mknod vnode call |
172 | */ |
173 | /* ARGSUSED */ |
174 | int |
175 | ufs_mknod(void *v) |
176 | { |
177 | struct vop_mknod_v3_args /* { |
178 | struct vnode *a_dvp; |
179 | struct vnode **a_vpp; |
180 | struct componentname *a_cnp; |
181 | struct vattr *a_vap; |
182 | } */ *ap = v; |
183 | struct vattr *vap; |
184 | struct vnode **vpp; |
185 | struct inode *ip; |
186 | int error; |
187 | struct ufs_lookup_results *ulr; |
188 | |
189 | vap = ap->a_vap; |
190 | vpp = ap->a_vpp; |
191 | |
192 | /* XXX should handle this material another way */ |
193 | ulr = &VTOI(ap->a_dvp)->i_crap; |
194 | UFS_CHECK_CRAPCOUNTER(VTOI(ap->a_dvp)); |
195 | |
196 | /* |
197 | * UFS_WAPBL_BEGIN(dvp->v_mount) performed by successful |
198 | * ufs_makeinode |
199 | */ |
200 | fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED); |
201 | if ((error = ufs_makeinode(vap, ap->a_dvp, ulr, vpp, ap->a_cnp)) != 0) |
202 | goto out; |
203 | VN_KNOTE(ap->a_dvp, NOTE_WRITE); |
204 | ip = VTOI(*vpp); |
205 | ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; |
206 | UFS_WAPBL_UPDATE(*vpp, NULL, NULL, 0); |
207 | UFS_WAPBL_END(ap->a_dvp->v_mount); |
208 | VOP_UNLOCK(*vpp); |
209 | out: |
210 | fstrans_done(ap->a_dvp->v_mount); |
211 | if (error != 0) { |
212 | *vpp = NULL; |
213 | return (error); |
214 | } |
215 | return (0); |
216 | } |
217 | |
218 | /* |
219 | * Open called. |
220 | * |
221 | * Nothing to do. |
222 | */ |
223 | /* ARGSUSED */ |
224 | int |
225 | ufs_open(void *v) |
226 | { |
227 | struct vop_open_args /* { |
228 | struct vnode *a_vp; |
229 | int a_mode; |
230 | kauth_cred_t a_cred; |
231 | } */ *ap = v; |
232 | |
233 | /* |
234 | * Files marked append-only must be opened for appending. |
235 | */ |
236 | if ((VTOI(ap->a_vp)->i_flags & APPEND) && |
237 | (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) |
238 | return (EPERM); |
239 | return (0); |
240 | } |
241 | |
242 | /* |
243 | * Close called. |
244 | * |
245 | * Update the times on the inode. |
246 | */ |
247 | /* ARGSUSED */ |
248 | int |
249 | ufs_close(void *v) |
250 | { |
251 | struct vop_close_args /* { |
252 | struct vnode *a_vp; |
253 | int a_fflag; |
254 | kauth_cred_t a_cred; |
255 | } */ *ap = v; |
256 | struct vnode *vp; |
257 | |
258 | vp = ap->a_vp; |
259 | fstrans_start(vp->v_mount, FSTRANS_SHARED); |
260 | if (vp->v_usecount > 1) |
261 | UFS_ITIMES(vp, NULL, NULL, NULL); |
262 | fstrans_done(vp->v_mount); |
263 | return (0); |
264 | } |
265 | |
266 | static int |
267 | ufs_check_possible(struct vnode *vp, struct inode *ip, mode_t mode, |
268 | kauth_cred_t cred) |
269 | { |
270 | #if defined(QUOTA) || defined(QUOTA2) |
271 | int error; |
272 | #endif |
273 | |
274 | /* |
275 | * Disallow write attempts on read-only file systems; |
276 | * unless the file is a socket, fifo, or a block or |
277 | * character device resident on the file system. |
278 | */ |
279 | if (mode & VWRITE) { |
280 | switch (vp->v_type) { |
281 | case VDIR: |
282 | case VLNK: |
283 | case VREG: |
284 | if (vp->v_mount->mnt_flag & MNT_RDONLY) |
285 | return (EROFS); |
286 | #if defined(QUOTA) || defined(QUOTA2) |
287 | fstrans_start(vp->v_mount, FSTRANS_SHARED); |
288 | error = chkdq(ip, 0, cred, 0); |
289 | fstrans_done(vp->v_mount); |
290 | if (error != 0) |
291 | return error; |
292 | #endif |
293 | break; |
294 | case VBAD: |
295 | case VBLK: |
296 | case VCHR: |
297 | case VSOCK: |
298 | case VFIFO: |
299 | case VNON: |
300 | default: |
301 | break; |
302 | } |
303 | } |
304 | |
305 | /* If it is a snapshot, nobody gets access to it. */ |
306 | if ((ip->i_flags & SF_SNAPSHOT)) |
307 | return (EPERM); |
308 | /* If immutable bit set, nobody gets to write it. */ |
309 | if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE)) |
310 | return (EPERM); |
311 | |
312 | return 0; |
313 | } |
314 | |
315 | static int |
316 | ufs_check_permitted(struct vnode *vp, struct inode *ip, mode_t mode, |
317 | kauth_cred_t cred) |
318 | { |
319 | |
320 | return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode, vp->v_type, |
321 | ip->i_mode & ALLPERMS), vp, NULL, genfs_can_access(vp->v_type, |
322 | ip->i_mode & ALLPERMS, ip->i_uid, ip->i_gid, mode, cred)); |
323 | } |
324 | |
325 | int |
326 | ufs_access(void *v) |
327 | { |
328 | struct vop_access_args /* { |
329 | struct vnode *a_vp; |
330 | int a_mode; |
331 | kauth_cred_t a_cred; |
332 | } */ *ap = v; |
333 | struct vnode *vp; |
334 | struct inode *ip; |
335 | mode_t mode; |
336 | int error; |
337 | |
338 | vp = ap->a_vp; |
339 | ip = VTOI(vp); |
340 | mode = ap->a_mode; |
341 | |
342 | error = ufs_check_possible(vp, ip, mode, ap->a_cred); |
343 | if (error) |
344 | return error; |
345 | |
346 | error = ufs_check_permitted(vp, ip, mode, ap->a_cred); |
347 | |
348 | return error; |
349 | } |
350 | |
351 | /* ARGSUSED */ |
352 | int |
353 | ufs_getattr(void *v) |
354 | { |
355 | struct vop_getattr_args /* { |
356 | struct vnode *a_vp; |
357 | struct vattr *a_vap; |
358 | kauth_cred_t a_cred; |
359 | } */ *ap = v; |
360 | struct vnode *vp; |
361 | struct inode *ip; |
362 | struct vattr *vap; |
363 | |
364 | vp = ap->a_vp; |
365 | ip = VTOI(vp); |
366 | vap = ap->a_vap; |
367 | fstrans_start(vp->v_mount, FSTRANS_SHARED); |
368 | UFS_ITIMES(vp, NULL, NULL, NULL); |
369 | |
370 | /* |
371 | * Copy from inode table |
372 | */ |
373 | vap->va_fsid = ip->i_dev; |
374 | vap->va_fileid = ip->i_number; |
375 | vap->va_mode = ip->i_mode & ALLPERMS; |
376 | vap->va_nlink = ip->i_nlink; |
377 | vap->va_uid = ip->i_uid; |
378 | vap->va_gid = ip->i_gid; |
379 | vap->va_size = vp->v_size; |
380 | if (ip->i_ump->um_fstype == UFS1) { |
381 | switch (vp->v_type) { |
382 | case VBLK: |
383 | case VCHR: |
384 | vap->va_rdev = (dev_t)ufs_rw32(ip->i_ffs1_rdev, |
385 | UFS_MPNEEDSWAP(ip->i_ump)); |
386 | break; |
387 | default: |
388 | vap->va_rdev = NODEV; |
389 | break; |
390 | } |
391 | vap->va_atime.tv_sec = ip->i_ffs1_atime; |
392 | vap->va_atime.tv_nsec = ip->i_ffs1_atimensec; |
393 | vap->va_mtime.tv_sec = ip->i_ffs1_mtime; |
394 | vap->va_mtime.tv_nsec = ip->i_ffs1_mtimensec; |
395 | vap->va_ctime.tv_sec = ip->i_ffs1_ctime; |
396 | vap->va_ctime.tv_nsec = ip->i_ffs1_ctimensec; |
397 | vap->va_birthtime.tv_sec = 0; |
398 | vap->va_birthtime.tv_nsec = 0; |
399 | vap->va_bytes = dbtob((u_quad_t)ip->i_ffs1_blocks); |
400 | } else { |
401 | switch (vp->v_type) { |
402 | case VBLK: |
403 | case VCHR: |
404 | vap->va_rdev = (dev_t)ufs_rw64(ip->i_ffs2_rdev, |
405 | UFS_MPNEEDSWAP(ip->i_ump)); |
406 | break; |
407 | default: |
408 | vap->va_rdev = NODEV; |
409 | break; |
410 | } |
411 | vap->va_atime.tv_sec = ip->i_ffs2_atime; |
412 | vap->va_atime.tv_nsec = ip->i_ffs2_atimensec; |
413 | vap->va_mtime.tv_sec = ip->i_ffs2_mtime; |
414 | vap->va_mtime.tv_nsec = ip->i_ffs2_mtimensec; |
415 | vap->va_ctime.tv_sec = ip->i_ffs2_ctime; |
416 | vap->va_ctime.tv_nsec = ip->i_ffs2_ctimensec; |
417 | vap->va_birthtime.tv_sec = ip->i_ffs2_birthtime; |
418 | vap->va_birthtime.tv_nsec = ip->i_ffs2_birthnsec; |
419 | vap->va_bytes = dbtob(ip->i_ffs2_blocks); |
420 | } |
421 | vap->va_gen = ip->i_gen; |
422 | vap->va_flags = ip->i_flags; |
423 | |
424 | /* this doesn't belong here */ |
425 | if (vp->v_type == VBLK) |
426 | vap->va_blocksize = BLKDEV_IOSIZE; |
427 | else if (vp->v_type == VCHR) |
428 | vap->va_blocksize = MAXBSIZE; |
429 | else |
430 | vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; |
431 | vap->va_type = vp->v_type; |
432 | vap->va_filerev = ip->i_modrev; |
433 | fstrans_done(vp->v_mount); |
434 | return (0); |
435 | } |
436 | |
437 | /* |
438 | * Set attribute vnode op. called from several syscalls |
439 | */ |
440 | int |
441 | ufs_setattr(void *v) |
442 | { |
443 | struct vop_setattr_args /* { |
444 | struct vnode *a_vp; |
445 | struct vattr *a_vap; |
446 | kauth_cred_t a_cred; |
447 | } */ *ap = v; |
448 | struct vattr *vap; |
449 | struct vnode *vp; |
450 | struct inode *ip; |
451 | kauth_cred_t cred; |
452 | struct lwp *l; |
453 | int error; |
454 | kauth_action_t action; |
455 | bool changing_sysflags; |
456 | |
457 | vap = ap->a_vap; |
458 | vp = ap->a_vp; |
459 | ip = VTOI(vp); |
460 | cred = ap->a_cred; |
461 | l = curlwp; |
462 | action = KAUTH_VNODE_WRITE_FLAGS; |
463 | changing_sysflags = false; |
464 | |
465 | /* |
466 | * Check for unsettable attributes. |
467 | */ |
468 | if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || |
469 | (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || |
470 | (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || |
471 | ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { |
472 | return (EINVAL); |
473 | } |
474 | |
475 | UFS_WAPBL_JUNLOCK_ASSERT(vp->v_mount); |
476 | |
477 | fstrans_start(vp->v_mount, FSTRANS_SHARED); |
478 | |
479 | if (vap->va_flags != VNOVAL) { |
480 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { |
481 | error = EROFS; |
482 | goto out; |
483 | } |
484 | |
485 | /* Snapshot flag cannot be set or cleared */ |
486 | if ((vap->va_flags & (SF_SNAPSHOT | SF_SNAPINVAL)) != |
487 | (ip->i_flags & (SF_SNAPSHOT | SF_SNAPINVAL))) { |
488 | error = EPERM; |
489 | goto out; |
490 | } |
491 | |
492 | if (ip->i_flags & (SF_IMMUTABLE | SF_APPEND)) { |
493 | action |= KAUTH_VNODE_HAS_SYSFLAGS; |
494 | } |
495 | |
496 | if ((vap->va_flags & SF_SETTABLE) != |
497 | (ip->i_flags & SF_SETTABLE)) { |
498 | action |= KAUTH_VNODE_WRITE_SYSFLAGS; |
499 | changing_sysflags = true; |
500 | } |
501 | |
502 | error = kauth_authorize_vnode(cred, action, vp, NULL, |
503 | genfs_can_chflags(cred, vp->v_type, ip->i_uid, |
504 | changing_sysflags)); |
505 | if (error) |
506 | goto out; |
507 | |
508 | if (changing_sysflags) { |
509 | error = UFS_WAPBL_BEGIN(vp->v_mount); |
510 | if (error) |
511 | goto out; |
512 | ip->i_flags = vap->va_flags; |
513 | DIP_ASSIGN(ip, flags, ip->i_flags); |
514 | } else { |
515 | error = UFS_WAPBL_BEGIN(vp->v_mount); |
516 | if (error) |
517 | goto out; |
518 | ip->i_flags &= SF_SETTABLE; |
519 | ip->i_flags |= (vap->va_flags & UF_SETTABLE); |
520 | DIP_ASSIGN(ip, flags, ip->i_flags); |
521 | } |
522 | ip->i_flag |= IN_CHANGE; |
523 | UFS_WAPBL_UPDATE(vp, NULL, NULL, 0); |
524 | UFS_WAPBL_END(vp->v_mount); |
525 | if (vap->va_flags & (IMMUTABLE | APPEND)) { |
526 | error = 0; |
527 | goto out; |
528 | } |
529 | } |
530 | if (ip->i_flags & (IMMUTABLE | APPEND)) { |
531 | error = EPERM; |
532 | goto out; |
533 | } |
534 | /* |
535 | * Go through the fields and update iff not VNOVAL. |
536 | */ |
537 | if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { |
538 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { |
539 | error = EROFS; |
540 | goto out; |
541 | } |
542 | error = UFS_WAPBL_BEGIN(vp->v_mount); |
543 | if (error) |
544 | goto out; |
545 | error = ufs_chown(vp, vap->va_uid, vap->va_gid, cred, l); |
546 | UFS_WAPBL_END(vp->v_mount); |
547 | if (error) |
548 | goto out; |
549 | } |
550 | if (vap->va_size != VNOVAL) { |
551 | /* |
552 | * Disallow write attempts on read-only file systems; |
553 | * unless the file is a socket, fifo, or a block or |
554 | * character device resident on the file system. |
555 | */ |
556 | switch (vp->v_type) { |
557 | case VDIR: |
558 | error = EISDIR; |
559 | goto out; |
560 | case VCHR: |
561 | case VBLK: |
562 | case VFIFO: |
563 | break; |
564 | case VREG: |
565 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { |
566 | error = EROFS; |
567 | goto out; |
568 | } |
569 | if ((ip->i_flags & SF_SNAPSHOT) != 0) { |
570 | error = EPERM; |
571 | goto out; |
572 | } |
573 | error = ufs_truncate_retry(vp, vap->va_size, cred); |
574 | if (error) |
575 | goto out; |
576 | break; |
577 | default: |
578 | error = EOPNOTSUPP; |
579 | goto out; |
580 | } |
581 | } |
582 | ip = VTOI(vp); |
583 | if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || |
584 | vap->va_birthtime.tv_sec != VNOVAL) { |
585 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { |
586 | error = EROFS; |
587 | goto out; |
588 | } |
589 | if ((ip->i_flags & SF_SNAPSHOT) != 0) { |
590 | error = EPERM; |
591 | goto out; |
592 | } |
593 | error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp, |
594 | NULL, genfs_can_chtimes(vp, vap->va_vaflags, ip->i_uid, cred)); |
595 | if (error) |
596 | goto out; |
597 | error = UFS_WAPBL_BEGIN(vp->v_mount); |
598 | if (error) |
599 | goto out; |
600 | if (vap->va_atime.tv_sec != VNOVAL) |
601 | if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) |
602 | ip->i_flag |= IN_ACCESS; |
603 | if (vap->va_mtime.tv_sec != VNOVAL) { |
604 | ip->i_flag |= IN_CHANGE | IN_UPDATE; |
605 | if (vp->v_mount->mnt_flag & MNT_RELATIME) |
606 | ip->i_flag |= IN_ACCESS; |
607 | } |
608 | if (vap->va_birthtime.tv_sec != VNOVAL && |
609 | ip->i_ump->um_fstype == UFS2) { |
610 | ip->i_ffs2_birthtime = vap->va_birthtime.tv_sec; |
611 | ip->i_ffs2_birthnsec = vap->va_birthtime.tv_nsec; |
612 | } |
613 | error = UFS_UPDATE(vp, &vap->va_atime, &vap->va_mtime, 0); |
614 | UFS_WAPBL_END(vp->v_mount); |
615 | if (error) |
616 | goto out; |
617 | } |
618 | error = 0; |
619 | if (vap->va_mode != (mode_t)VNOVAL) { |
620 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { |
621 | error = EROFS; |
622 | goto out; |
623 | } |
624 | if ((ip->i_flags & SF_SNAPSHOT) != 0 && |
625 | (vap->va_mode & (S_IXUSR | S_IWUSR | S_IXGRP | S_IWGRP | |
626 | S_IXOTH | S_IWOTH))) { |
627 | error = EPERM; |
628 | goto out; |
629 | } |
630 | error = UFS_WAPBL_BEGIN(vp->v_mount); |
631 | if (error) |
632 | goto out; |
633 | error = ufs_chmod(vp, (int)vap->va_mode, cred, l); |
634 | UFS_WAPBL_END(vp->v_mount); |
635 | } |
636 | VN_KNOTE(vp, NOTE_ATTRIB); |
637 | out: |
638 | fstrans_done(vp->v_mount); |
639 | return (error); |
640 | } |
641 | |
642 | /* |
643 | * Change the mode on a file. |
644 | * Inode must be locked before calling. |
645 | */ |
646 | static int |
647 | ufs_chmod(struct vnode *vp, int mode, kauth_cred_t cred, struct lwp *l) |
648 | { |
649 | struct inode *ip; |
650 | int error; |
651 | |
652 | UFS_WAPBL_JLOCK_ASSERT(vp->v_mount); |
653 | |
654 | ip = VTOI(vp); |
655 | |
656 | error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp, |
657 | NULL, genfs_can_chmod(vp->v_type, cred, ip->i_uid, ip->i_gid, mode)); |
658 | if (error) |
659 | return (error); |
660 | |
661 | fstrans_start(vp->v_mount, FSTRANS_SHARED); |
662 | ip->i_mode &= ~ALLPERMS; |
663 | ip->i_mode |= (mode & ALLPERMS); |
664 | ip->i_flag |= IN_CHANGE; |
665 | DIP_ASSIGN(ip, mode, ip->i_mode); |
666 | UFS_WAPBL_UPDATE(vp, NULL, NULL, 0); |
667 | fstrans_done(vp->v_mount); |
668 | return (0); |
669 | } |
670 | |
671 | /* |
672 | * Perform chown operation on inode ip; |
673 | * inode must be locked prior to call. |
674 | */ |
675 | static int |
676 | ufs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred, |
677 | struct lwp *l) |
678 | { |
679 | struct inode *ip; |
680 | int error = 0; |
681 | #if defined(QUOTA) || defined(QUOTA2) |
682 | uid_t ouid; |
683 | gid_t ogid; |
684 | int64_t change; |
685 | #endif |
686 | ip = VTOI(vp); |
687 | error = 0; |
688 | |
689 | if (uid == (uid_t)VNOVAL) |
690 | uid = ip->i_uid; |
691 | if (gid == (gid_t)VNOVAL) |
692 | gid = ip->i_gid; |
693 | |
694 | error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp, |
695 | NULL, genfs_can_chown(cred, ip->i_uid, ip->i_gid, uid, gid)); |
696 | if (error) |
697 | return (error); |
698 | |
699 | fstrans_start(vp->v_mount, FSTRANS_SHARED); |
700 | #if defined(QUOTA) || defined(QUOTA2) |
701 | ogid = ip->i_gid; |
702 | ouid = ip->i_uid; |
703 | change = DIP(ip, blocks); |
704 | (void) chkdq(ip, -change, cred, 0); |
705 | (void) chkiq(ip, -1, cred, 0); |
706 | #endif |
707 | ip->i_gid = gid; |
708 | DIP_ASSIGN(ip, gid, gid); |
709 | ip->i_uid = uid; |
710 | DIP_ASSIGN(ip, uid, uid); |
711 | #if defined(QUOTA) || defined(QUOTA2) |
712 | if ((error = chkdq(ip, change, cred, 0)) == 0) { |
713 | if ((error = chkiq(ip, 1, cred, 0)) == 0) |
714 | goto good; |
715 | else |
716 | (void) chkdq(ip, -change, cred, FORCE); |
717 | } |
718 | ip->i_gid = ogid; |
719 | DIP_ASSIGN(ip, gid, ogid); |
720 | ip->i_uid = ouid; |
721 | DIP_ASSIGN(ip, uid, ouid); |
722 | (void) chkdq(ip, change, cred, FORCE); |
723 | (void) chkiq(ip, 1, cred, FORCE); |
724 | fstrans_done(vp->v_mount); |
725 | return (error); |
726 | good: |
727 | #endif /* QUOTA || QUOTA2 */ |
728 | ip->i_flag |= IN_CHANGE; |
729 | UFS_WAPBL_UPDATE(vp, NULL, NULL, 0); |
730 | fstrans_done(vp->v_mount); |
731 | return (0); |
732 | } |
733 | |
734 | int |
735 | ufs_remove(void *v) |
736 | { |
737 | struct vop_remove_args /* { |
738 | struct vnode *a_dvp; |
739 | struct vnode *a_vp; |
740 | struct componentname *a_cnp; |
741 | } */ *ap = v; |
742 | struct vnode *vp, *dvp; |
743 | struct inode *ip; |
744 | struct mount *mp; |
745 | int error; |
746 | struct ufs_lookup_results *ulr; |
747 | |
748 | vp = ap->a_vp; |
749 | dvp = ap->a_dvp; |
750 | ip = VTOI(vp); |
751 | mp = dvp->v_mount; |
752 | KASSERT(mp == vp->v_mount); /* XXX Not stable without lock. */ |
753 | |
754 | /* XXX should handle this material another way */ |
755 | ulr = &VTOI(dvp)->i_crap; |
756 | UFS_CHECK_CRAPCOUNTER(VTOI(dvp)); |
757 | |
758 | fstrans_start(mp, FSTRANS_SHARED); |
759 | if (vp->v_type == VDIR || (ip->i_flags & (IMMUTABLE | APPEND)) || |
760 | (VTOI(dvp)->i_flags & APPEND)) |
761 | error = EPERM; |
762 | else { |
763 | error = UFS_WAPBL_BEGIN(mp); |
764 | if (error == 0) { |
765 | error = ufs_dirremove(dvp, ulr, |
766 | ip, ap->a_cnp->cn_flags, 0); |
767 | UFS_WAPBL_END(mp); |
768 | } |
769 | } |
770 | VN_KNOTE(vp, NOTE_DELETE); |
771 | VN_KNOTE(dvp, NOTE_WRITE); |
772 | if (dvp == vp) |
773 | vrele(vp); |
774 | else |
775 | vput(vp); |
776 | vput(dvp); |
777 | fstrans_done(mp); |
778 | return (error); |
779 | } |
780 | |
781 | /* |
782 | * ufs_link: create hard link. |
783 | */ |
784 | int |
785 | ufs_link(void *v) |
786 | { |
787 | struct vop_link_v2_args /* { |
788 | struct vnode *a_dvp; |
789 | struct vnode *a_vp; |
790 | struct componentname *a_cnp; |
791 | } */ *ap = v; |
792 | struct vnode *dvp = ap->a_dvp; |
793 | struct vnode *vp = ap->a_vp; |
794 | struct componentname *cnp = ap->a_cnp; |
795 | struct mount *mp = dvp->v_mount; |
796 | struct inode *ip; |
797 | struct direct *newdir; |
798 | int error; |
799 | struct ufs_lookup_results *ulr; |
800 | |
801 | KASSERT(dvp != vp); |
802 | KASSERT(vp->v_type != VDIR); |
803 | KASSERT(mp == vp->v_mount); /* XXX Not stable without lock. */ |
804 | |
805 | /* XXX should handle this material another way */ |
806 | ulr = &VTOI(dvp)->i_crap; |
807 | UFS_CHECK_CRAPCOUNTER(VTOI(dvp)); |
808 | |
809 | fstrans_start(mp, FSTRANS_SHARED); |
810 | error = vn_lock(vp, LK_EXCLUSIVE); |
811 | if (error) { |
812 | VOP_ABORTOP(dvp, cnp); |
813 | goto out2; |
814 | } |
815 | ip = VTOI(vp); |
816 | if ((nlink_t)ip->i_nlink >= LINK_MAX) { |
817 | VOP_ABORTOP(dvp, cnp); |
818 | error = EMLINK; |
819 | goto out1; |
820 | } |
821 | if (ip->i_flags & (IMMUTABLE | APPEND)) { |
822 | VOP_ABORTOP(dvp, cnp); |
823 | error = EPERM; |
824 | goto out1; |
825 | } |
826 | error = UFS_WAPBL_BEGIN(mp); |
827 | if (error) { |
828 | VOP_ABORTOP(dvp, cnp); |
829 | goto out1; |
830 | } |
831 | ip->i_nlink++; |
832 | DIP_ASSIGN(ip, nlink, ip->i_nlink); |
833 | ip->i_flag |= IN_CHANGE; |
834 | error = UFS_UPDATE(vp, NULL, NULL, UPDATE_DIROP); |
835 | if (!error) { |
836 | newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK); |
837 | ufs_makedirentry(ip, cnp, newdir); |
838 | error = ufs_direnter(dvp, ulr, vp, newdir, cnp, NULL); |
839 | pool_cache_put(ufs_direct_cache, newdir); |
840 | } |
841 | if (error) { |
842 | ip->i_nlink--; |
843 | DIP_ASSIGN(ip, nlink, ip->i_nlink); |
844 | ip->i_flag |= IN_CHANGE; |
845 | UFS_WAPBL_UPDATE(vp, NULL, NULL, UPDATE_DIROP); |
846 | } |
847 | UFS_WAPBL_END(mp); |
848 | out1: |
849 | VOP_UNLOCK(vp); |
850 | out2: |
851 | VN_KNOTE(vp, NOTE_LINK); |
852 | VN_KNOTE(dvp, NOTE_WRITE); |
853 | fstrans_done(mp); |
854 | return (error); |
855 | } |
856 | |
857 | /* |
858 | * whiteout vnode call |
859 | */ |
860 | int |
861 | ufs_whiteout(void *v) |
862 | { |
863 | struct vop_whiteout_args /* { |
864 | struct vnode *a_dvp; |
865 | struct componentname *a_cnp; |
866 | int a_flags; |
867 | } */ *ap = v; |
868 | struct vnode *dvp = ap->a_dvp; |
869 | struct componentname *cnp = ap->a_cnp; |
870 | struct direct *newdir; |
871 | int error; |
872 | struct ufsmount *ump = VFSTOUFS(dvp->v_mount); |
873 | struct ufs_lookup_results *ulr; |
874 | |
875 | /* XXX should handle this material another way */ |
876 | ulr = &VTOI(dvp)->i_crap; |
877 | UFS_CHECK_CRAPCOUNTER(VTOI(dvp)); |
878 | |
879 | error = 0; |
880 | switch (ap->a_flags) { |
881 | case LOOKUP: |
882 | /* 4.4 format directories support whiteout operations */ |
883 | if (ump->um_maxsymlinklen > 0) |
884 | return (0); |
885 | return (EOPNOTSUPP); |
886 | |
887 | case CREATE: |
888 | /* create a new directory whiteout */ |
889 | fstrans_start(dvp->v_mount, FSTRANS_SHARED); |
890 | error = UFS_WAPBL_BEGIN(dvp->v_mount); |
891 | if (error) |
892 | break; |
893 | #ifdef DIAGNOSTIC |
894 | if (ump->um_maxsymlinklen <= 0) |
895 | panic("ufs_whiteout: old format filesystem" ); |
896 | #endif |
897 | |
898 | newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK); |
899 | newdir->d_ino = UFS_WINO; |
900 | newdir->d_namlen = cnp->cn_namelen; |
901 | memcpy(newdir->d_name, cnp->cn_nameptr, |
902 | (size_t)cnp->cn_namelen); |
903 | newdir->d_name[cnp->cn_namelen] = '\0'; |
904 | newdir->d_type = DT_WHT; |
905 | error = ufs_direnter(dvp, ulr, NULL, newdir, cnp, NULL); |
906 | pool_cache_put(ufs_direct_cache, newdir); |
907 | break; |
908 | |
909 | case DELETE: |
910 | /* remove an existing directory whiteout */ |
911 | fstrans_start(dvp->v_mount, FSTRANS_SHARED); |
912 | error = UFS_WAPBL_BEGIN(dvp->v_mount); |
913 | if (error) |
914 | break; |
915 | #ifdef DIAGNOSTIC |
916 | if (ump->um_maxsymlinklen <= 0) |
917 | panic("ufs_whiteout: old format filesystem" ); |
918 | #endif |
919 | |
920 | cnp->cn_flags &= ~DOWHITEOUT; |
921 | error = ufs_dirremove(dvp, ulr, NULL, cnp->cn_flags, 0); |
922 | break; |
923 | default: |
924 | panic("ufs_whiteout: unknown op" ); |
925 | /* NOTREACHED */ |
926 | } |
927 | UFS_WAPBL_END(dvp->v_mount); |
928 | fstrans_done(dvp->v_mount); |
929 | return (error); |
930 | } |
931 | |
932 | int |
933 | ufs_mkdir(void *v) |
934 | { |
935 | struct vop_mkdir_v3_args /* { |
936 | struct vnode *a_dvp; |
937 | struct vnode **a_vpp; |
938 | struct componentname *a_cnp; |
939 | struct vattr *a_vap; |
940 | } */ *ap = v; |
941 | struct vnode *dvp = ap->a_dvp, *tvp; |
942 | struct vattr *vap = ap->a_vap; |
943 | struct componentname *cnp = ap->a_cnp; |
944 | struct inode *ip, *dp = VTOI(dvp); |
945 | struct buf *bp; |
946 | struct dirtemplate dirtemplate; |
947 | struct direct *newdir; |
948 | int error; |
949 | struct ufsmount *ump = dp->i_ump; |
950 | int dirblksiz = ump->um_dirblksiz; |
951 | struct ufs_lookup_results *ulr; |
952 | |
953 | fstrans_start(dvp->v_mount, FSTRANS_SHARED); |
954 | |
955 | /* XXX should handle this material another way */ |
956 | ulr = &dp->i_crap; |
957 | UFS_CHECK_CRAPCOUNTER(dp); |
958 | |
959 | KASSERT(vap->va_type == VDIR); |
960 | |
961 | if ((nlink_t)dp->i_nlink >= LINK_MAX) { |
962 | error = EMLINK; |
963 | goto out; |
964 | } |
965 | /* |
966 | * Must simulate part of ufs_makeinode here to acquire the inode, |
967 | * but not have it entered in the parent directory. The entry is |
968 | * made later after writing "." and ".." entries. |
969 | */ |
970 | error = vcache_new(dvp->v_mount, dvp, vap, cnp->cn_cred, ap->a_vpp); |
971 | if (error) |
972 | goto out; |
973 | error = vn_lock(*ap->a_vpp, LK_EXCLUSIVE); |
974 | if (error) { |
975 | vrele(*ap->a_vpp); |
976 | *ap->a_vpp = NULL; |
977 | goto out; |
978 | } |
979 | error = UFS_WAPBL_BEGIN(ap->a_dvp->v_mount); |
980 | if (error) { |
981 | vput(*ap->a_vpp); |
982 | goto out; |
983 | } |
984 | |
985 | tvp = *ap->a_vpp; |
986 | ip = VTOI(tvp); |
987 | ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; |
988 | ip->i_nlink = 2; |
989 | DIP_ASSIGN(ip, nlink, 2); |
990 | if (cnp->cn_flags & ISWHITEOUT) { |
991 | ip->i_flags |= UF_OPAQUE; |
992 | DIP_ASSIGN(ip, flags, ip->i_flags); |
993 | } |
994 | |
995 | /* |
996 | * Bump link count in parent directory to reflect work done below. |
997 | * Should be done before reference is created so cleanup is |
998 | * possible if we crash. |
999 | */ |
1000 | dp->i_nlink++; |
1001 | DIP_ASSIGN(dp, nlink, dp->i_nlink); |
1002 | dp->i_flag |= IN_CHANGE; |
1003 | if ((error = UFS_UPDATE(dvp, NULL, NULL, UPDATE_DIROP)) != 0) |
1004 | goto bad; |
1005 | |
1006 | /* |
1007 | * Initialize directory with "." and ".." from static template. |
1008 | */ |
1009 | dirtemplate = mastertemplate; |
1010 | dirtemplate.dotdot_reclen = dirblksiz - dirtemplate.dot_reclen; |
1011 | dirtemplate.dot_ino = ufs_rw32(ip->i_number, UFS_MPNEEDSWAP(ump)); |
1012 | dirtemplate.dotdot_ino = ufs_rw32(dp->i_number, UFS_MPNEEDSWAP(ump)); |
1013 | dirtemplate.dot_reclen = ufs_rw16(dirtemplate.dot_reclen, |
1014 | UFS_MPNEEDSWAP(ump)); |
1015 | dirtemplate.dotdot_reclen = ufs_rw16(dirtemplate.dotdot_reclen, |
1016 | UFS_MPNEEDSWAP(ump)); |
1017 | if (ump->um_maxsymlinklen <= 0) { |
1018 | #if BYTE_ORDER == LITTLE_ENDIAN |
1019 | if (UFS_MPNEEDSWAP(ump) == 0) |
1020 | #else |
1021 | if (UFS_MPNEEDSWAP(ump) != 0) |
1022 | #endif |
1023 | { |
1024 | dirtemplate.dot_type = dirtemplate.dot_namlen; |
1025 | dirtemplate.dotdot_type = dirtemplate.dotdot_namlen; |
1026 | dirtemplate.dot_namlen = dirtemplate.dotdot_namlen = 0; |
1027 | } else |
1028 | dirtemplate.dot_type = dirtemplate.dotdot_type = 0; |
1029 | } |
1030 | if ((error = UFS_BALLOC(tvp, (off_t)0, dirblksiz, cnp->cn_cred, |
1031 | B_CLRBUF, &bp)) != 0) |
1032 | goto bad; |
1033 | ip->i_size = dirblksiz; |
1034 | DIP_ASSIGN(ip, size, dirblksiz); |
1035 | ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; |
1036 | uvm_vnp_setsize(tvp, ip->i_size); |
1037 | memcpy((void *)bp->b_data, (void *)&dirtemplate, sizeof dirtemplate); |
1038 | |
1039 | /* |
1040 | * Directory set up, now install its entry in the parent directory. |
1041 | * We must write out the buffer containing the new directory body |
1042 | * before entering the new name in the parent. |
1043 | */ |
1044 | if ((error = VOP_BWRITE(bp->b_vp, bp)) != 0) |
1045 | goto bad; |
1046 | if ((error = UFS_UPDATE(tvp, NULL, NULL, UPDATE_DIROP)) != 0) { |
1047 | goto bad; |
1048 | } |
1049 | newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK); |
1050 | ufs_makedirentry(ip, cnp, newdir); |
1051 | error = ufs_direnter(dvp, ulr, tvp, newdir, cnp, bp); |
1052 | pool_cache_put(ufs_direct_cache, newdir); |
1053 | bad: |
1054 | if (error == 0) { |
1055 | VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); |
1056 | VOP_UNLOCK(tvp); |
1057 | UFS_WAPBL_END(dvp->v_mount); |
1058 | } else { |
1059 | dp->i_nlink--; |
1060 | DIP_ASSIGN(dp, nlink, dp->i_nlink); |
1061 | dp->i_flag |= IN_CHANGE; |
1062 | UFS_WAPBL_UPDATE(dvp, NULL, NULL, UPDATE_DIROP); |
1063 | /* |
1064 | * No need to do an explicit UFS_TRUNCATE here, vrele will |
1065 | * do this for us because we set the link count to 0. |
1066 | */ |
1067 | ip->i_nlink = 0; |
1068 | DIP_ASSIGN(ip, nlink, 0); |
1069 | ip->i_flag |= IN_CHANGE; |
1070 | UFS_WAPBL_UPDATE(tvp, NULL, NULL, UPDATE_DIROP); |
1071 | UFS_WAPBL_END(dvp->v_mount); |
1072 | vput(tvp); |
1073 | } |
1074 | out: |
1075 | fstrans_done(dvp->v_mount); |
1076 | return (error); |
1077 | } |
1078 | |
1079 | int |
1080 | ufs_rmdir(void *v) |
1081 | { |
1082 | struct vop_rmdir_args /* { |
1083 | struct vnode *a_dvp; |
1084 | struct vnode *a_vp; |
1085 | struct componentname *a_cnp; |
1086 | } */ *ap = v; |
1087 | struct vnode *vp, *dvp; |
1088 | struct componentname *cnp; |
1089 | struct inode *ip, *dp; |
1090 | int error; |
1091 | struct ufs_lookup_results *ulr; |
1092 | |
1093 | vp = ap->a_vp; |
1094 | dvp = ap->a_dvp; |
1095 | cnp = ap->a_cnp; |
1096 | ip = VTOI(vp); |
1097 | dp = VTOI(dvp); |
1098 | |
1099 | /* XXX should handle this material another way */ |
1100 | ulr = &dp->i_crap; |
1101 | UFS_CHECK_CRAPCOUNTER(dp); |
1102 | |
1103 | /* |
1104 | * No rmdir "." or of mounted directories please. |
1105 | */ |
1106 | if (dp == ip || vp->v_mountedhere != NULL) { |
1107 | if (dp == ip) |
1108 | vrele(dvp); |
1109 | else |
1110 | vput(dvp); |
1111 | vput(vp); |
1112 | return (EINVAL); |
1113 | } |
1114 | |
1115 | fstrans_start(dvp->v_mount, FSTRANS_SHARED); |
1116 | |
1117 | /* |
1118 | * Do not remove a directory that is in the process of being renamed. |
1119 | * Verify that the directory is empty (and valid). (Rmdir ".." won't |
1120 | * be valid since ".." will contain a reference to the current |
1121 | * directory and thus be non-empty.) |
1122 | */ |
1123 | error = 0; |
1124 | if (ip->i_nlink != 2 || |
1125 | !ufs_dirempty(ip, dp->i_number, cnp->cn_cred)) { |
1126 | error = ENOTEMPTY; |
1127 | goto out; |
1128 | } |
1129 | if ((dp->i_flags & APPEND) || |
1130 | (ip->i_flags & (IMMUTABLE | APPEND))) { |
1131 | error = EPERM; |
1132 | goto out; |
1133 | } |
1134 | error = UFS_WAPBL_BEGIN(dvp->v_mount); |
1135 | if (error) |
1136 | goto out; |
1137 | /* |
1138 | * Delete reference to directory before purging |
1139 | * inode. If we crash in between, the directory |
1140 | * will be reattached to lost+found, |
1141 | */ |
1142 | error = ufs_dirremove(dvp, ulr, ip, cnp->cn_flags, 1); |
1143 | if (error) { |
1144 | UFS_WAPBL_END(dvp->v_mount); |
1145 | goto out; |
1146 | } |
1147 | VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); |
1148 | cache_purge(dvp); |
1149 | /* |
1150 | * Truncate inode. The only stuff left in the directory is "." and |
1151 | * "..". The "." reference is inconsequential since we're quashing |
1152 | * it. |
1153 | */ |
1154 | dp->i_nlink--; |
1155 | DIP_ASSIGN(dp, nlink, dp->i_nlink); |
1156 | dp->i_flag |= IN_CHANGE; |
1157 | UFS_WAPBL_UPDATE(dvp, NULL, NULL, UPDATE_DIROP); |
1158 | ip->i_nlink--; |
1159 | DIP_ASSIGN(ip, nlink, ip->i_nlink); |
1160 | ip->i_flag |= IN_CHANGE; |
1161 | (void) UFS_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred); |
1162 | cache_purge(vp); |
1163 | /* |
1164 | * Unlock the log while we still have reference to unlinked |
1165 | * directory vp so that it will not get locked for recycling |
1166 | */ |
1167 | UFS_WAPBL_END(dvp->v_mount); |
1168 | #ifdef UFS_DIRHASH |
1169 | if (ip->i_dirhash != NULL) |
1170 | ufsdirhash_free(ip); |
1171 | #endif |
1172 | out: |
1173 | VN_KNOTE(vp, NOTE_DELETE); |
1174 | vput(vp); |
1175 | fstrans_done(dvp->v_mount); |
1176 | vput(dvp); |
1177 | return (error); |
1178 | } |
1179 | |
1180 | /* |
1181 | * symlink -- make a symbolic link |
1182 | */ |
1183 | int |
1184 | ufs_symlink(void *v) |
1185 | { |
1186 | struct vop_symlink_v3_args /* { |
1187 | struct vnode *a_dvp; |
1188 | struct vnode **a_vpp; |
1189 | struct componentname *a_cnp; |
1190 | struct vattr *a_vap; |
1191 | char *a_target; |
1192 | } */ *ap = v; |
1193 | struct vnode *vp, **vpp; |
1194 | struct inode *ip; |
1195 | int len, error; |
1196 | struct ufs_lookup_results *ulr; |
1197 | |
1198 | vpp = ap->a_vpp; |
1199 | |
1200 | /* XXX should handle this material another way */ |
1201 | ulr = &VTOI(ap->a_dvp)->i_crap; |
1202 | UFS_CHECK_CRAPCOUNTER(VTOI(ap->a_dvp)); |
1203 | |
1204 | /* |
1205 | * UFS_WAPBL_BEGIN(dvp->v_mount) performed by successful |
1206 | * ufs_makeinode |
1207 | */ |
1208 | fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED); |
1209 | KASSERT(ap->a_vap->va_type == VLNK); |
1210 | error = ufs_makeinode(ap->a_vap, ap->a_dvp, ulr, vpp, ap->a_cnp); |
1211 | if (error) |
1212 | goto out; |
1213 | VN_KNOTE(ap->a_dvp, NOTE_WRITE); |
1214 | vp = *vpp; |
1215 | len = strlen(ap->a_target); |
1216 | ip = VTOI(vp); |
1217 | /* |
1218 | * This test is off by one. um_maxsymlinklen contains the |
1219 | * number of bytes available, and we aren't storing a \0, so |
1220 | * the test should properly be <=. However, it cannot be |
1221 | * changed as this would break compatibility with existing fs |
1222 | * images -- see the way ufs_readlink() works. |
1223 | */ |
1224 | if (len < ip->i_ump->um_maxsymlinklen) { |
1225 | memcpy((char *)SHORTLINK(ip), ap->a_target, len); |
1226 | ip->i_size = len; |
1227 | DIP_ASSIGN(ip, size, len); |
1228 | uvm_vnp_setsize(vp, ip->i_size); |
1229 | ip->i_flag |= IN_CHANGE | IN_UPDATE; |
1230 | if (vp->v_mount->mnt_flag & MNT_RELATIME) |
1231 | ip->i_flag |= IN_ACCESS; |
1232 | UFS_WAPBL_UPDATE(vp, NULL, NULL, 0); |
1233 | } else |
1234 | error = ufs_bufio(UIO_WRITE, vp, ap->a_target, len, (off_t)0, |
1235 | IO_NODELOCKED | IO_JOURNALLOCKED, ap->a_cnp->cn_cred, NULL, |
1236 | NULL); |
1237 | UFS_WAPBL_END(ap->a_dvp->v_mount); |
1238 | VOP_UNLOCK(vp); |
1239 | if (error) |
1240 | vrele(vp); |
1241 | out: |
1242 | fstrans_done(ap->a_dvp->v_mount); |
1243 | return (error); |
1244 | } |
1245 | |
1246 | /* |
1247 | * Vnode op for reading directories. |
1248 | * |
1249 | * This routine handles converting from the on-disk directory format |
1250 | * "struct direct" to the in-memory format "struct dirent" as well as |
1251 | * byte swapping the entries if necessary. |
1252 | */ |
1253 | int |
1254 | ufs_readdir(void *v) |
1255 | { |
1256 | struct vop_readdir_args /* { |
1257 | struct vnode *a_vp; |
1258 | struct uio *a_uio; |
1259 | kauth_cred_t a_cred; |
1260 | int *a_eofflag; |
1261 | off_t **a_cookies; |
1262 | int *ncookies; |
1263 | } */ *ap = v; |
1264 | struct vnode *vp = ap->a_vp; |
1265 | struct direct *cdp, *ecdp; |
1266 | struct dirent *ndp; |
1267 | char *cdbuf, *ndbuf, *endp; |
1268 | struct uio auio, *uio; |
1269 | struct iovec aiov; |
1270 | int error; |
1271 | size_t count, ccount, rcount, cdbufsz, ndbufsz; |
1272 | off_t off, *ccp; |
1273 | off_t startoff; |
1274 | size_t skipbytes; |
1275 | struct ufsmount *ump = VFSTOUFS(vp->v_mount); |
1276 | int nswap = UFS_MPNEEDSWAP(ump); |
1277 | #if BYTE_ORDER == LITTLE_ENDIAN |
1278 | int needswap = ump->um_maxsymlinklen <= 0 && nswap == 0; |
1279 | #else |
1280 | int needswap = ump->um_maxsymlinklen <= 0 && nswap != 0; |
1281 | #endif |
1282 | uio = ap->a_uio; |
1283 | count = uio->uio_resid; |
1284 | rcount = count - ((uio->uio_offset + count) & (ump->um_dirblksiz - 1)); |
1285 | |
1286 | if (rcount < _DIRENT_MINSIZE(cdp) || count < _DIRENT_MINSIZE(ndp)) |
1287 | return EINVAL; |
1288 | |
1289 | startoff = uio->uio_offset & ~(ump->um_dirblksiz - 1); |
1290 | skipbytes = uio->uio_offset - startoff; |
1291 | rcount += skipbytes; |
1292 | |
1293 | auio.uio_iov = &aiov; |
1294 | auio.uio_iovcnt = 1; |
1295 | auio.uio_offset = startoff; |
1296 | auio.uio_resid = rcount; |
1297 | UIO_SETUP_SYSSPACE(&auio); |
1298 | auio.uio_rw = UIO_READ; |
1299 | cdbufsz = rcount; |
1300 | cdbuf = kmem_alloc(cdbufsz, KM_SLEEP); |
1301 | aiov.iov_base = cdbuf; |
1302 | aiov.iov_len = rcount; |
1303 | error = UFS_BUFRD(vp, &auio, 0, ap->a_cred); |
1304 | if (error != 0) { |
1305 | kmem_free(cdbuf, cdbufsz); |
1306 | return error; |
1307 | } |
1308 | |
1309 | rcount -= auio.uio_resid; |
1310 | |
1311 | cdp = (struct direct *)(void *)cdbuf; |
1312 | ecdp = (struct direct *)(void *)&cdbuf[rcount]; |
1313 | |
1314 | ndbufsz = count; |
1315 | ndbuf = kmem_alloc(ndbufsz, KM_SLEEP); |
1316 | ndp = (struct dirent *)(void *)ndbuf; |
1317 | endp = &ndbuf[count]; |
1318 | |
1319 | off = uio->uio_offset; |
1320 | if (ap->a_cookies) { |
1321 | ccount = rcount / _DIRENT_RECLEN(cdp, 1); |
1322 | ccp = *(ap->a_cookies) = malloc(ccount * sizeof(*ccp), |
1323 | M_TEMP, M_WAITOK); |
1324 | } else { |
1325 | /* XXX: GCC */ |
1326 | ccount = 0; |
1327 | ccp = NULL; |
1328 | } |
1329 | |
1330 | while (cdp < ecdp) { |
1331 | cdp->d_reclen = ufs_rw16(cdp->d_reclen, nswap); |
1332 | if (skipbytes > 0) { |
1333 | if (cdp->d_reclen <= skipbytes) { |
1334 | skipbytes -= cdp->d_reclen; |
1335 | cdp = _DIRENT_NEXT(cdp); |
1336 | continue; |
1337 | } |
1338 | /* |
1339 | * invalid cookie. |
1340 | */ |
1341 | error = EINVAL; |
1342 | goto out; |
1343 | } |
1344 | if (cdp->d_reclen == 0) { |
1345 | struct dirent *ondp = ndp; |
1346 | ndp->d_reclen = _DIRENT_MINSIZE(ndp); |
1347 | ndp = _DIRENT_NEXT(ndp); |
1348 | ondp->d_reclen = 0; |
1349 | cdp = ecdp; |
1350 | break; |
1351 | } |
1352 | if (needswap) { |
1353 | ndp->d_type = cdp->d_namlen; |
1354 | ndp->d_namlen = cdp->d_type; |
1355 | } else { |
1356 | ndp->d_type = cdp->d_type; |
1357 | ndp->d_namlen = cdp->d_namlen; |
1358 | } |
1359 | ndp->d_reclen = _DIRENT_RECLEN(ndp, ndp->d_namlen); |
1360 | if ((char *)(void *)ndp + ndp->d_reclen + |
1361 | _DIRENT_MINSIZE(ndp) > endp) |
1362 | break; |
1363 | ndp->d_fileno = ufs_rw32(cdp->d_ino, nswap); |
1364 | (void)memcpy(ndp->d_name, cdp->d_name, ndp->d_namlen); |
1365 | memset(&ndp->d_name[ndp->d_namlen], 0, |
1366 | ndp->d_reclen - _DIRENT_NAMEOFF(ndp) - ndp->d_namlen); |
1367 | off += cdp->d_reclen; |
1368 | if (ap->a_cookies) { |
1369 | KASSERT(ccp - *(ap->a_cookies) < ccount); |
1370 | *(ccp++) = off; |
1371 | } |
1372 | ndp = _DIRENT_NEXT(ndp); |
1373 | cdp = _DIRENT_NEXT(cdp); |
1374 | } |
1375 | |
1376 | count = ((char *)(void *)ndp - ndbuf); |
1377 | error = uiomove(ndbuf, count, uio); |
1378 | out: |
1379 | if (ap->a_cookies) { |
1380 | if (error) { |
1381 | free(*(ap->a_cookies), M_TEMP); |
1382 | *(ap->a_cookies) = NULL; |
1383 | *(ap->a_ncookies) = 0; |
1384 | } else { |
1385 | *ap->a_ncookies = ccp - *(ap->a_cookies); |
1386 | } |
1387 | } |
1388 | uio->uio_offset = off; |
1389 | kmem_free(ndbuf, ndbufsz); |
1390 | kmem_free(cdbuf, cdbufsz); |
1391 | *ap->a_eofflag = VTOI(vp)->i_size <= uio->uio_offset; |
1392 | return error; |
1393 | } |
1394 | |
1395 | /* |
1396 | * Return target name of a symbolic link |
1397 | */ |
1398 | int |
1399 | ufs_readlink(void *v) |
1400 | { |
1401 | struct vop_readlink_args /* { |
1402 | struct vnode *a_vp; |
1403 | struct uio *a_uio; |
1404 | kauth_cred_t a_cred; |
1405 | } */ *ap = v; |
1406 | struct vnode *vp = ap->a_vp; |
1407 | struct inode *ip = VTOI(vp); |
1408 | struct ufsmount *ump = VFSTOUFS(vp->v_mount); |
1409 | int isize; |
1410 | |
1411 | /* |
1412 | * The test against um_maxsymlinklen is off by one; it should |
1413 | * theoretically be <=, not <. However, it cannot be changed |
1414 | * as that would break compatibility with existing fs images. |
1415 | */ |
1416 | |
1417 | isize = ip->i_size; |
1418 | if (isize < ump->um_maxsymlinklen || |
1419 | (ump->um_maxsymlinklen == 0 && DIP(ip, blocks) == 0)) { |
1420 | uiomove((char *)SHORTLINK(ip), isize, ap->a_uio); |
1421 | return (0); |
1422 | } |
1423 | return (UFS_BUFRD(vp, ap->a_uio, 0, ap->a_cred)); |
1424 | } |
1425 | |
1426 | /* |
1427 | * Calculate the logical to physical mapping if not done already, |
1428 | * then call the device strategy routine. |
1429 | */ |
1430 | int |
1431 | ufs_strategy(void *v) |
1432 | { |
1433 | struct vop_strategy_args /* { |
1434 | struct vnode *a_vp; |
1435 | struct buf *a_bp; |
1436 | } */ *ap = v; |
1437 | struct buf *bp; |
1438 | struct vnode *vp; |
1439 | struct inode *ip; |
1440 | struct mount *mp; |
1441 | int error; |
1442 | |
1443 | bp = ap->a_bp; |
1444 | vp = ap->a_vp; |
1445 | ip = VTOI(vp); |
1446 | if (vp->v_type == VBLK || vp->v_type == VCHR) |
1447 | panic("ufs_strategy: spec" ); |
1448 | KASSERT(bp->b_bcount != 0); |
1449 | if (bp->b_blkno == bp->b_lblkno) { |
1450 | error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, |
1451 | NULL); |
1452 | if (error) { |
1453 | bp->b_error = error; |
1454 | biodone(bp); |
1455 | return (error); |
1456 | } |
1457 | if (bp->b_blkno == -1) /* no valid data */ |
1458 | clrbuf(bp); |
1459 | } |
1460 | if (bp->b_blkno < 0) { /* block is not on disk */ |
1461 | biodone(bp); |
1462 | return (0); |
1463 | } |
1464 | vp = ip->i_devvp; |
1465 | |
1466 | error = VOP_STRATEGY(vp, bp); |
1467 | if (error) |
1468 | return error; |
1469 | |
1470 | if (!BUF_ISREAD(bp)) |
1471 | return 0; |
1472 | |
1473 | mp = wapbl_vptomp(vp); |
1474 | if (mp == NULL || mp->mnt_wapbl_replay == NULL || |
1475 | !WAPBL_REPLAY_ISOPEN(mp) || |
1476 | !WAPBL_REPLAY_CAN_READ(mp, bp->b_blkno, bp->b_bcount)) |
1477 | return 0; |
1478 | |
1479 | error = biowait(bp); |
1480 | if (error) |
1481 | return error; |
1482 | |
1483 | error = WAPBL_REPLAY_READ(mp, bp->b_data, bp->b_blkno, bp->b_bcount); |
1484 | if (error) { |
1485 | mutex_enter(&bufcache_lock); |
1486 | SET(bp->b_cflags, BC_INVAL); |
1487 | mutex_exit(&bufcache_lock); |
1488 | } |
1489 | return error; |
1490 | } |
1491 | |
1492 | /* |
1493 | * Print out the contents of an inode. |
1494 | */ |
1495 | int |
1496 | ufs_print(void *v) |
1497 | { |
1498 | struct vop_print_args /* { |
1499 | struct vnode *a_vp; |
1500 | } */ *ap = v; |
1501 | struct vnode *vp; |
1502 | struct inode *ip; |
1503 | |
1504 | vp = ap->a_vp; |
1505 | ip = VTOI(vp); |
1506 | printf("tag VT_UFS, ino %llu, on dev %llu, %llu" , |
1507 | (unsigned long long)ip->i_number, |
1508 | (unsigned long long)major(ip->i_dev), |
1509 | (unsigned long long)minor(ip->i_dev)); |
1510 | printf(" flags 0x%x, nlink %d\n" , |
1511 | ip->i_flag, ip->i_nlink); |
1512 | printf("\tmode 0%o, owner %d, group %d, size %qd" , |
1513 | ip->i_mode, ip->i_uid, ip->i_gid, |
1514 | (long long)ip->i_size); |
1515 | if (vp->v_type == VFIFO) |
1516 | VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v); |
1517 | printf("\n" ); |
1518 | return (0); |
1519 | } |
1520 | |
1521 | /* |
1522 | * Read wrapper for special devices. |
1523 | */ |
1524 | int |
1525 | ufsspec_read(void *v) |
1526 | { |
1527 | struct vop_read_args /* { |
1528 | struct vnode *a_vp; |
1529 | struct uio *a_uio; |
1530 | int a_ioflag; |
1531 | kauth_cred_t a_cred; |
1532 | } */ *ap = v; |
1533 | |
1534 | /* |
1535 | * Set access flag. |
1536 | */ |
1537 | if ((ap->a_vp->v_mount->mnt_flag & MNT_NODEVMTIME) == 0) |
1538 | VTOI(ap->a_vp)->i_flag |= IN_ACCESS; |
1539 | return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap)); |
1540 | } |
1541 | |
1542 | /* |
1543 | * Write wrapper for special devices. |
1544 | */ |
1545 | int |
1546 | ufsspec_write(void *v) |
1547 | { |
1548 | struct vop_write_args /* { |
1549 | struct vnode *a_vp; |
1550 | struct uio *a_uio; |
1551 | int a_ioflag; |
1552 | kauth_cred_t a_cred; |
1553 | } */ *ap = v; |
1554 | |
1555 | /* |
1556 | * Set update and change flags. |
1557 | */ |
1558 | if ((ap->a_vp->v_mount->mnt_flag & MNT_NODEVMTIME) == 0) |
1559 | VTOI(ap->a_vp)->i_flag |= IN_MODIFY; |
1560 | return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap)); |
1561 | } |
1562 | |
1563 | /* |
1564 | * Close wrapper for special devices. |
1565 | * |
1566 | * Update the times on the inode then do device close. |
1567 | */ |
1568 | int |
1569 | ufsspec_close(void *v) |
1570 | { |
1571 | struct vop_close_args /* { |
1572 | struct vnode *a_vp; |
1573 | int a_fflag; |
1574 | kauth_cred_t a_cred; |
1575 | } */ *ap = v; |
1576 | struct vnode *vp; |
1577 | |
1578 | vp = ap->a_vp; |
1579 | if (vp->v_usecount > 1) |
1580 | UFS_ITIMES(vp, NULL, NULL, NULL); |
1581 | return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap)); |
1582 | } |
1583 | |
1584 | /* |
1585 | * Read wrapper for fifo's |
1586 | */ |
1587 | int |
1588 | ufsfifo_read(void *v) |
1589 | { |
1590 | struct vop_read_args /* { |
1591 | struct vnode *a_vp; |
1592 | struct uio *a_uio; |
1593 | int a_ioflag; |
1594 | kauth_cred_t a_cred; |
1595 | } */ *ap = v; |
1596 | |
1597 | /* |
1598 | * Set access flag. |
1599 | */ |
1600 | VTOI(ap->a_vp)->i_flag |= IN_ACCESS; |
1601 | return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap)); |
1602 | } |
1603 | |
1604 | /* |
1605 | * Write wrapper for fifo's. |
1606 | */ |
1607 | int |
1608 | ufsfifo_write(void *v) |
1609 | { |
1610 | struct vop_write_args /* { |
1611 | struct vnode *a_vp; |
1612 | struct uio *a_uio; |
1613 | int a_ioflag; |
1614 | kauth_cred_t a_cred; |
1615 | } */ *ap = v; |
1616 | |
1617 | /* |
1618 | * Set update and change flags. |
1619 | */ |
1620 | VTOI(ap->a_vp)->i_flag |= IN_MODIFY; |
1621 | return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap)); |
1622 | } |
1623 | |
1624 | /* |
1625 | * Close wrapper for fifo's. |
1626 | * |
1627 | * Update the times on the inode then do device close. |
1628 | */ |
1629 | int |
1630 | ufsfifo_close(void *v) |
1631 | { |
1632 | struct vop_close_args /* { |
1633 | struct vnode *a_vp; |
1634 | int a_fflag; |
1635 | kauth_cred_t a_cred; |
1636 | } */ *ap = v; |
1637 | struct vnode *vp; |
1638 | |
1639 | vp = ap->a_vp; |
1640 | if (ap->a_vp->v_usecount > 1) |
1641 | UFS_ITIMES(vp, NULL, NULL, NULL); |
1642 | return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap)); |
1643 | } |
1644 | |
1645 | /* |
1646 | * Return POSIX pathconf information applicable to ufs filesystems. |
1647 | */ |
1648 | int |
1649 | ufs_pathconf(void *v) |
1650 | { |
1651 | struct vop_pathconf_args /* { |
1652 | struct vnode *a_vp; |
1653 | int a_name; |
1654 | register_t *a_retval; |
1655 | } */ *ap = v; |
1656 | |
1657 | switch (ap->a_name) { |
1658 | case _PC_LINK_MAX: |
1659 | *ap->a_retval = LINK_MAX; |
1660 | return (0); |
1661 | case _PC_NAME_MAX: |
1662 | *ap->a_retval = FFS_MAXNAMLEN; |
1663 | return (0); |
1664 | case _PC_PATH_MAX: |
1665 | *ap->a_retval = PATH_MAX; |
1666 | return (0); |
1667 | case _PC_PIPE_BUF: |
1668 | *ap->a_retval = PIPE_BUF; |
1669 | return (0); |
1670 | case _PC_CHOWN_RESTRICTED: |
1671 | *ap->a_retval = 1; |
1672 | return (0); |
1673 | case _PC_NO_TRUNC: |
1674 | *ap->a_retval = 1; |
1675 | return (0); |
1676 | case _PC_SYNC_IO: |
1677 | *ap->a_retval = 1; |
1678 | return (0); |
1679 | case _PC_FILESIZEBITS: |
1680 | *ap->a_retval = 42; |
1681 | return (0); |
1682 | case _PC_SYMLINK_MAX: |
1683 | *ap->a_retval = MAXPATHLEN; |
1684 | return (0); |
1685 | case _PC_2_SYMLINKS: |
1686 | *ap->a_retval = 1; |
1687 | return (0); |
1688 | default: |
1689 | return (EINVAL); |
1690 | } |
1691 | /* NOTREACHED */ |
1692 | } |
1693 | |
1694 | /* |
1695 | * Advisory record locking support |
1696 | */ |
1697 | int |
1698 | ufs_advlock(void *v) |
1699 | { |
1700 | struct vop_advlock_args /* { |
1701 | struct vnode *a_vp; |
1702 | void * a_id; |
1703 | int a_op; |
1704 | struct flock *a_fl; |
1705 | int a_flags; |
1706 | } */ *ap = v; |
1707 | struct inode *ip; |
1708 | |
1709 | ip = VTOI(ap->a_vp); |
1710 | return lf_advlock(ap, &ip->i_lockf, ip->i_size); |
1711 | } |
1712 | |
1713 | /* |
1714 | * Initialize the vnode associated with a new inode, handle aliased |
1715 | * vnodes. |
1716 | */ |
1717 | void |
1718 | ufs_vinit(struct mount *mntp, int (**specops)(void *), int (**fifoops)(void *), |
1719 | struct vnode **vpp) |
1720 | { |
1721 | struct timeval tv; |
1722 | struct inode *ip; |
1723 | struct vnode *vp; |
1724 | dev_t rdev; |
1725 | struct ufsmount *ump; |
1726 | |
1727 | vp = *vpp; |
1728 | ip = VTOI(vp); |
1729 | switch(vp->v_type = IFTOVT(ip->i_mode)) { |
1730 | case VCHR: |
1731 | case VBLK: |
1732 | vp->v_op = specops; |
1733 | ump = ip->i_ump; |
1734 | if (ump->um_fstype == UFS1) |
1735 | rdev = (dev_t)ufs_rw32(ip->i_ffs1_rdev, |
1736 | UFS_MPNEEDSWAP(ump)); |
1737 | else |
1738 | rdev = (dev_t)ufs_rw64(ip->i_ffs2_rdev, |
1739 | UFS_MPNEEDSWAP(ump)); |
1740 | spec_node_init(vp, rdev); |
1741 | break; |
1742 | case VFIFO: |
1743 | vp->v_op = fifoops; |
1744 | break; |
1745 | case VNON: |
1746 | case VBAD: |
1747 | case VSOCK: |
1748 | case VLNK: |
1749 | case VDIR: |
1750 | case VREG: |
1751 | break; |
1752 | } |
1753 | if (ip->i_number == UFS_ROOTINO) |
1754 | vp->v_vflag |= VV_ROOT; |
1755 | /* |
1756 | * Initialize modrev times |
1757 | */ |
1758 | getmicrouptime(&tv); |
1759 | ip->i_modrev = (uint64_t)(uint)tv.tv_sec << 32 |
1760 | | tv.tv_usec * 4294u; |
1761 | *vpp = vp; |
1762 | } |
1763 | |
1764 | /* |
1765 | * Allocate a new inode. |
1766 | */ |
1767 | static int |
1768 | ufs_makeinode(struct vattr *vap, struct vnode *dvp, |
1769 | const struct ufs_lookup_results *ulr, |
1770 | struct vnode **vpp, struct componentname *cnp) |
1771 | { |
1772 | struct inode *ip; |
1773 | struct direct *newdir; |
1774 | struct vnode *tvp; |
1775 | int error; |
1776 | |
1777 | UFS_WAPBL_JUNLOCK_ASSERT(dvp->v_mount); |
1778 | |
1779 | error = vcache_new(dvp->v_mount, dvp, vap, cnp->cn_cred, &tvp); |
1780 | if (error) |
1781 | return error; |
1782 | error = vn_lock(tvp, LK_EXCLUSIVE); |
1783 | if (error) { |
1784 | vrele(tvp); |
1785 | return error; |
1786 | } |
1787 | *vpp = tvp; |
1788 | ip = VTOI(tvp); |
1789 | error = UFS_WAPBL_BEGIN(dvp->v_mount); |
1790 | if (error) { |
1791 | vput(tvp); |
1792 | return (error); |
1793 | } |
1794 | ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; |
1795 | ip->i_nlink = 1; |
1796 | DIP_ASSIGN(ip, nlink, 1); |
1797 | |
1798 | /* Authorize setting SGID if needed. */ |
1799 | if (ip->i_mode & ISGID) { |
1800 | error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_WRITE_SECURITY, |
1801 | tvp, NULL, genfs_can_chmod(tvp->v_type, cnp->cn_cred, ip->i_uid, |
1802 | ip->i_gid, MAKEIMODE(vap->va_type, vap->va_mode))); |
1803 | if (error) { |
1804 | ip->i_mode &= ~ISGID; |
1805 | DIP_ASSIGN(ip, mode, ip->i_mode); |
1806 | } |
1807 | } |
1808 | |
1809 | if (cnp->cn_flags & ISWHITEOUT) { |
1810 | ip->i_flags |= UF_OPAQUE; |
1811 | DIP_ASSIGN(ip, flags, ip->i_flags); |
1812 | } |
1813 | |
1814 | /* |
1815 | * Make sure inode goes to disk before directory entry. |
1816 | */ |
1817 | if ((error = UFS_UPDATE(tvp, NULL, NULL, UPDATE_DIROP)) != 0) |
1818 | goto bad; |
1819 | newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK); |
1820 | ufs_makedirentry(ip, cnp, newdir); |
1821 | error = ufs_direnter(dvp, ulr, tvp, newdir, cnp, NULL); |
1822 | pool_cache_put(ufs_direct_cache, newdir); |
1823 | if (error) |
1824 | goto bad; |
1825 | *vpp = tvp; |
1826 | return (0); |
1827 | |
1828 | bad: |
1829 | /* |
1830 | * Write error occurred trying to update the inode |
1831 | * or the directory so must deallocate the inode. |
1832 | */ |
1833 | ip->i_nlink = 0; |
1834 | DIP_ASSIGN(ip, nlink, 0); |
1835 | ip->i_flag |= IN_CHANGE; |
1836 | UFS_WAPBL_UPDATE(tvp, NULL, NULL, 0); |
1837 | UFS_WAPBL_END(dvp->v_mount); |
1838 | vput(tvp); |
1839 | return (error); |
1840 | } |
1841 | |
1842 | /* |
1843 | * Allocate len bytes at offset off. |
1844 | */ |
1845 | int |
1846 | ufs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags, |
1847 | kauth_cred_t cred) |
1848 | { |
1849 | struct inode *ip = VTOI(vp); |
1850 | int error, delta, bshift, bsize; |
1851 | UVMHIST_FUNC("ufs_gop_alloc" ); UVMHIST_CALLED(ubchist); |
1852 | |
1853 | error = 0; |
1854 | bshift = vp->v_mount->mnt_fs_bshift; |
1855 | bsize = 1 << bshift; |
1856 | |
1857 | delta = off & (bsize - 1); |
1858 | off -= delta; |
1859 | len += delta; |
1860 | |
1861 | while (len > 0) { |
1862 | bsize = MIN(bsize, len); |
1863 | |
1864 | error = UFS_BALLOC(vp, off, bsize, cred, flags, NULL); |
1865 | if (error) { |
1866 | goto out; |
1867 | } |
1868 | |
1869 | /* |
1870 | * increase file size now, UFS_BALLOC() requires that |
1871 | * EOF be up-to-date before each call. |
1872 | */ |
1873 | |
1874 | if (ip->i_size < off + bsize) { |
1875 | UVMHIST_LOG(ubchist, "vp %p old 0x%x new 0x%x" , |
1876 | vp, ip->i_size, off + bsize, 0); |
1877 | ip->i_size = off + bsize; |
1878 | DIP_ASSIGN(ip, size, ip->i_size); |
1879 | } |
1880 | |
1881 | off += bsize; |
1882 | len -= bsize; |
1883 | } |
1884 | |
1885 | out: |
1886 | UFS_WAPBL_UPDATE(vp, NULL, NULL, 0); |
1887 | return error; |
1888 | } |
1889 | |
1890 | void |
1891 | ufs_gop_markupdate(struct vnode *vp, int flags) |
1892 | { |
1893 | u_int32_t mask = 0; |
1894 | |
1895 | if ((flags & GOP_UPDATE_ACCESSED) != 0) { |
1896 | mask = IN_ACCESS; |
1897 | } |
1898 | if ((flags & GOP_UPDATE_MODIFIED) != 0) { |
1899 | if (vp->v_type == VREG) { |
1900 | mask |= IN_CHANGE | IN_UPDATE; |
1901 | } else { |
1902 | mask |= IN_MODIFY; |
1903 | } |
1904 | } |
1905 | if (mask) { |
1906 | struct inode *ip = VTOI(vp); |
1907 | |
1908 | ip->i_flag |= mask; |
1909 | } |
1910 | } |
1911 | |
1912 | int |
1913 | ufs_bufio(enum uio_rw rw, struct vnode *vp, void *buf, size_t len, off_t off, |
1914 | int ioflg, kauth_cred_t cred, size_t *aresid, struct lwp *l) |
1915 | { |
1916 | struct iovec iov; |
1917 | struct uio uio; |
1918 | int error; |
1919 | |
1920 | KASSERT(ISSET(ioflg, IO_NODELOCKED)); |
1921 | KASSERT(VOP_ISLOCKED(vp)); |
1922 | KASSERT(rw != UIO_WRITE || VOP_ISLOCKED(vp) == LK_EXCLUSIVE); |
1923 | KASSERT(rw != UIO_WRITE || vp->v_mount->mnt_wapbl == NULL || |
1924 | ISSET(ioflg, IO_JOURNALLOCKED)); |
1925 | |
1926 | iov.iov_base = buf; |
1927 | iov.iov_len = len; |
1928 | uio.uio_iov = &iov; |
1929 | uio.uio_iovcnt = 1; |
1930 | uio.uio_resid = len; |
1931 | uio.uio_offset = off; |
1932 | uio.uio_rw = rw; |
1933 | UIO_SETUP_SYSSPACE(&uio); |
1934 | |
1935 | switch (rw) { |
1936 | case UIO_READ: |
1937 | error = UFS_BUFRD(vp, &uio, ioflg, cred); |
1938 | break; |
1939 | case UIO_WRITE: |
1940 | error = UFS_BUFWR(vp, &uio, ioflg, cred); |
1941 | break; |
1942 | default: |
1943 | panic("invalid uio rw: %d" , (int)rw); |
1944 | } |
1945 | |
1946 | if (aresid) |
1947 | *aresid = uio.uio_resid; |
1948 | else if (uio.uio_resid && error == 0) |
1949 | error = EIO; |
1950 | |
1951 | KASSERT(VOP_ISLOCKED(vp)); |
1952 | KASSERT(rw != UIO_WRITE || VOP_ISLOCKED(vp) == LK_EXCLUSIVE); |
1953 | return error; |
1954 | } |
1955 | |