1 | /* $NetBSD: ulfs_vnops.c,v 1.44 2016/06/20 03:36:09 dholland Exp $ */ |
2 | /* from NetBSD: ufs_vnops.c,v 1.232 2016/05/19 18:32:03 riastradh Exp */ |
3 | |
4 | /*- |
5 | * Copyright (c) 2008 The NetBSD Foundation, Inc. |
6 | * All rights reserved. |
7 | * |
8 | * This code is derived from software contributed to The NetBSD Foundation |
9 | * by Wasabi Systems, Inc. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | * Copyright (c) 1982, 1986, 1989, 1993, 1995 |
35 | * The Regents of the University of California. All rights reserved. |
36 | * (c) UNIX System Laboratories, Inc. |
37 | * All or some portions of this file are derived from material licensed |
38 | * to the University of California by American Telephone and Telegraph |
39 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with |
40 | * the permission of UNIX System Laboratories, Inc. |
41 | * |
42 | * Redistribution and use in source and binary forms, with or without |
43 | * modification, are permitted provided that the following conditions |
44 | * are met: |
45 | * 1. Redistributions of source code must retain the above copyright |
46 | * notice, this list of conditions and the following disclaimer. |
47 | * 2. Redistributions in binary form must reproduce the above copyright |
48 | * notice, this list of conditions and the following disclaimer in the |
49 | * documentation and/or other materials provided with the distribution. |
50 | * 3. Neither the name of the University nor the names of its contributors |
51 | * may be used to endorse or promote products derived from this software |
52 | * without specific prior written permission. |
53 | * |
54 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
55 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
56 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
57 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
58 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
59 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
60 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
61 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
62 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
63 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
64 | * SUCH DAMAGE. |
65 | * |
66 | * @(#)ufs_vnops.c 8.28 (Berkeley) 7/31/95 |
67 | */ |
68 | |
69 | #include <sys/cdefs.h> |
70 | __KERNEL_RCSID(0, "$NetBSD: ulfs_vnops.c,v 1.44 2016/06/20 03:36:09 dholland Exp $" ); |
71 | |
72 | #if defined(_KERNEL_OPT) |
73 | #include "opt_lfs.h" |
74 | #include "opt_quota.h" |
75 | #endif |
76 | |
77 | #include <sys/param.h> |
78 | #include <sys/systm.h> |
79 | #include <sys/namei.h> |
80 | #include <sys/resourcevar.h> |
81 | #include <sys/kernel.h> |
82 | #include <sys/file.h> |
83 | #include <sys/stat.h> |
84 | #include <sys/buf.h> |
85 | #include <sys/proc.h> |
86 | #include <sys/mount.h> |
87 | #include <sys/vnode.h> |
88 | #include <sys/kmem.h> |
89 | #include <sys/malloc.h> |
90 | #include <sys/dirent.h> |
91 | #include <sys/lockf.h> |
92 | #include <sys/kauth.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/lfs/lfs_extern.h> |
100 | #include <ufs/lfs/lfs.h> |
101 | #include <ufs/lfs/lfs_accessors.h> |
102 | |
103 | #include <ufs/lfs/ulfs_inode.h> |
104 | #include <ufs/lfs/ulfsmount.h> |
105 | #include <ufs/lfs/ulfs_bswap.h> |
106 | #include <ufs/lfs/ulfs_extern.h> |
107 | #ifdef LFS_DIRHASH |
108 | #include <ufs/lfs/ulfs_dirhash.h> |
109 | #endif |
110 | |
111 | #include <uvm/uvm.h> |
112 | |
113 | static int ulfs_chmod(struct vnode *, int, kauth_cred_t, struct lwp *); |
114 | static int ulfs_chown(struct vnode *, uid_t, gid_t, kauth_cred_t, |
115 | struct lwp *); |
116 | |
117 | /* |
118 | * Open called. |
119 | * |
120 | * Nothing to do. |
121 | */ |
122 | /* ARGSUSED */ |
123 | int |
124 | ulfs_open(void *v) |
125 | { |
126 | struct vop_open_args /* { |
127 | struct vnode *a_vp; |
128 | int a_mode; |
129 | kauth_cred_t a_cred; |
130 | } */ *ap = v; |
131 | |
132 | /* |
133 | * Files marked append-only must be opened for appending. |
134 | */ |
135 | if ((VTOI(ap->a_vp)->i_flags & APPEND) && |
136 | (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) |
137 | return (EPERM); |
138 | return (0); |
139 | } |
140 | |
141 | static int |
142 | ulfs_check_possible(struct vnode *vp, struct inode *ip, mode_t mode, |
143 | kauth_cred_t cred) |
144 | { |
145 | #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) |
146 | int error; |
147 | #endif |
148 | |
149 | /* |
150 | * Disallow write attempts on read-only file systems; |
151 | * unless the file is a socket, fifo, or a block or |
152 | * character device resident on the file system. |
153 | */ |
154 | if (mode & VWRITE) { |
155 | switch (vp->v_type) { |
156 | case VDIR: |
157 | case VLNK: |
158 | case VREG: |
159 | if (vp->v_mount->mnt_flag & MNT_RDONLY) |
160 | return (EROFS); |
161 | #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) |
162 | fstrans_start(vp->v_mount, FSTRANS_SHARED); |
163 | error = lfs_chkdq(ip, 0, cred, 0); |
164 | fstrans_done(vp->v_mount); |
165 | if (error != 0) |
166 | return error; |
167 | #endif |
168 | break; |
169 | case VBAD: |
170 | case VBLK: |
171 | case VCHR: |
172 | case VSOCK: |
173 | case VFIFO: |
174 | case VNON: |
175 | default: |
176 | break; |
177 | } |
178 | } |
179 | |
180 | /* If it is a snapshot, nobody gets access to it. */ |
181 | if ((ip->i_flags & SF_SNAPSHOT)) |
182 | return (EPERM); |
183 | /* If immutable bit set, nobody gets to write it. */ |
184 | if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE)) |
185 | return (EPERM); |
186 | |
187 | return 0; |
188 | } |
189 | |
190 | static int |
191 | ulfs_check_permitted(struct vnode *vp, struct inode *ip, mode_t mode, |
192 | kauth_cred_t cred) |
193 | { |
194 | |
195 | return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode, vp->v_type, |
196 | ip->i_mode & ALLPERMS), vp, NULL, genfs_can_access(vp->v_type, |
197 | ip->i_mode & ALLPERMS, ip->i_uid, ip->i_gid, mode, cred)); |
198 | } |
199 | |
200 | int |
201 | ulfs_access(void *v) |
202 | { |
203 | struct vop_access_args /* { |
204 | struct vnode *a_vp; |
205 | int a_mode; |
206 | kauth_cred_t a_cred; |
207 | } */ *ap = v; |
208 | struct vnode *vp; |
209 | struct inode *ip; |
210 | mode_t mode; |
211 | int error; |
212 | |
213 | vp = ap->a_vp; |
214 | ip = VTOI(vp); |
215 | mode = ap->a_mode; |
216 | |
217 | error = ulfs_check_possible(vp, ip, mode, ap->a_cred); |
218 | if (error) |
219 | return error; |
220 | |
221 | error = ulfs_check_permitted(vp, ip, mode, ap->a_cred); |
222 | |
223 | return error; |
224 | } |
225 | |
226 | /* |
227 | * Set attribute vnode op. called from several syscalls |
228 | */ |
229 | int |
230 | ulfs_setattr(void *v) |
231 | { |
232 | struct vop_setattr_args /* { |
233 | struct vnode *a_vp; |
234 | struct vattr *a_vap; |
235 | kauth_cred_t a_cred; |
236 | } */ *ap = v; |
237 | struct vattr *vap; |
238 | struct vnode *vp; |
239 | struct inode *ip; |
240 | struct lfs *fs; |
241 | kauth_cred_t cred; |
242 | struct lwp *l; |
243 | int error; |
244 | kauth_action_t action; |
245 | bool changing_sysflags; |
246 | |
247 | vap = ap->a_vap; |
248 | vp = ap->a_vp; |
249 | ip = VTOI(vp); |
250 | fs = ip->i_lfs; |
251 | cred = ap->a_cred; |
252 | l = curlwp; |
253 | action = KAUTH_VNODE_WRITE_FLAGS; |
254 | changing_sysflags = false; |
255 | |
256 | /* |
257 | * Check for unsettable attributes. |
258 | */ |
259 | if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || |
260 | (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || |
261 | (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || |
262 | ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { |
263 | return (EINVAL); |
264 | } |
265 | |
266 | fstrans_start(vp->v_mount, FSTRANS_SHARED); |
267 | |
268 | if (vap->va_flags != VNOVAL) { |
269 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { |
270 | error = EROFS; |
271 | goto out; |
272 | } |
273 | |
274 | /* Snapshot flag cannot be set or cleared */ |
275 | if ((vap->va_flags & (SF_SNAPSHOT | SF_SNAPINVAL)) != |
276 | (ip->i_flags & (SF_SNAPSHOT | SF_SNAPINVAL))) { |
277 | error = EPERM; |
278 | goto out; |
279 | } |
280 | |
281 | if (ip->i_flags & (SF_IMMUTABLE | SF_APPEND)) { |
282 | action |= KAUTH_VNODE_HAS_SYSFLAGS; |
283 | } |
284 | |
285 | if ((vap->va_flags & SF_SETTABLE) != |
286 | (ip->i_flags & SF_SETTABLE)) { |
287 | action |= KAUTH_VNODE_WRITE_SYSFLAGS; |
288 | changing_sysflags = true; |
289 | } |
290 | |
291 | error = kauth_authorize_vnode(cred, action, vp, NULL, |
292 | genfs_can_chflags(cred, vp->v_type, ip->i_uid, |
293 | changing_sysflags)); |
294 | if (error) |
295 | goto out; |
296 | |
297 | if (changing_sysflags) { |
298 | ip->i_flags = vap->va_flags; |
299 | DIP_ASSIGN(ip, flags, ip->i_flags); |
300 | } else { |
301 | ip->i_flags &= SF_SETTABLE; |
302 | ip->i_flags |= (vap->va_flags & UF_SETTABLE); |
303 | DIP_ASSIGN(ip, flags, ip->i_flags); |
304 | } |
305 | ip->i_flag |= IN_CHANGE; |
306 | if (vap->va_flags & (IMMUTABLE | APPEND)) { |
307 | error = 0; |
308 | goto out; |
309 | } |
310 | } |
311 | if (ip->i_flags & (IMMUTABLE | APPEND)) { |
312 | error = EPERM; |
313 | goto out; |
314 | } |
315 | /* |
316 | * Go through the fields and update iff not VNOVAL. |
317 | */ |
318 | if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { |
319 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { |
320 | error = EROFS; |
321 | goto out; |
322 | } |
323 | error = ulfs_chown(vp, vap->va_uid, vap->va_gid, cred, l); |
324 | if (error) |
325 | goto out; |
326 | } |
327 | if (vap->va_size != VNOVAL) { |
328 | /* |
329 | * Disallow write attempts on read-only file systems; |
330 | * unless the file is a socket, fifo, or a block or |
331 | * character device resident on the file system. |
332 | */ |
333 | switch (vp->v_type) { |
334 | case VDIR: |
335 | error = EISDIR; |
336 | goto out; |
337 | case VCHR: |
338 | case VBLK: |
339 | case VFIFO: |
340 | break; |
341 | case VREG: |
342 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { |
343 | error = EROFS; |
344 | goto out; |
345 | } |
346 | if ((ip->i_flags & SF_SNAPSHOT) != 0) { |
347 | error = EPERM; |
348 | goto out; |
349 | } |
350 | error = lfs_truncate(vp, vap->va_size, 0, cred); |
351 | if (error) |
352 | goto out; |
353 | break; |
354 | default: |
355 | error = EOPNOTSUPP; |
356 | goto out; |
357 | } |
358 | } |
359 | ip = VTOI(vp); |
360 | if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || |
361 | vap->va_birthtime.tv_sec != VNOVAL) { |
362 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { |
363 | error = EROFS; |
364 | goto out; |
365 | } |
366 | if ((ip->i_flags & SF_SNAPSHOT) != 0) { |
367 | error = EPERM; |
368 | goto out; |
369 | } |
370 | error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp, |
371 | NULL, genfs_can_chtimes(vp, vap->va_vaflags, ip->i_uid, cred)); |
372 | if (error) |
373 | goto out; |
374 | if (vap->va_atime.tv_sec != VNOVAL) |
375 | if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) |
376 | ip->i_flag |= IN_ACCESS; |
377 | if (vap->va_mtime.tv_sec != VNOVAL) { |
378 | ip->i_flag |= IN_CHANGE | IN_UPDATE; |
379 | if (vp->v_mount->mnt_flag & MNT_RELATIME) |
380 | ip->i_flag |= IN_ACCESS; |
381 | } |
382 | if (vap->va_birthtime.tv_sec != VNOVAL) { |
383 | lfs_dino_setbirthtime(fs, ip->i_din, |
384 | &vap->va_birthtime); |
385 | } |
386 | error = lfs_update(vp, &vap->va_atime, &vap->va_mtime, 0); |
387 | if (error) |
388 | goto out; |
389 | } |
390 | error = 0; |
391 | if (vap->va_mode != (mode_t)VNOVAL) { |
392 | if (vp->v_mount->mnt_flag & MNT_RDONLY) { |
393 | error = EROFS; |
394 | goto out; |
395 | } |
396 | if ((ip->i_flags & SF_SNAPSHOT) != 0 && |
397 | (vap->va_mode & (S_IXUSR | S_IWUSR | S_IXGRP | S_IWGRP | |
398 | S_IXOTH | S_IWOTH))) { |
399 | error = EPERM; |
400 | goto out; |
401 | } |
402 | error = ulfs_chmod(vp, (int)vap->va_mode, cred, l); |
403 | } |
404 | VN_KNOTE(vp, NOTE_ATTRIB); |
405 | out: |
406 | fstrans_done(vp->v_mount); |
407 | return (error); |
408 | } |
409 | |
410 | /* |
411 | * Change the mode on a file. |
412 | * Inode must be locked before calling. |
413 | */ |
414 | static int |
415 | ulfs_chmod(struct vnode *vp, int mode, kauth_cred_t cred, struct lwp *l) |
416 | { |
417 | struct inode *ip; |
418 | int error; |
419 | |
420 | ip = VTOI(vp); |
421 | |
422 | error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp, |
423 | NULL, genfs_can_chmod(vp->v_type, cred, ip->i_uid, ip->i_gid, mode)); |
424 | if (error) |
425 | return (error); |
426 | |
427 | fstrans_start(vp->v_mount, FSTRANS_SHARED); |
428 | ip->i_mode &= ~ALLPERMS; |
429 | ip->i_mode |= (mode & ALLPERMS); |
430 | ip->i_flag |= IN_CHANGE; |
431 | DIP_ASSIGN(ip, mode, ip->i_mode); |
432 | fstrans_done(vp->v_mount); |
433 | return (0); |
434 | } |
435 | |
436 | /* |
437 | * Perform chown operation on inode ip; |
438 | * inode must be locked prior to call. |
439 | */ |
440 | static int |
441 | ulfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred, |
442 | struct lwp *l) |
443 | { |
444 | struct inode *ip; |
445 | int error = 0; |
446 | #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) |
447 | uid_t ouid; |
448 | gid_t ogid; |
449 | int64_t change; |
450 | #endif |
451 | ip = VTOI(vp); |
452 | error = 0; |
453 | |
454 | if (uid == (uid_t)VNOVAL) |
455 | uid = ip->i_uid; |
456 | if (gid == (gid_t)VNOVAL) |
457 | gid = ip->i_gid; |
458 | |
459 | error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp, |
460 | NULL, genfs_can_chown(cred, ip->i_uid, ip->i_gid, uid, gid)); |
461 | if (error) |
462 | return (error); |
463 | |
464 | fstrans_start(vp->v_mount, FSTRANS_SHARED); |
465 | #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) |
466 | ogid = ip->i_gid; |
467 | ouid = ip->i_uid; |
468 | change = DIP(ip, blocks); |
469 | (void) lfs_chkdq(ip, -change, cred, 0); |
470 | (void) lfs_chkiq(ip, -1, cred, 0); |
471 | #endif |
472 | ip->i_gid = gid; |
473 | DIP_ASSIGN(ip, gid, gid); |
474 | ip->i_uid = uid; |
475 | DIP_ASSIGN(ip, uid, uid); |
476 | #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) |
477 | if ((error = lfs_chkdq(ip, change, cred, 0)) == 0) { |
478 | if ((error = lfs_chkiq(ip, 1, cred, 0)) == 0) |
479 | goto good; |
480 | else |
481 | (void) lfs_chkdq(ip, -change, cred, FORCE); |
482 | } |
483 | ip->i_gid = ogid; |
484 | DIP_ASSIGN(ip, gid, ogid); |
485 | ip->i_uid = ouid; |
486 | DIP_ASSIGN(ip, uid, ouid); |
487 | (void) lfs_chkdq(ip, change, cred, FORCE); |
488 | (void) lfs_chkiq(ip, 1, cred, FORCE); |
489 | fstrans_done(vp->v_mount); |
490 | return (error); |
491 | good: |
492 | #endif /* LFS_QUOTA || LFS_QUOTA2 */ |
493 | ip->i_flag |= IN_CHANGE; |
494 | fstrans_done(vp->v_mount); |
495 | return (0); |
496 | } |
497 | |
498 | int |
499 | ulfs_remove(void *v) |
500 | { |
501 | struct vop_remove_args /* { |
502 | struct vnode *a_dvp; |
503 | struct vnode *a_vp; |
504 | struct componentname *a_cnp; |
505 | } */ *ap = v; |
506 | struct vnode *vp, *dvp; |
507 | struct inode *ip; |
508 | struct mount *mp; |
509 | int error; |
510 | struct ulfs_lookup_results *ulr; |
511 | |
512 | vp = ap->a_vp; |
513 | dvp = ap->a_dvp; |
514 | ip = VTOI(vp); |
515 | mp = dvp->v_mount; |
516 | KASSERT(mp == vp->v_mount); /* XXX Not stable without lock. */ |
517 | |
518 | /* XXX should handle this material another way */ |
519 | ulr = &VTOI(dvp)->i_crap; |
520 | ULFS_CHECK_CRAPCOUNTER(VTOI(dvp)); |
521 | |
522 | fstrans_start(mp, FSTRANS_SHARED); |
523 | if (vp->v_type == VDIR || (ip->i_flags & (IMMUTABLE | APPEND)) || |
524 | (VTOI(dvp)->i_flags & APPEND)) |
525 | error = EPERM; |
526 | else { |
527 | error = ulfs_dirremove(dvp, ulr, |
528 | ip, ap->a_cnp->cn_flags, 0); |
529 | } |
530 | VN_KNOTE(vp, NOTE_DELETE); |
531 | VN_KNOTE(dvp, NOTE_WRITE); |
532 | if (dvp == vp) |
533 | vrele(vp); |
534 | else |
535 | vput(vp); |
536 | vput(dvp); |
537 | fstrans_done(mp); |
538 | return (error); |
539 | } |
540 | |
541 | /* |
542 | * ulfs_link: create hard link. |
543 | */ |
544 | int |
545 | ulfs_link(void *v) |
546 | { |
547 | struct vop_link_v2_args /* { |
548 | struct vnode *a_dvp; |
549 | struct vnode *a_vp; |
550 | struct componentname *a_cnp; |
551 | } */ *ap = v; |
552 | struct vnode *dvp = ap->a_dvp; |
553 | struct vnode *vp = ap->a_vp; |
554 | struct componentname *cnp = ap->a_cnp; |
555 | struct mount *mp = dvp->v_mount; |
556 | struct inode *ip; |
557 | int error; |
558 | struct ulfs_lookup_results *ulr; |
559 | |
560 | KASSERT(dvp != vp); |
561 | KASSERT(vp->v_type != VDIR); |
562 | KASSERT(mp == vp->v_mount); /* XXX Not stable without lock. */ |
563 | |
564 | /* XXX should handle this material another way */ |
565 | ulr = &VTOI(dvp)->i_crap; |
566 | ULFS_CHECK_CRAPCOUNTER(VTOI(dvp)); |
567 | |
568 | fstrans_start(mp, FSTRANS_SHARED); |
569 | error = vn_lock(vp, LK_EXCLUSIVE); |
570 | if (error) { |
571 | VOP_ABORTOP(dvp, cnp); |
572 | goto out2; |
573 | } |
574 | ip = VTOI(vp); |
575 | if ((nlink_t)ip->i_nlink >= LINK_MAX) { |
576 | VOP_ABORTOP(dvp, cnp); |
577 | error = EMLINK; |
578 | goto out1; |
579 | } |
580 | if (ip->i_flags & (IMMUTABLE | APPEND)) { |
581 | VOP_ABORTOP(dvp, cnp); |
582 | error = EPERM; |
583 | goto out1; |
584 | } |
585 | ip->i_nlink++; |
586 | DIP_ASSIGN(ip, nlink, ip->i_nlink); |
587 | ip->i_flag |= IN_CHANGE; |
588 | error = lfs_update(vp, NULL, NULL, UPDATE_DIROP); |
589 | if (!error) { |
590 | error = ulfs_direnter(dvp, ulr, vp, |
591 | cnp, ip->i_number, LFS_IFTODT(ip->i_mode), NULL); |
592 | } |
593 | if (error) { |
594 | ip->i_nlink--; |
595 | DIP_ASSIGN(ip, nlink, ip->i_nlink); |
596 | ip->i_flag |= IN_CHANGE; |
597 | } |
598 | out1: |
599 | VOP_UNLOCK(vp); |
600 | out2: |
601 | VN_KNOTE(vp, NOTE_LINK); |
602 | VN_KNOTE(dvp, NOTE_WRITE); |
603 | fstrans_done(mp); |
604 | return (error); |
605 | } |
606 | |
607 | /* |
608 | * whiteout vnode call |
609 | */ |
610 | int |
611 | ulfs_whiteout(void *v) |
612 | { |
613 | struct vop_whiteout_args /* { |
614 | struct vnode *a_dvp; |
615 | struct componentname *a_cnp; |
616 | int a_flags; |
617 | } */ *ap = v; |
618 | struct vnode *dvp = ap->a_dvp; |
619 | struct componentname *cnp = ap->a_cnp; |
620 | int error; |
621 | struct ulfsmount *ump = VFSTOULFS(dvp->v_mount); |
622 | struct lfs *fs = ump->um_lfs; |
623 | struct ulfs_lookup_results *ulr; |
624 | |
625 | /* XXX should handle this material another way */ |
626 | ulr = &VTOI(dvp)->i_crap; |
627 | ULFS_CHECK_CRAPCOUNTER(VTOI(dvp)); |
628 | |
629 | error = 0; |
630 | switch (ap->a_flags) { |
631 | case LOOKUP: |
632 | /* 4.4 format directories support whiteout operations */ |
633 | if (fs->um_maxsymlinklen > 0) |
634 | return (0); |
635 | return (EOPNOTSUPP); |
636 | |
637 | case CREATE: |
638 | /* create a new directory whiteout */ |
639 | fstrans_start(dvp->v_mount, FSTRANS_SHARED); |
640 | #ifdef DIAGNOSTIC |
641 | if (fs->um_maxsymlinklen <= 0) |
642 | panic("ulfs_whiteout: old format filesystem" ); |
643 | #endif |
644 | |
645 | error = ulfs_direnter(dvp, ulr, NULL, |
646 | cnp, ULFS_WINO, LFS_DT_WHT, NULL); |
647 | break; |
648 | |
649 | case DELETE: |
650 | /* remove an existing directory whiteout */ |
651 | fstrans_start(dvp->v_mount, FSTRANS_SHARED); |
652 | #ifdef DIAGNOSTIC |
653 | if (fs->um_maxsymlinklen <= 0) |
654 | panic("ulfs_whiteout: old format filesystem" ); |
655 | #endif |
656 | |
657 | cnp->cn_flags &= ~DOWHITEOUT; |
658 | error = ulfs_dirremove(dvp, ulr, NULL, cnp->cn_flags, 0); |
659 | break; |
660 | default: |
661 | panic("ulfs_whiteout: unknown op" ); |
662 | /* NOTREACHED */ |
663 | } |
664 | fstrans_done(dvp->v_mount); |
665 | return (error); |
666 | } |
667 | |
668 | int |
669 | ulfs_rmdir(void *v) |
670 | { |
671 | struct vop_rmdir_args /* { |
672 | struct vnode *a_dvp; |
673 | struct vnode *a_vp; |
674 | struct componentname *a_cnp; |
675 | } */ *ap = v; |
676 | struct vnode *vp, *dvp; |
677 | struct componentname *cnp; |
678 | struct inode *ip, *dp; |
679 | int error; |
680 | struct ulfs_lookup_results *ulr; |
681 | |
682 | vp = ap->a_vp; |
683 | dvp = ap->a_dvp; |
684 | cnp = ap->a_cnp; |
685 | ip = VTOI(vp); |
686 | dp = VTOI(dvp); |
687 | |
688 | /* XXX should handle this material another way */ |
689 | ulr = &dp->i_crap; |
690 | ULFS_CHECK_CRAPCOUNTER(dp); |
691 | |
692 | /* |
693 | * No rmdir "." or of mounted directories please. |
694 | */ |
695 | if (dp == ip || vp->v_mountedhere != NULL) { |
696 | if (dp == ip) |
697 | vrele(dvp); |
698 | else |
699 | vput(dvp); |
700 | vput(vp); |
701 | return (EINVAL); |
702 | } |
703 | |
704 | fstrans_start(dvp->v_mount, FSTRANS_SHARED); |
705 | |
706 | /* |
707 | * Do not remove a directory that is in the process of being renamed. |
708 | * Verify that the directory is empty (and valid). (Rmdir ".." won't |
709 | * be valid since ".." will contain a reference to the current |
710 | * directory and thus be non-empty.) |
711 | */ |
712 | error = 0; |
713 | if (ip->i_nlink != 2 || |
714 | !ulfs_dirempty(ip, dp->i_number, cnp->cn_cred)) { |
715 | error = ENOTEMPTY; |
716 | goto out; |
717 | } |
718 | if ((dp->i_flags & APPEND) || |
719 | (ip->i_flags & (IMMUTABLE | APPEND))) { |
720 | error = EPERM; |
721 | goto out; |
722 | } |
723 | /* |
724 | * Delete reference to directory before purging |
725 | * inode. If we crash in between, the directory |
726 | * will be reattached to lost+found, |
727 | */ |
728 | error = ulfs_dirremove(dvp, ulr, ip, cnp->cn_flags, 1); |
729 | if (error) { |
730 | goto out; |
731 | } |
732 | VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); |
733 | cache_purge(dvp); |
734 | /* |
735 | * Truncate inode. The only stuff left in the directory is "." and |
736 | * "..". The "." reference is inconsequential since we're quashing |
737 | * it. |
738 | */ |
739 | dp->i_nlink--; |
740 | DIP_ASSIGN(dp, nlink, dp->i_nlink); |
741 | dp->i_flag |= IN_CHANGE; |
742 | ip->i_nlink--; |
743 | DIP_ASSIGN(ip, nlink, ip->i_nlink); |
744 | ip->i_flag |= IN_CHANGE; |
745 | error = lfs_truncate(vp, (off_t)0, IO_SYNC, cnp->cn_cred); |
746 | cache_purge(vp); |
747 | #ifdef LFS_DIRHASH |
748 | if (ip->i_dirhash != NULL) |
749 | ulfsdirhash_free(ip); |
750 | #endif |
751 | out: |
752 | VN_KNOTE(vp, NOTE_DELETE); |
753 | vput(vp); |
754 | fstrans_done(dvp->v_mount); |
755 | vput(dvp); |
756 | return (error); |
757 | } |
758 | |
759 | /* |
760 | * Vnode op for reading directories. |
761 | * |
762 | * This routine handles converting from the on-disk directory format |
763 | * "struct lfs_direct" to the in-memory format "struct dirent" as well as |
764 | * byte swapping the entries if necessary. |
765 | */ |
766 | int |
767 | ulfs_readdir(void *v) |
768 | { |
769 | struct vop_readdir_args /* { |
770 | struct vnode *a_vp; |
771 | struct uio *a_uio; |
772 | kauth_cred_t a_cred; |
773 | int *a_eofflag; |
774 | off_t **a_cookies; |
775 | int *ncookies; |
776 | } */ *ap = v; |
777 | struct vnode *vp = ap->a_vp; |
778 | LFS_DIRHEADER *cdp, *ecdp; |
779 | struct dirent *ndp; |
780 | char *cdbuf, *ndbuf, *endp; |
781 | struct uio auio, *uio; |
782 | struct iovec aiov; |
783 | int error; |
784 | size_t count, ccount, rcount, cdbufsz, ndbufsz; |
785 | off_t off, *ccp; |
786 | off_t startoff; |
787 | size_t skipbytes; |
788 | struct ulfsmount *ump = VFSTOULFS(vp->v_mount); |
789 | struct lfs *fs = ump->um_lfs; |
790 | uio = ap->a_uio; |
791 | count = uio->uio_resid; |
792 | rcount = count - ((uio->uio_offset + count) & (fs->um_dirblksiz - 1)); |
793 | |
794 | if (rcount < LFS_DIRECTSIZ(fs, 0) || count < _DIRENT_MINSIZE(ndp)) |
795 | return EINVAL; |
796 | |
797 | startoff = uio->uio_offset & ~(fs->um_dirblksiz - 1); |
798 | skipbytes = uio->uio_offset - startoff; |
799 | rcount += skipbytes; |
800 | |
801 | auio.uio_iov = &aiov; |
802 | auio.uio_iovcnt = 1; |
803 | auio.uio_offset = startoff; |
804 | auio.uio_resid = rcount; |
805 | UIO_SETUP_SYSSPACE(&auio); |
806 | auio.uio_rw = UIO_READ; |
807 | cdbufsz = rcount; |
808 | cdbuf = kmem_alloc(cdbufsz, KM_SLEEP); |
809 | aiov.iov_base = cdbuf; |
810 | aiov.iov_len = rcount; |
811 | error = VOP_READ(vp, &auio, 0, ap->a_cred); |
812 | if (error != 0) { |
813 | kmem_free(cdbuf, cdbufsz); |
814 | return error; |
815 | } |
816 | |
817 | rcount -= auio.uio_resid; |
818 | |
819 | cdp = (LFS_DIRHEADER *)(void *)cdbuf; |
820 | ecdp = (LFS_DIRHEADER *)(void *)&cdbuf[rcount]; |
821 | |
822 | ndbufsz = count; |
823 | ndbuf = kmem_alloc(ndbufsz, KM_SLEEP); |
824 | ndp = (struct dirent *)(void *)ndbuf; |
825 | endp = &ndbuf[count]; |
826 | |
827 | off = uio->uio_offset; |
828 | if (ap->a_cookies) { |
829 | ccount = rcount / _DIRENT_RECLEN(ndp, 1); |
830 | ccp = *(ap->a_cookies) = malloc(ccount * sizeof(*ccp), |
831 | M_TEMP, M_WAITOK); |
832 | } else { |
833 | /* XXX: GCC */ |
834 | ccount = 0; |
835 | ccp = NULL; |
836 | } |
837 | |
838 | while (cdp < ecdp) { |
839 | if (skipbytes > 0) { |
840 | if (lfs_dir_getreclen(fs, cdp) <= skipbytes) { |
841 | skipbytes -= lfs_dir_getreclen(fs, cdp); |
842 | cdp = LFS_NEXTDIR(fs, cdp); |
843 | continue; |
844 | } |
845 | /* |
846 | * invalid cookie. |
847 | */ |
848 | error = EINVAL; |
849 | goto out; |
850 | } |
851 | if (lfs_dir_getreclen(fs, cdp) == 0) { |
852 | struct dirent *ondp = ndp; |
853 | ndp->d_reclen = _DIRENT_MINSIZE(ndp); |
854 | ndp = _DIRENT_NEXT(ndp); |
855 | ondp->d_reclen = 0; |
856 | cdp = ecdp; |
857 | break; |
858 | } |
859 | ndp->d_type = lfs_dir_gettype(fs, cdp); |
860 | ndp->d_namlen = lfs_dir_getnamlen(fs, cdp); |
861 | ndp->d_reclen = _DIRENT_RECLEN(ndp, ndp->d_namlen); |
862 | if ((char *)(void *)ndp + ndp->d_reclen + |
863 | _DIRENT_MINSIZE(ndp) > endp) |
864 | break; |
865 | ndp->d_fileno = lfs_dir_getino(fs, cdp); |
866 | (void)memcpy(ndp->d_name, lfs_dir_nameptr(fs, cdp), |
867 | ndp->d_namlen); |
868 | memset(&ndp->d_name[ndp->d_namlen], 0, |
869 | ndp->d_reclen - _DIRENT_NAMEOFF(ndp) - ndp->d_namlen); |
870 | off += lfs_dir_getreclen(fs, cdp); |
871 | if (ap->a_cookies) { |
872 | KASSERT(ccp - *(ap->a_cookies) < ccount); |
873 | *(ccp++) = off; |
874 | } |
875 | ndp = _DIRENT_NEXT(ndp); |
876 | cdp = LFS_NEXTDIR(fs, cdp); |
877 | } |
878 | |
879 | count = ((char *)(void *)ndp - ndbuf); |
880 | error = uiomove(ndbuf, count, uio); |
881 | out: |
882 | if (ap->a_cookies) { |
883 | if (error) { |
884 | free(*(ap->a_cookies), M_TEMP); |
885 | *(ap->a_cookies) = NULL; |
886 | *(ap->a_ncookies) = 0; |
887 | } else { |
888 | *ap->a_ncookies = ccp - *(ap->a_cookies); |
889 | } |
890 | } |
891 | uio->uio_offset = off; |
892 | kmem_free(ndbuf, ndbufsz); |
893 | kmem_free(cdbuf, cdbufsz); |
894 | *ap->a_eofflag = VTOI(vp)->i_size <= uio->uio_offset; |
895 | return error; |
896 | } |
897 | |
898 | /* |
899 | * Return target name of a symbolic link |
900 | */ |
901 | int |
902 | ulfs_readlink(void *v) |
903 | { |
904 | struct vop_readlink_args /* { |
905 | struct vnode *a_vp; |
906 | struct uio *a_uio; |
907 | kauth_cred_t a_cred; |
908 | } */ *ap = v; |
909 | struct vnode *vp = ap->a_vp; |
910 | struct inode *ip = VTOI(vp); |
911 | struct ulfsmount *ump = VFSTOULFS(vp->v_mount); |
912 | struct lfs *fs = ump->um_lfs; |
913 | int isize; |
914 | |
915 | /* |
916 | * The test against um_maxsymlinklen is off by one; it should |
917 | * theoretically be <=, not <. However, it cannot be changed |
918 | * as that would break compatibility with existing fs images. |
919 | */ |
920 | |
921 | isize = ip->i_size; |
922 | if (isize < fs->um_maxsymlinklen || |
923 | (fs->um_maxsymlinklen == 0 && DIP(ip, blocks) == 0)) { |
924 | uiomove((char *)SHORTLINK(ip), isize, ap->a_uio); |
925 | return (0); |
926 | } |
927 | return (lfs_bufrd(vp, ap->a_uio, 0, ap->a_cred)); |
928 | } |
929 | |
930 | /* |
931 | * Print out the contents of an inode. |
932 | */ |
933 | int |
934 | ulfs_print(void *v) |
935 | { |
936 | struct vop_print_args /* { |
937 | struct vnode *a_vp; |
938 | } */ *ap = v; |
939 | struct vnode *vp; |
940 | struct inode *ip; |
941 | |
942 | vp = ap->a_vp; |
943 | ip = VTOI(vp); |
944 | printf("tag VT_ULFS, ino %llu, on dev %llu, %llu" , |
945 | (unsigned long long)ip->i_number, |
946 | (unsigned long long)major(ip->i_dev), |
947 | (unsigned long long)minor(ip->i_dev)); |
948 | printf(" flags 0x%x, nlink %d\n" , |
949 | ip->i_flag, ip->i_nlink); |
950 | printf("\tmode 0%o, owner %d, group %d, size %qd" , |
951 | ip->i_mode, ip->i_uid, ip->i_gid, |
952 | (long long)ip->i_size); |
953 | if (vp->v_type == VFIFO) |
954 | VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v); |
955 | printf("\n" ); |
956 | return (0); |
957 | } |
958 | |
959 | /* |
960 | * Read wrapper for special devices. |
961 | */ |
962 | int |
963 | ulfsspec_read(void *v) |
964 | { |
965 | struct vop_read_args /* { |
966 | struct vnode *a_vp; |
967 | struct uio *a_uio; |
968 | int a_ioflag; |
969 | kauth_cred_t a_cred; |
970 | } */ *ap = v; |
971 | |
972 | /* |
973 | * Set access flag. |
974 | */ |
975 | if ((ap->a_vp->v_mount->mnt_flag & MNT_NODEVMTIME) == 0) |
976 | VTOI(ap->a_vp)->i_flag |= IN_ACCESS; |
977 | return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap)); |
978 | } |
979 | |
980 | /* |
981 | * Write wrapper for special devices. |
982 | */ |
983 | int |
984 | ulfsspec_write(void *v) |
985 | { |
986 | struct vop_write_args /* { |
987 | struct vnode *a_vp; |
988 | struct uio *a_uio; |
989 | int a_ioflag; |
990 | kauth_cred_t a_cred; |
991 | } */ *ap = v; |
992 | |
993 | /* |
994 | * Set update and change flags. |
995 | */ |
996 | if ((ap->a_vp->v_mount->mnt_flag & MNT_NODEVMTIME) == 0) |
997 | VTOI(ap->a_vp)->i_flag |= IN_MODIFY; |
998 | return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap)); |
999 | } |
1000 | |
1001 | /* |
1002 | * Read wrapper for fifo's |
1003 | */ |
1004 | int |
1005 | ulfsfifo_read(void *v) |
1006 | { |
1007 | struct vop_read_args /* { |
1008 | struct vnode *a_vp; |
1009 | struct uio *a_uio; |
1010 | int a_ioflag; |
1011 | kauth_cred_t a_cred; |
1012 | } */ *ap = v; |
1013 | |
1014 | /* |
1015 | * Set access flag. |
1016 | */ |
1017 | VTOI(ap->a_vp)->i_flag |= IN_ACCESS; |
1018 | return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap)); |
1019 | } |
1020 | |
1021 | /* |
1022 | * Write wrapper for fifo's. |
1023 | */ |
1024 | int |
1025 | ulfsfifo_write(void *v) |
1026 | { |
1027 | struct vop_write_args /* { |
1028 | struct vnode *a_vp; |
1029 | struct uio *a_uio; |
1030 | int a_ioflag; |
1031 | kauth_cred_t a_cred; |
1032 | } */ *ap = v; |
1033 | |
1034 | /* |
1035 | * Set update and change flags. |
1036 | */ |
1037 | VTOI(ap->a_vp)->i_flag |= IN_MODIFY; |
1038 | return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap)); |
1039 | } |
1040 | |
1041 | /* |
1042 | * Return POSIX pathconf information applicable to ulfs filesystems. |
1043 | */ |
1044 | int |
1045 | ulfs_pathconf(void *v) |
1046 | { |
1047 | struct vop_pathconf_args /* { |
1048 | struct vnode *a_vp; |
1049 | int a_name; |
1050 | register_t *a_retval; |
1051 | } */ *ap = v; |
1052 | |
1053 | switch (ap->a_name) { |
1054 | case _PC_LINK_MAX: |
1055 | *ap->a_retval = LINK_MAX; |
1056 | return (0); |
1057 | case _PC_NAME_MAX: |
1058 | *ap->a_retval = LFS_MAXNAMLEN; |
1059 | return (0); |
1060 | case _PC_PATH_MAX: |
1061 | *ap->a_retval = PATH_MAX; |
1062 | return (0); |
1063 | case _PC_PIPE_BUF: |
1064 | *ap->a_retval = PIPE_BUF; |
1065 | return (0); |
1066 | case _PC_CHOWN_RESTRICTED: |
1067 | *ap->a_retval = 1; |
1068 | return (0); |
1069 | case _PC_NO_TRUNC: |
1070 | *ap->a_retval = 1; |
1071 | return (0); |
1072 | case _PC_SYNC_IO: |
1073 | *ap->a_retval = 1; |
1074 | return (0); |
1075 | case _PC_FILESIZEBITS: |
1076 | *ap->a_retval = 42; |
1077 | return (0); |
1078 | case _PC_SYMLINK_MAX: |
1079 | *ap->a_retval = MAXPATHLEN; |
1080 | return (0); |
1081 | case _PC_2_SYMLINKS: |
1082 | *ap->a_retval = 1; |
1083 | return (0); |
1084 | default: |
1085 | return (EINVAL); |
1086 | } |
1087 | /* NOTREACHED */ |
1088 | } |
1089 | |
1090 | /* |
1091 | * Advisory record locking support |
1092 | */ |
1093 | int |
1094 | ulfs_advlock(void *v) |
1095 | { |
1096 | struct vop_advlock_args /* { |
1097 | struct vnode *a_vp; |
1098 | void * a_id; |
1099 | int a_op; |
1100 | struct flock *a_fl; |
1101 | int a_flags; |
1102 | } */ *ap = v; |
1103 | struct inode *ip; |
1104 | |
1105 | ip = VTOI(ap->a_vp); |
1106 | return lf_advlock(ap, &ip->i_lockf, ip->i_size); |
1107 | } |
1108 | |
1109 | /* |
1110 | * Initialize the vnode associated with a new inode, handle aliased |
1111 | * vnodes. |
1112 | */ |
1113 | void |
1114 | ulfs_vinit(struct mount *mntp, int (**specops)(void *), int (**fifoops)(void *), |
1115 | struct vnode **vpp) |
1116 | { |
1117 | struct timeval tv; |
1118 | struct inode *ip; |
1119 | struct vnode *vp; |
1120 | dev_t rdev; |
1121 | struct ulfsmount *ump; |
1122 | |
1123 | vp = *vpp; |
1124 | ip = VTOI(vp); |
1125 | switch(vp->v_type = IFTOVT(ip->i_mode)) { |
1126 | case VCHR: |
1127 | case VBLK: |
1128 | vp->v_op = specops; |
1129 | ump = ip->i_ump; |
1130 | // XXX clean this up |
1131 | if (ump->um_fstype == ULFS1) |
1132 | rdev = (dev_t)ulfs_rw32(ip->i_din->u_32.di_rdev, |
1133 | ULFS_MPNEEDSWAP(ump->um_lfs)); |
1134 | else |
1135 | rdev = (dev_t)ulfs_rw64(ip->i_din->u_64.di_rdev, |
1136 | ULFS_MPNEEDSWAP(ump->um_lfs)); |
1137 | spec_node_init(vp, rdev); |
1138 | break; |
1139 | case VFIFO: |
1140 | vp->v_op = fifoops; |
1141 | break; |
1142 | case VNON: |
1143 | case VBAD: |
1144 | case VSOCK: |
1145 | case VLNK: |
1146 | case VDIR: |
1147 | case VREG: |
1148 | break; |
1149 | } |
1150 | if (ip->i_number == ULFS_ROOTINO) |
1151 | vp->v_vflag |= VV_ROOT; |
1152 | /* |
1153 | * Initialize modrev times |
1154 | */ |
1155 | getmicrouptime(&tv); |
1156 | ip->i_modrev = (uint64_t)(uint)tv.tv_sec << 32 |
1157 | | tv.tv_usec * 4294u; |
1158 | *vpp = vp; |
1159 | } |
1160 | |
1161 | /* |
1162 | * Allocate len bytes at offset off. |
1163 | */ |
1164 | int |
1165 | ulfs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags, |
1166 | kauth_cred_t cred) |
1167 | { |
1168 | struct inode *ip = VTOI(vp); |
1169 | int error, delta, bshift, bsize; |
1170 | UVMHIST_FUNC("ulfs_gop_alloc" ); UVMHIST_CALLED(ubchist); |
1171 | |
1172 | error = 0; |
1173 | bshift = vp->v_mount->mnt_fs_bshift; |
1174 | bsize = 1 << bshift; |
1175 | |
1176 | delta = off & (bsize - 1); |
1177 | off -= delta; |
1178 | len += delta; |
1179 | |
1180 | while (len > 0) { |
1181 | bsize = MIN(bsize, len); |
1182 | |
1183 | error = lfs_balloc(vp, off, bsize, cred, flags, NULL); |
1184 | if (error) { |
1185 | goto out; |
1186 | } |
1187 | |
1188 | /* |
1189 | * increase file size now, lfs_balloc() requires that |
1190 | * EOF be up-to-date before each call. |
1191 | */ |
1192 | |
1193 | if (ip->i_size < off + bsize) { |
1194 | UVMHIST_LOG(ubchist, "vp %p old 0x%x new 0x%x" , |
1195 | vp, ip->i_size, off + bsize, 0); |
1196 | ip->i_size = off + bsize; |
1197 | DIP_ASSIGN(ip, size, ip->i_size); |
1198 | } |
1199 | |
1200 | off += bsize; |
1201 | len -= bsize; |
1202 | } |
1203 | |
1204 | out: |
1205 | return error; |
1206 | } |
1207 | |
1208 | void |
1209 | ulfs_gop_markupdate(struct vnode *vp, int flags) |
1210 | { |
1211 | u_int32_t mask = 0; |
1212 | |
1213 | if ((flags & GOP_UPDATE_ACCESSED) != 0) { |
1214 | mask = IN_ACCESS; |
1215 | } |
1216 | if ((flags & GOP_UPDATE_MODIFIED) != 0) { |
1217 | if (vp->v_type == VREG) { |
1218 | mask |= IN_CHANGE | IN_UPDATE; |
1219 | } else { |
1220 | mask |= IN_MODIFY; |
1221 | } |
1222 | } |
1223 | if (mask) { |
1224 | struct inode *ip = VTOI(vp); |
1225 | |
1226 | ip->i_flag |= mask; |
1227 | } |
1228 | } |
1229 | |
1230 | int |
1231 | ulfs_bufio(enum uio_rw rw, struct vnode *vp, void *buf, size_t len, off_t off, |
1232 | int ioflg, kauth_cred_t cred, size_t *aresid, struct lwp *l) |
1233 | { |
1234 | struct iovec iov; |
1235 | struct uio uio; |
1236 | int error; |
1237 | |
1238 | KASSERT(ISSET(ioflg, IO_NODELOCKED)); |
1239 | KASSERT(VOP_ISLOCKED(vp)); |
1240 | KASSERT(rw != UIO_WRITE || VOP_ISLOCKED(vp) == LK_EXCLUSIVE); |
1241 | |
1242 | iov.iov_base = buf; |
1243 | iov.iov_len = len; |
1244 | uio.uio_iov = &iov; |
1245 | uio.uio_iovcnt = 1; |
1246 | uio.uio_resid = len; |
1247 | uio.uio_offset = off; |
1248 | uio.uio_rw = rw; |
1249 | UIO_SETUP_SYSSPACE(&uio); |
1250 | |
1251 | switch (rw) { |
1252 | case UIO_READ: |
1253 | error = lfs_bufrd(vp, &uio, ioflg, cred); |
1254 | break; |
1255 | case UIO_WRITE: |
1256 | error = lfs_bufwr(vp, &uio, ioflg, cred); |
1257 | break; |
1258 | default: |
1259 | panic("invalid uio rw: %d" , (int)rw); |
1260 | } |
1261 | |
1262 | if (aresid) |
1263 | *aresid = uio.uio_resid; |
1264 | else if (uio.uio_resid && error == 0) |
1265 | error = EIO; |
1266 | |
1267 | KASSERT(VOP_ISLOCKED(vp)); |
1268 | KASSERT(rw != UIO_WRITE || VOP_ISLOCKED(vp) == LK_EXCLUSIVE); |
1269 | return error; |
1270 | } |
1271 | |