]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vnops.c
19006da0ea82e25b227cd4441b311913df24e88a
[apple/xnu.git] / bsd / hfs / hfs_vnops.c
1 /*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/file.h>
26 #include <sys/dirent.h>
27 #include <sys/stat.h>
28 #include <sys/buf.h>
29 #include <sys/mount.h>
30 #include <sys/vnode.h>
31 #include <sys/malloc.h>
32 #include <sys/namei.h>
33 #include <sys/ubc.h>
34 #include <sys/quota.h>
35
36 #include <miscfs/specfs/specdev.h>
37 #include <miscfs/fifofs/fifo.h>
38 #include <vfs/vfs_support.h>
39 #include <machine/spl.h>
40
41 #include <sys/kdebug.h>
42
43 #include "hfs.h"
44 #include "hfs_catalog.h"
45 #include "hfs_cnode.h"
46 #include "hfs_lockf.h"
47 #include "hfs_dbg.h"
48 #include "hfs_mount.h"
49 #include "hfs_quota.h"
50 #include "hfs_endian.h"
51
52 #include "hfscommon/headers/BTreesInternal.h"
53 #include "hfscommon/headers/FileMgrInternal.h"
54
55 #define MAKE_DELETED_NAME(NAME,FID) \
56 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
57
58
59 extern uid_t console_user;
60
61 /* Global vfs data structures for hfs */
62
63
64 extern int groupmember(gid_t gid, struct ucred *cred);
65
66 static int hfs_makenode(int mode, struct vnode *dvp, struct vnode **vpp,
67 struct componentname *cnp);
68
69 static int hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp,
70 struct vnode **rvpp, struct proc *p);
71
72 static int hfs_metasync(struct hfsmount *hfsmp, daddr_t node, struct proc *p);
73
74 int hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags);
75
76 int hfs_chflags(struct vnode *vp, u_long flags, struct ucred *cred,
77 struct proc *p);
78 int hfs_chmod(struct vnode *vp, int mode, struct ucred *cred,
79 struct proc *p);
80 int hfs_chown(struct vnode *vp, uid_t uid, gid_t gid,
81 struct ucred *cred, struct proc *p);
82
83 /*****************************************************************************
84 *
85 * Common Operations on vnodes
86 *
87 *****************************************************************************/
88
89 /*
90 * Create a regular file
91 #% create dvp L U U
92 #% create vpp - L -
93 #
94 vop_create {
95 IN WILLRELE struct vnode *dvp;
96 OUT struct vnode **vpp;
97 IN struct componentname *cnp;
98 IN struct vattr *vap;
99
100 We are responsible for freeing the namei buffer,
101 it is done in hfs_makenode()
102 */
103
104 static int
105 hfs_create(ap)
106 struct vop_create_args /* {
107 struct vnode *a_dvp;
108 struct vnode **a_vpp;
109 struct componentname *a_cnp;
110 struct vattr *a_vap;
111 } */ *ap;
112 {
113 struct vattr *vap = ap->a_vap;
114
115 return (hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
116 ap->a_dvp, ap->a_vpp, ap->a_cnp));
117 }
118
119
120 /*
121 * Mknod vnode call
122
123 #% mknod dvp L U U
124 #% mknod vpp - X -
125 #
126 vop_mknod {
127 IN WILLRELE struct vnode *dvp;
128 OUT WILLRELE struct vnode **vpp;
129 IN struct componentname *cnp;
130 IN struct vattr *vap;
131 */
132 /* ARGSUSED */
133
134 static int
135 hfs_mknod(ap)
136 struct vop_mknod_args /* {
137 struct vnode *a_dvp;
138 struct vnode **a_vpp;
139 struct componentname *a_cnp;
140 struct vattr *a_vap;
141 } */ *ap;
142 {
143 struct vattr *vap = ap->a_vap;
144 struct vnode **vpp = ap->a_vpp;
145 struct cnode *cp;
146 int error;
147
148 if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
149 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
150 vput(ap->a_dvp);
151 return (EOPNOTSUPP);
152 }
153
154 /* Create the vnode */
155 error = hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
156 ap->a_dvp, vpp, ap->a_cnp);
157 if (error)
158 return (error);
159 cp = VTOC(*vpp);
160 cp->c_flag |= C_ACCESS | C_CHANGE | C_UPDATE;
161 if ((vap->va_rdev != VNOVAL) &&
162 (vap->va_type == VBLK || vap->va_type == VCHR))
163 cp->c_rdev = vap->va_rdev;
164 /*
165 * Remove cnode so that it will be reloaded by lookup and
166 * checked to see if it is an alias of an existing vnode.
167 * Note: unlike UFS, we don't bash v_type here.
168 */
169 vput(*vpp);
170 vgone(*vpp);
171 *vpp = 0;
172 return (0);
173 }
174
175
176 /*
177 * Open called.
178 #% open vp L L L
179 #
180 vop_open {
181 IN struct vnode *vp;
182 IN int mode;
183 IN struct ucred *cred;
184 IN struct proc *p;
185 */
186
187
188 static int
189 hfs_open(ap)
190 struct vop_open_args /* {
191 struct vnode *a_vp;
192 int a_mode;
193 struct ucred *a_cred;
194 struct proc *a_p;
195 } */ *ap;
196 {
197 struct vnode *vp = ap->a_vp;
198
199 /*
200 * Files marked append-only must be opened for appending.
201 */
202 if ((vp->v_type != VDIR) && (VTOC(vp)->c_flags & APPEND) &&
203 (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
204 return (EPERM);
205
206 return (0);
207 }
208
209 /*
210 * Close called.
211 *
212 * Update the times on the cnode.
213 #% close vp U U U
214 #
215 vop_close {
216 IN struct vnode *vp;
217 IN int fflag;
218 IN struct ucred *cred;
219 IN struct proc *p;
220 */
221
222
223 static int
224 hfs_close(ap)
225 struct vop_close_args /* {
226 struct vnode *a_vp;
227 int a_fflag;
228 struct ucred *a_cred;
229 struct proc *a_p;
230 } */ *ap;
231 {
232 register struct vnode *vp = ap->a_vp;
233 register struct cnode *cp = VTOC(vp);
234 register struct filefork *fp = VTOF(vp);
235 struct proc *p = ap->a_p;
236 struct timeval tv;
237 off_t leof;
238 u_long blks, blocksize;
239 int devBlockSize;
240 int error;
241
242 simple_lock(&vp->v_interlock);
243 if ((!UBCISVALID(vp) && vp->v_usecount > 1)
244 || (UBCISVALID(vp) && ubc_isinuse(vp, 1))) {
245 tv = time;
246 CTIMES(cp, &tv, &tv);
247 }
248 simple_unlock(&vp->v_interlock);
249
250 /*
251 * VOP_CLOSE can be called with vp locked (from vclean).
252 * We check for this case using VOP_ISLOCKED and bail.
253 *
254 * XXX During a force unmount we won't do the cleanup below!
255 */
256 if (vp->v_type == VDIR || VOP_ISLOCKED(vp))
257 return (0);
258
259 leof = fp->ff_size;
260
261 if ((fp->ff_blocks > 0) && !ISSET(cp->c_flag, C_DELETED)) {
262 enum vtype our_type = vp->v_type;
263 u_long our_id = vp->v_id;
264 int was_nocache = ISSET(vp->v_flag, VNOCACHE_DATA);
265
266 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
267 if (error)
268 return (0);
269 /*
270 * Since we can context switch in vn_lock our vnode
271 * could get recycled (eg umount -f). Double check
272 * that its still ours.
273 */
274 if (vp->v_type != our_type || vp->v_id != our_id
275 || cp != VTOC(vp) || !UBCINFOEXISTS(vp)) {
276 VOP_UNLOCK(vp, 0, p);
277 return (0);
278 }
279
280 /*
281 * Last chance to explicitly zero out the areas
282 * that are currently marked invalid:
283 */
284 VOP_DEVBLOCKSIZE(cp->c_devvp, &devBlockSize);
285 (void) cluster_push(vp);
286 SET(vp->v_flag, VNOCACHE_DATA); /* Don't cache zeros */
287 while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) {
288 struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges);
289 off_t start = invalid_range->rl_start;
290 off_t end = invalid_range->rl_end;
291
292 /* The range about to be written must be validated
293 * first, so that VOP_CMAP() will return the
294 * appropriate mapping for the cluster code:
295 */
296 rl_remove(start, end, &fp->ff_invalidranges);
297
298 (void) cluster_write(vp, (struct uio *) 0, leof,
299 invalid_range->rl_end + 1, invalid_range->rl_start,
300 (off_t)0, devBlockSize, IO_HEADZEROFILL | IO_NOZERODIRTY);
301
302 if (ISSET(vp->v_flag, VHASDIRTY))
303 (void) cluster_push(vp);
304
305 cp->c_flag |= C_MODIFIED;
306 }
307 cp->c_flag &= ~C_ZFWANTSYNC;
308 cp->c_zftimeout = 0;
309 blocksize = VTOVCB(vp)->blockSize;
310 blks = leof / blocksize;
311 if (((off_t)blks * (off_t)blocksize) != leof)
312 blks++;
313 /*
314 * Shrink the peof to the smallest size neccessary to contain the leof.
315 */
316 if (blks < fp->ff_blocks)
317 (void) VOP_TRUNCATE(vp, leof, IO_NDELAY, ap->a_cred, p);
318 (void) cluster_push(vp);
319
320 if (!was_nocache)
321 CLR(vp->v_flag, VNOCACHE_DATA);
322
323 /*
324 * If the VOP_TRUNCATE didn't happen to flush the vnode's
325 * information out to disk, force it to be updated now that
326 * all invalid ranges have been zero-filled and validated:
327 */
328 if (cp->c_flag & C_MODIFIED) {
329 tv = time;
330 VOP_UPDATE(vp, &tv, &tv, 0);
331 }
332 VOP_UNLOCK(vp, 0, p);
333 }
334 return (0);
335 }
336
337 /*
338 #% access vp L L L
339 #
340 vop_access {
341 IN struct vnode *vp;
342 IN int mode;
343 IN struct ucred *cred;
344 IN struct proc *p;
345
346 */
347
348 static int
349 hfs_access(ap)
350 struct vop_access_args /* {
351 struct vnode *a_vp;
352 int a_mode;
353 struct ucred *a_cred;
354 struct proc *a_p;
355 } */ *ap;
356 {
357 struct vnode *vp = ap->a_vp;
358 struct cnode *cp = VTOC(vp);
359 struct ucred *cred = ap->a_cred;
360 register gid_t *gp;
361 mode_t mode = ap->a_mode;
362 mode_t mask = 0;
363 int i;
364 int error;
365
366 /*
367 * Disallow write attempts on read-only file systems;
368 * unless the file is a socket, fifo, or a block or
369 * character device resident on the file system.
370 */
371 if (mode & VWRITE) {
372 switch (vp->v_type) {
373 case VDIR:
374 case VLNK:
375 case VREG:
376 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
377 return (EROFS);
378 #if QUOTA
379 if ((error = hfs_getinoquota(cp)))
380 return (error);
381 #endif /* QUOTA */
382 break;
383 }
384 }
385
386 /* If immutable bit set, nobody gets to write it. */
387 if ((mode & VWRITE) && (cp->c_flags & IMMUTABLE))
388 return (EPERM);
389
390 /* Otherwise, user id 0 always gets access. */
391 if (ap->a_cred->cr_uid == 0)
392 return (0);
393
394 mask = 0;
395
396 /* Otherwise, check the owner. */
397 if (hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, ap->a_p, false) == 0) {
398 if (mode & VEXEC)
399 mask |= S_IXUSR;
400 if (mode & VREAD)
401 mask |= S_IRUSR;
402 if (mode & VWRITE)
403 mask |= S_IWUSR;
404 return ((cp->c_mode & mask) == mask ? 0 : EACCES);
405 }
406
407 /* Otherwise, check the groups. */
408 if (! (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS)) {
409 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
410 if (cp->c_gid == *gp) {
411 if (mode & VEXEC)
412 mask |= S_IXGRP;
413 if (mode & VREAD)
414 mask |= S_IRGRP;
415 if (mode & VWRITE)
416 mask |= S_IWGRP;
417 return ((cp->c_mode & mask) == mask ? 0 : EACCES);
418 }
419 }
420
421 /* Otherwise, check everyone else. */
422 if (mode & VEXEC)
423 mask |= S_IXOTH;
424 if (mode & VREAD)
425 mask |= S_IROTH;
426 if (mode & VWRITE)
427 mask |= S_IWOTH;
428 return ((cp->c_mode & mask) == mask ? 0 : EACCES);
429 }
430
431
432
433 /*
434 #% getattr vp = = =
435 #
436 vop_getattr {
437 IN struct vnode *vp;
438 IN struct vattr *vap;
439 IN struct ucred *cred;
440 IN struct proc *p;
441
442 */
443
444
445 /* ARGSUSED */
446 static int
447 hfs_getattr(ap)
448 struct vop_getattr_args /* {
449 struct vnode *a_vp;
450 struct vattr *a_vap;
451 struct ucred *a_cred;
452 struct proc *a_p;
453 } */ *ap;
454 {
455 struct vnode *vp = ap->a_vp;
456 struct cnode *cp = VTOC(vp);
457 struct vattr *vap = ap->a_vap;
458 struct timeval tv;
459
460 tv = time;
461 CTIMES(cp, &tv, &tv);
462
463 vap->va_type = vp->v_type;
464 /*
465 * [2856576] Since we are dynamically changing the owner, also
466 * effectively turn off the set-user-id and set-group-id bits,
467 * just like chmod(2) would when changing ownership. This prevents
468 * a security hole where set-user-id programs run as whoever is
469 * logged on (or root if nobody is logged in yet!)
470 */
471 vap->va_mode = (cp->c_uid == UNKNOWNUID) ? cp->c_mode & ~(S_ISUID | S_ISGID) : cp->c_mode;
472 vap->va_nlink = cp->c_nlink;
473 vap->va_uid = (cp->c_uid == UNKNOWNUID) ? console_user : cp->c_uid;
474 vap->va_gid = cp->c_gid;
475 vap->va_fsid = cp->c_dev;
476 /*
477 * Exporting file IDs from HFS Plus:
478 *
479 * For "normal" files the c_fileid is the same value as the
480 * c_cnid. But for hard link files, they are different - the
481 * c_cnid belongs to the active directory entry (ie the link)
482 * and the c_fileid is for the actual inode (ie the data file).
483 *
484 * The stat call (getattr) will always return the c_fileid
485 * and Carbon APIs, which are hardlink-ignorant, will always
486 * receive the c_cnid (from getattrlist).
487 */
488 vap->va_fileid = cp->c_fileid;
489 vap->va_atime.tv_sec = cp->c_atime;
490 vap->va_atime.tv_nsec = 0;
491 vap->va_mtime.tv_sec = cp->c_mtime;
492 vap->va_mtime.tv_nsec = cp->c_mtime_nsec;
493 vap->va_ctime.tv_sec = cp->c_ctime;
494 vap->va_ctime.tv_nsec = 0;
495 vap->va_gen = 0;
496 vap->va_flags = cp->c_flags;
497 vap->va_rdev = 0;
498 vap->va_blocksize = VTOVFS(vp)->mnt_stat.f_iosize;
499 vap->va_filerev = 0;
500 vap->va_spare = 0;
501 if (vp->v_type == VDIR) {
502 vap->va_size = cp->c_nlink * AVERAGE_HFSDIRENTRY_SIZE;
503 vap->va_bytes = 0;
504 } else {
505 vap->va_size = VTOF(vp)->ff_size;
506 vap->va_bytes = (u_quad_t)cp->c_blocks *
507 (u_quad_t)VTOVCB(vp)->blockSize;
508 if (vp->v_type == VBLK || vp->v_type == VCHR)
509 vap->va_rdev = cp->c_rdev;
510 }
511 return (0);
512 }
513
514 /*
515 * Set attribute vnode op. called from several syscalls
516 #% setattr vp L L L
517 #
518 vop_setattr {
519 IN struct vnode *vp;
520 IN struct vattr *vap;
521 IN struct ucred *cred;
522 IN struct proc *p;
523
524 */
525
526 static int
527 hfs_setattr(ap)
528 struct vop_setattr_args /* {
529 struct vnode *a_vp;
530 struct vattr *a_vap;
531 struct ucred *a_cred;
532 struct proc *a_p;
533 } */ *ap;
534 {
535 struct vattr *vap = ap->a_vap;
536 struct vnode *vp = ap->a_vp;
537 struct cnode *cp = VTOC(vp);
538 struct ucred *cred = ap->a_cred;
539 struct proc *p = ap->a_p;
540 struct timeval atimeval, mtimeval;
541 int error;
542
543 /*
544 * Check for unsettable attributes.
545 */
546 if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
547 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
548 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
549 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
550 return (EINVAL);
551 }
552
553 if (vap->va_flags != VNOVAL) {
554 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
555 return (EROFS);
556 if ((error = hfs_chflags(vp, vap->va_flags, cred, p)))
557 return (error);
558 if (vap->va_flags & (IMMUTABLE | APPEND))
559 return (0);
560 }
561
562 if (cp->c_flags & (IMMUTABLE | APPEND))
563 return (EPERM);
564 /*
565 * Go through the fields and update iff not VNOVAL.
566 */
567 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
568 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
569 return (EROFS);
570 if ((error = hfs_chown(vp, vap->va_uid, vap->va_gid, cred, p)))
571 return (error);
572 }
573 if (vap->va_size != VNOVAL) {
574 /*
575 * Disallow write attempts on read-only file systems;
576 * unless the file is a socket, fifo, or a block or
577 * character device resident on the file system.
578 */
579 switch (vp->v_type) {
580 case VDIR:
581 return (EISDIR);
582 case VLNK:
583 case VREG:
584 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
585 return (EROFS);
586 break;
587 default:
588 break;
589 }
590 if ((error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p)))
591 return (error);
592 }
593 cp = VTOC(vp);
594 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
595 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
596 return (EROFS);
597 if (((error = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, true)) != 0) &&
598 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
599 (error = VOP_ACCESS(vp, VWRITE, cred, p)))) {
600 return (error);
601 }
602 if (vap->va_atime.tv_sec != VNOVAL)
603 cp->c_flag |= C_ACCESS;
604 if (vap->va_mtime.tv_sec != VNOVAL) {
605 cp->c_flag |= C_CHANGE | C_UPDATE;
606 /*
607 * The utimes system call can reset the modification
608 * time but it doesn't know about HFS create times.
609 * So we need to insure that the creation time is
610 * always at least as old as the modification time.
611 */
612 if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) &&
613 (cp->c_cnid != kRootDirID) &&
614 (vap->va_mtime.tv_sec < cp->c_itime)) {
615 cp->c_itime = vap->va_mtime.tv_sec;
616 }
617 }
618 atimeval.tv_sec = vap->va_atime.tv_sec;
619 atimeval.tv_usec = 0;
620 mtimeval.tv_sec = vap->va_mtime.tv_sec;
621 mtimeval.tv_usec = 0;
622 if ((error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1)))
623 return (error);
624 }
625 error = 0;
626 if (vap->va_mode != (mode_t)VNOVAL) {
627 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
628 return (EROFS);
629 error = hfs_chmod(vp, (int)vap->va_mode, cred, p);
630 }
631 return (error);
632 }
633
634
635 /*
636 * Change the mode on a file.
637 * cnode must be locked before calling.
638 */
639 int
640 hfs_chmod(vp, mode, cred, p)
641 register struct vnode *vp;
642 register int mode;
643 register struct ucred *cred;
644 struct proc *p;
645 {
646 register struct cnode *cp = VTOC(vp);
647 int error;
648
649 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
650 return (0);
651
652 #if OVERRIDE_UNKNOWN_PERMISSIONS
653 if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
654 return (0);
655 };
656 #endif
657 if ((error = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, true)) != 0)
658 return (error);
659 if (cred->cr_uid) {
660 if (vp->v_type != VDIR && (mode & S_ISTXT))
661 return (EFTYPE);
662 if (!groupmember(cp->c_gid, cred) && (mode & S_ISGID))
663 return (EPERM);
664 }
665 cp->c_mode &= ~ALLPERMS;
666 cp->c_mode |= (mode & ALLPERMS);
667 cp->c_flag |= C_CHANGE;
668 return (0);
669 }
670
671
672 int
673 hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags)
674 {
675 struct cnode *cp = VTOC(vp);
676 gid_t *gp;
677 int retval = 0;
678 int i;
679
680 /*
681 * Disallow write attempts on read-only file systems;
682 * unless the file is a socket, fifo, or a block or
683 * character device resident on the file system.
684 */
685 switch (vp->v_type) {
686 case VDIR:
687 case VLNK:
688 case VREG:
689 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
690 return (EROFS);
691 break;
692 default:
693 break;
694 }
695
696 /* If immutable bit set, nobody gets to write it. */
697 if (considerFlags && (cp->c_flags & IMMUTABLE))
698 return (EPERM);
699
700 /* Otherwise, user id 0 always gets access. */
701 if (cred->cr_uid == 0)
702 return (0);
703
704 /* Otherwise, check the owner. */
705 if ((retval = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, false)) == 0)
706 return ((cp->c_mode & S_IWUSR) == S_IWUSR ? 0 : EACCES);
707
708 /* Otherwise, check the groups. */
709 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) {
710 if (cp->c_gid == *gp)
711 return ((cp->c_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES);
712 }
713
714 /* Otherwise, check everyone else. */
715 return ((cp->c_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES);
716 }
717
718
719
720 /*
721 * Change the flags on a file or directory.
722 * cnode must be locked before calling.
723 */
724 int
725 hfs_chflags(vp, flags, cred, p)
726 register struct vnode *vp;
727 register u_long flags;
728 register struct ucred *cred;
729 struct proc *p;
730 {
731 register struct cnode *cp = VTOC(vp);
732 int retval;
733
734 if (VTOVCB(vp)->vcbSigWord == kHFSSigWord) {
735 if ((retval = hfs_write_access(vp, cred, p, false)) != 0) {
736 return retval;
737 };
738 } else if ((retval = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, true)) != 0) {
739 return retval;
740 };
741
742 if (cred->cr_uid == 0) {
743 if ((cp->c_flags & (SF_IMMUTABLE | SF_APPEND)) &&
744 securelevel > 0) {
745 return EPERM;
746 };
747 cp->c_flags = flags;
748 } else {
749 if (cp->c_flags & (SF_IMMUTABLE | SF_APPEND) ||
750 (flags & UF_SETTABLE) != flags) {
751 return EPERM;
752 };
753 cp->c_flags &= SF_SETTABLE;
754 cp->c_flags |= (flags & UF_SETTABLE);
755 }
756 cp->c_flag |= C_CHANGE;
757
758 return (0);
759 }
760
761
762 /*
763 * Perform chown operation on cnode cp;
764 * code must be locked prior to call.
765 */
766 int
767 hfs_chown(vp, uid, gid, cred, p)
768 register struct vnode *vp;
769 uid_t uid;
770 gid_t gid;
771 struct ucred *cred;
772 struct proc *p;
773 {
774 register struct cnode *cp = VTOC(vp);
775 uid_t ouid;
776 gid_t ogid;
777 int error = 0;
778 #if QUOTA
779 register int i;
780 int64_t change;
781 #endif /* QUOTA */
782
783 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
784 return (EOPNOTSUPP);
785
786 if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS)
787 return (0);
788
789 if (uid == (uid_t)VNOVAL)
790 uid = cp->c_uid;
791 if (gid == (gid_t)VNOVAL)
792 gid = cp->c_gid;
793 /*
794 * If we don't own the file, are trying to change the owner
795 * of the file, or are not a member of the target group,
796 * the caller must be superuser or the call fails.
797 */
798 if ((cred->cr_uid != cp->c_uid || uid != cp->c_uid ||
799 (gid != cp->c_gid && !groupmember((gid_t)gid, cred))) &&
800 (error = suser(cred, &p->p_acflag)))
801 return (error);
802
803 ogid = cp->c_gid;
804 ouid = cp->c_uid;
805 #if QUOTA
806 if ((error = hfs_getinoquota(cp)))
807 return (error);
808 if (ouid == uid) {
809 dqrele(vp, cp->c_dquot[USRQUOTA]);
810 cp->c_dquot[USRQUOTA] = NODQUOT;
811 }
812 if (ogid == gid) {
813 dqrele(vp, cp->c_dquot[GRPQUOTA]);
814 cp->c_dquot[GRPQUOTA] = NODQUOT;
815 }
816
817 /*
818 * Eventually need to account for (fake) a block per directory
819 *if (vp->v_type == VDIR)
820 *change = VTOVCB(vp)->blockSize;
821 *else
822 */
823
824 change = (int64_t)(cp->c_blocks) * (int64_t)VTOVCB(vp)->blockSize;
825 (void) hfs_chkdq(cp, -change, cred, CHOWN);
826 (void) hfs_chkiq(cp, -1, cred, CHOWN);
827 for (i = 0; i < MAXQUOTAS; i++) {
828 dqrele(vp, cp->c_dquot[i]);
829 cp->c_dquot[i] = NODQUOT;
830 }
831 #endif /* QUOTA */
832 cp->c_gid = gid;
833 cp->c_uid = uid;
834 #if QUOTA
835 if ((error = hfs_getinoquota(cp)) == 0) {
836 if (ouid == uid) {
837 dqrele(vp, cp->c_dquot[USRQUOTA]);
838 cp->c_dquot[USRQUOTA] = NODQUOT;
839 }
840 if (ogid == gid) {
841 dqrele(vp, cp->c_dquot[GRPQUOTA]);
842 cp->c_dquot[GRPQUOTA] = NODQUOT;
843 }
844 if ((error = hfs_chkdq(cp, change, cred, CHOWN)) == 0) {
845 if ((error = hfs_chkiq(cp, 1, cred, CHOWN)) == 0)
846 goto good;
847 else
848 (void) hfs_chkdq(cp, -change, cred, CHOWN|FORCE);
849 }
850 for (i = 0; i < MAXQUOTAS; i++) {
851 dqrele(vp, cp->c_dquot[i]);
852 cp->c_dquot[i] = NODQUOT;
853 }
854 }
855 cp->c_gid = ogid;
856 cp->c_uid = ouid;
857 if (hfs_getinoquota(cp) == 0) {
858 if (ouid == uid) {
859 dqrele(vp, cp->c_dquot[USRQUOTA]);
860 cp->c_dquot[USRQUOTA] = NODQUOT;
861 }
862 if (ogid == gid) {
863 dqrele(vp, cp->c_dquot[GRPQUOTA]);
864 cp->c_dquot[GRPQUOTA] = NODQUOT;
865 }
866 (void) hfs_chkdq(cp, change, cred, FORCE|CHOWN);
867 (void) hfs_chkiq(cp, 1, cred, FORCE|CHOWN);
868 (void) hfs_getinoquota(cp);
869 }
870 return (error);
871 good:
872 if (hfs_getinoquota(cp))
873 panic("hfs_chown: lost quota");
874 #endif /* QUOTA */
875
876 if (ouid != uid || ogid != gid)
877 cp->c_flag |= C_CHANGE;
878 if (ouid != uid && cred->cr_uid != 0)
879 cp->c_mode &= ~S_ISUID;
880 if (ogid != gid && cred->cr_uid != 0)
881 cp->c_mode &= ~S_ISGID;
882 return (0);
883 }
884
885
886 /*
887 #
888 #% exchange fvp L L L
889 #% exchange tvp L L L
890 #
891 */
892 /*
893 * The hfs_exchange routine swaps the fork data in two files by
894 * exchanging some of the information in the cnode. It is used
895 * to preserve the file ID when updating an existing file, in
896 * case the file is being tracked through its file ID. Typically
897 * its used after creating a new file during a safe-save.
898 */
899
900 static int
901 hfs_exchange(ap)
902 struct vop_exchange_args /* {
903 struct vnode *a_fvp;
904 struct vnode *a_tvp;
905 struct ucred *a_cred;
906 struct proc *a_p;
907 } */ *ap;
908 {
909 struct vnode *from_vp = ap->a_fvp;
910 struct vnode *to_vp = ap->a_tvp;
911 struct vnode *from_rvp = NULL;
912 struct vnode *to_rvp = NULL;
913 struct cnode *from_cp = VTOC(from_vp);
914 struct cnode *to_cp = VTOC(to_vp);
915 struct hfsmount *hfsmp = VTOHFS(from_vp);
916 struct cat_desc tempdesc;
917 struct cat_attr tempattr;
918 int error = 0;
919
920 /* The files must be on the same volume. */
921 if (from_vp->v_mount != to_vp->v_mount)
922 return (EXDEV);
923
924 /* Only normal files can be exchanged. */
925 if ((from_vp->v_type != VREG) || (to_vp->v_type != VREG) ||
926 (from_cp->c_flag & C_HARDLINK) || (to_cp->c_flag & C_HARDLINK) ||
927 VNODE_IS_RSRC(from_vp) || VNODE_IS_RSRC(to_vp))
928 return (EINVAL);
929
930 from_rvp = from_cp->c_rsrc_vp;
931 to_rvp = to_cp->c_rsrc_vp;
932
933 /* If one of the resource forks is open then get the other one. */
934 if (from_rvp || to_rvp) {
935 error = hfs_vgetrsrc(hfsmp, from_vp, &from_rvp, ap->a_p);
936 if (error)
937 return (error);
938 error = hfs_vgetrsrc(hfsmp, to_vp, &to_rvp, ap->a_p);
939 if (error) {
940 vrele(from_rvp);
941 return (error);
942 }
943 }
944
945 /* Ignore any errors, we are doing a 'best effort' on flushing */
946 if (from_vp)
947 (void) vinvalbuf(from_vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0);
948 if (to_vp)
949 (void) vinvalbuf(to_vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0);
950 if (from_rvp)
951 (void) vinvalbuf(from_rvp, V_SAVE, ap->a_cred, ap->a_p, 0, 0);
952 if (to_rvp)
953 (void) vinvalbuf(to_rvp, V_SAVE, ap->a_cred, ap->a_p, 0, 0);
954
955 /* Lock catalog b-tree */
956 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, ap->a_p);
957 if (error) goto Err_Exit;
958
959 /* The backend code always tries to delete the virtual
960 * extent id for exchanging files so we neeed to lock
961 * the extents b-tree.
962 */
963 error = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
964 if (error) {
965 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, ap->a_p);
966 goto Err_Exit;
967 }
968
969 /* Do the exchange */
970 error = MacToVFSError(ExchangeFileIDs(HFSTOVCB(hfsmp),
971 from_cp->c_desc.cd_nameptr, to_cp->c_desc.cd_nameptr,
972 from_cp->c_parentcnid, to_cp->c_parentcnid,
973 from_cp->c_hint, to_cp->c_hint));
974
975 (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, ap->a_p);
976 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, ap->a_p);
977
978 if (error != E_NONE) {
979 goto Err_Exit;
980 }
981
982 /* Purge the vnodes from the name cache */
983 if (from_vp)
984 cache_purge(from_vp);
985 if (to_vp)
986 cache_purge(to_vp);
987
988 /* Save a copy of from attributes before swapping. */
989 bcopy(&from_cp->c_desc, &tempdesc, sizeof(struct cat_desc));
990 bcopy(&from_cp->c_attr, &tempattr, sizeof(struct cat_attr));
991
992 /*
993 * Swap the descriptors and all non-fork related attributes.
994 * (except the modify date)
995 */
996 bcopy(&to_cp->c_desc, &from_cp->c_desc, sizeof(struct cat_desc));
997 from_cp->c_hint = 0;
998 from_cp->c_fileid = from_cp->c_cnid;
999 from_cp->c_itime = to_cp->c_itime;
1000 from_cp->c_btime = to_cp->c_btime;
1001 from_cp->c_atime = to_cp->c_atime;
1002 from_cp->c_ctime = to_cp->c_ctime;
1003 from_cp->c_gid = to_cp->c_gid;
1004 from_cp->c_uid = to_cp->c_uid;
1005 from_cp->c_flags = to_cp->c_flags;
1006 from_cp->c_mode = to_cp->c_mode;
1007 bcopy(to_cp->c_finderinfo, from_cp->c_finderinfo, 32);
1008
1009 bcopy(&tempdesc, &to_cp->c_desc, sizeof(struct cat_desc));
1010 to_cp->c_hint = 0;
1011 to_cp->c_fileid = to_cp->c_cnid;
1012 to_cp->c_itime = tempattr.ca_itime;
1013 to_cp->c_btime = tempattr.ca_btime;
1014 to_cp->c_atime = tempattr.ca_atime;
1015 to_cp->c_ctime = tempattr.ca_ctime;
1016 to_cp->c_gid = tempattr.ca_gid;
1017 to_cp->c_uid = tempattr.ca_uid;
1018 to_cp->c_flags = tempattr.ca_flags;
1019 to_cp->c_mode = tempattr.ca_mode;
1020 bcopy(tempattr.ca_finderinfo, to_cp->c_finderinfo, 32);
1021
1022 /* Reinsert into the cnode hash under new file IDs*/
1023 hfs_chashremove(from_cp);
1024 hfs_chashremove(to_cp);
1025
1026 hfs_chashinsert(from_cp);
1027 hfs_chashinsert(to_cp);
1028 Err_Exit:
1029 if (to_rvp)
1030 vrele(to_rvp);
1031 if (from_rvp)
1032 vrele(from_rvp);
1033
1034 return (error);
1035 }
1036
1037
1038 /*
1039
1040 #% fsync vp L L L
1041 #
1042 vop_fsync {
1043 IN struct vnode *vp;
1044 IN struct ucred *cred;
1045 IN int waitfor;
1046 IN struct proc *p;
1047
1048 */
1049
1050 static int
1051 hfs_fsync(ap)
1052 struct vop_fsync_args /* {
1053 struct vnode *a_vp;
1054 struct ucred *a_cred;
1055 int a_waitfor;
1056 struct proc *a_p;
1057 } */ *ap;
1058 {
1059 struct vnode *vp = ap->a_vp;
1060 struct cnode *cp = VTOC(vp);
1061 struct filefork *fp = NULL;
1062 int retval = 0;
1063 register struct buf *bp;
1064 struct timeval tv;
1065 struct buf *nbp;
1066 int s;
1067 int wait;
1068 int retry = 0;
1069
1070 wait = (ap->a_waitfor == MNT_WAIT);
1071
1072 /* HFS directories don't have any data blocks. */
1073 if (vp->v_type == VDIR)
1074 goto metasync;
1075
1076 /*
1077 * For system files flush the B-tree header and
1078 * for regular files write out any clusters
1079 */
1080 if (vp->v_flag & VSYSTEM) {
1081 if (VTOF(vp)->fcbBTCBPtr != NULL)
1082 BTFlushPath(VTOF(vp));
1083 } else if (UBCINFOEXISTS(vp))
1084 (void) cluster_push(vp);
1085
1086 /*
1087 * When MNT_WAIT is requested and the zero fill timeout
1088 * has expired then we must explicitly zero out any areas
1089 * that are currently marked invalid (holes).
1090 */
1091 if ((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
1092 UBCINFOEXISTS(vp) && (fp = VTOF(vp)) &&
1093 cp->c_zftimeout != 0) {
1094 int devblksize;
1095 int was_nocache;
1096
1097 if (time.tv_sec < cp->c_zftimeout) {
1098 /* Remember that a force sync was requested. */
1099 cp->c_flag |= C_ZFWANTSYNC;
1100 goto loop;
1101 }
1102 VOP_DEVBLOCKSIZE(cp->c_devvp, &devblksize);
1103 was_nocache = ISSET(vp->v_flag, VNOCACHE_DATA);
1104 SET(vp->v_flag, VNOCACHE_DATA); /* Don't cache zeros */
1105
1106 while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) {
1107 struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges);
1108 off_t start = invalid_range->rl_start;
1109 off_t end = invalid_range->rl_end;
1110
1111 /* The range about to be written must be validated
1112 * first, so that VOP_CMAP() will return the
1113 * appropriate mapping for the cluster code:
1114 */
1115 rl_remove(start, end, &fp->ff_invalidranges);
1116
1117 (void) cluster_write(vp, (struct uio *) 0,
1118 fp->ff_size,
1119 invalid_range->rl_end + 1,
1120 invalid_range->rl_start,
1121 (off_t)0, devblksize,
1122 IO_HEADZEROFILL | IO_NOZERODIRTY);
1123 cp->c_flag |= C_MODIFIED;
1124 }
1125 (void) cluster_push(vp);
1126 if (!was_nocache)
1127 CLR(vp->v_flag, VNOCACHE_DATA);
1128 cp->c_flag &= ~C_ZFWANTSYNC;
1129 cp->c_zftimeout = 0;
1130 }
1131
1132 /*
1133 * Flush all dirty buffers associated with a vnode.
1134 */
1135 loop:
1136 s = splbio();
1137 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
1138 nbp = bp->b_vnbufs.le_next;
1139 if ((bp->b_flags & B_BUSY))
1140 continue;
1141 if ((bp->b_flags & B_DELWRI) == 0)
1142 panic("hfs_fsync: not dirty");
1143 bremfree(bp);
1144 bp->b_flags |= B_BUSY;
1145 /* Clear B_LOCKED, should only be set on meta files */
1146 bp->b_flags &= ~B_LOCKED;
1147 splx(s);
1148 /*
1149 * Wait for I/O associated with indirect blocks to complete,
1150 * since there is no way to quickly wait for them below.
1151 */
1152 if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT)
1153 (void) bawrite(bp);
1154 else
1155 (void) VOP_BWRITE(bp);
1156 goto loop;
1157 }
1158
1159 if (wait) {
1160 while (vp->v_numoutput) {
1161 vp->v_flag |= VBWAIT;
1162 tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "hfs_fsync", 0);
1163 }
1164
1165 if (vp->v_dirtyblkhd.lh_first) {
1166 /* still have some dirty buffers */
1167 if (retry++ > 10) {
1168 vprint("hfs_fsync: dirty", vp);
1169 splx(s);
1170 /*
1171 * Looks like the requests are not
1172 * getting queued to the driver.
1173 * Retrying here causes a cpu bound loop.
1174 * Yield to the other threads and hope
1175 * for the best.
1176 */
1177 (void)tsleep((caddr_t)&vp->v_numoutput,
1178 PRIBIO + 1, "hfs_fsync", hz/10);
1179 retry = 0;
1180 } else {
1181 splx(s);
1182 }
1183 /* try again */
1184 goto loop;
1185 }
1186 }
1187 splx(s);
1188
1189 metasync:
1190 tv = time;
1191 if (vp->v_flag & VSYSTEM) {
1192 if (VTOF(vp)->fcbBTCBPtr != NULL)
1193 BTSetLastSync(VTOF(vp), tv.tv_sec);
1194 cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE);
1195 } else /* User file */ {
1196 retval = VOP_UPDATE(ap->a_vp, &tv, &tv, wait);
1197
1198 /* When MNT_WAIT is requested push out any delayed meta data */
1199 if ((retval == 0) && wait && cp->c_hint &&
1200 !ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
1201 hfs_metasync(VTOHFS(vp), cp->c_hint, ap->a_p);
1202 }
1203 }
1204
1205 return (retval);
1206 }
1207
1208 /* Sync an hfs catalog b-tree node */
1209 static int
1210 hfs_metasync(struct hfsmount *hfsmp, daddr_t node, struct proc *p)
1211 {
1212 struct vnode *vp;
1213 struct buf *bp;
1214 struct buf *nbp;
1215 int s;
1216
1217 vp = HFSTOVCB(hfsmp)->catalogRefNum;
1218
1219 if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p) != 0)
1220 return (0);
1221
1222 /*
1223 * Look for a matching node that has been delayed
1224 * but is not part of a set (B_LOCKED).
1225 */
1226 s = splbio();
1227 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
1228 nbp = bp->b_vnbufs.le_next;
1229 if (bp->b_flags & B_BUSY)
1230 continue;
1231 if (bp->b_lblkno == node) {
1232 if (bp->b_flags & B_LOCKED)
1233 break;
1234
1235 bremfree(bp);
1236 bp->b_flags |= B_BUSY;
1237 splx(s);
1238 (void) VOP_BWRITE(bp);
1239 goto exit;
1240 }
1241 }
1242 splx(s);
1243 exit:
1244 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1245
1246 return (0);
1247 }
1248
1249 __private_extern__
1250 int
1251 hfs_btsync(struct vnode *vp, int sync_transaction)
1252 {
1253 struct cnode *cp = VTOC(vp);
1254 register struct buf *bp;
1255 struct timeval tv;
1256 struct buf *nbp;
1257 int s;
1258
1259 /*
1260 * Flush all dirty buffers associated with b-tree.
1261 */
1262 loop:
1263 s = splbio();
1264
1265 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
1266 nbp = bp->b_vnbufs.le_next;
1267 if ((bp->b_flags & B_BUSY))
1268 continue;
1269 if ((bp->b_flags & B_DELWRI) == 0)
1270 panic("hfs_fsync: not dirty");
1271 if (sync_transaction && !(bp->b_flags & B_LOCKED))
1272 continue;
1273
1274 bremfree(bp);
1275 bp->b_flags |= B_BUSY;
1276 bp->b_flags &= ~B_LOCKED;
1277 splx(s);
1278
1279 (void) bawrite(bp);
1280
1281 goto loop;
1282 }
1283 splx(s);
1284
1285 tv = time;
1286 if ((vp->v_flag & VSYSTEM) && (VTOF(vp)->fcbBTCBPtr != NULL))
1287 (void) BTSetLastSync(VTOF(vp), tv.tv_sec);
1288 cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE);
1289
1290 return 0;
1291 }
1292
1293 /*
1294 * Rmdir system call.
1295 #% rmdir dvp L U U
1296 #% rmdir vp L U U
1297 #
1298 vop_rmdir {
1299 IN WILLRELE struct vnode *dvp;
1300 IN WILLRELE struct vnode *vp;
1301 IN struct componentname *cnp;
1302
1303 */
1304 static int
1305 hfs_rmdir(ap)
1306 struct vop_rmdir_args /* {
1307 struct vnode *a_dvp;
1308 struct vnode *a_vp;
1309 struct componentname *a_cnp;
1310 } */ *ap;
1311 {
1312 struct vnode *vp = ap->a_vp;
1313 struct vnode *dvp = ap->a_dvp;
1314 struct proc *p = ap->a_cnp->cn_proc;
1315 struct cnode *cp;
1316 struct cnode *dcp;
1317 struct hfsmount * hfsmp;
1318 struct timeval tv;
1319 int error = 0;
1320
1321 cp = VTOC(vp);
1322 dcp = VTOC(dvp);
1323 hfsmp = VTOHFS(vp);
1324
1325 if (dcp == cp) {
1326 vrele(dvp);
1327 vput(vp);
1328 return (EINVAL); /* cannot remove "." */
1329 }
1330 /*
1331 * Verify the directory is empty (and valid).
1332 * (Rmdir ".." won't be valid since
1333 * ".." will contain a reference to
1334 * the current directory and thus be
1335 * non-empty.)
1336 */
1337 if (cp->c_entries != 0) {
1338 error = ENOTEMPTY;
1339 goto out;
1340 }
1341 if ((dcp->c_flags & APPEND) || (cp->c_flags & (IMMUTABLE | APPEND))) {
1342 error = EPERM;
1343 goto out;
1344 }
1345
1346 /* Remove the entry from the namei cache: */
1347 cache_purge(vp);
1348
1349 /* Lock catalog b-tree */
1350 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1351 if (error) goto out;
1352
1353 if (cp->c_entries > 0)
1354 panic("hfs_rmdir: attempting to delete a non-empty directory!");
1355 /* Remove entry from catalog */
1356 error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
1357
1358 /* Unlock catalog b-tree */
1359 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1360 if (error) goto out;
1361
1362 #if QUOTA
1363 if (!hfs_getinoquota(cp))
1364 (void)hfs_chkiq(cp, -1, NOCRED, 0);
1365 #endif /* QUOTA */
1366
1367 /* The parent lost a child */
1368 if (dcp->c_entries > 0)
1369 dcp->c_entries--;
1370 if (dcp->c_nlink > 0)
1371 dcp->c_nlink--;
1372 dcp->c_flag |= C_CHANGE | C_UPDATE;
1373 tv = time;
1374 (void) VOP_UPDATE(dvp, &tv, &tv, 0);
1375 hfs_volupdate(hfsmp, VOL_RMDIR, (dcp->c_cnid == kHFSRootFolderID));
1376
1377 cp->c_mode = 0; /* Makes the vnode go away...see inactive */
1378 cp->c_flag |= C_NOEXISTS;
1379 out:
1380 if (dvp)
1381 vput(dvp);
1382 vput(vp);
1383 return (error);
1384 }
1385
1386 /*
1387
1388 #% remove dvp L U U
1389 #% remove vp L U U
1390 #
1391 vop_remove {
1392 IN WILLRELE struct vnode *dvp;
1393 IN WILLRELE struct vnode *vp;
1394 IN struct componentname *cnp;
1395
1396 */
1397
1398 static int
1399 hfs_remove(ap)
1400 struct vop_remove_args /* {
1401 struct vnode *a_dvp;
1402 struct vnode *a_vp;
1403 struct componentname *a_cnp;
1404 } */ *ap;
1405 {
1406 struct vnode *vp = ap->a_vp;
1407 struct vnode *dvp = ap->a_dvp;
1408 struct vnode *rvp = NULL;
1409 struct cnode *cp;
1410 struct cnode *dcp;
1411 struct hfsmount *hfsmp;
1412 struct proc *p = current_proc();
1413 int dataforkbusy = 0;
1414 int rsrcforkbusy = 0;
1415 int truncated = 0;
1416 struct timeval tv;
1417 int error = 0;
1418
1419 /* Redirect directories to rmdir */
1420 if (vp->v_type == VDIR)
1421 return (hfs_rmdir(ap));
1422
1423 cp = VTOC(vp);
1424 dcp = VTOC(dvp);
1425 hfsmp = VTOHFS(vp);
1426
1427 if (cp->c_parentcnid != dcp->c_cnid) {
1428 error = EINVAL;
1429 goto out;
1430 }
1431
1432 /* Make sure a remove is permitted */
1433 if ((cp->c_flags & (IMMUTABLE | APPEND)) ||
1434 (VTOC(dvp)->c_flags & APPEND) ||
1435 VNODE_IS_RSRC(vp)) {
1436 error = EPERM;
1437 goto out;
1438 }
1439
1440 /*
1441 * Aquire a vnode for a non-empty resource fork.
1442 * (needed for VOP_TRUNCATE)
1443 */
1444 if (cp->c_blocks - VTOF(vp)->ff_blocks) {
1445 error = hfs_vgetrsrc(hfsmp, vp, &rvp, p);
1446 if (error)
1447 goto out;
1448 }
1449
1450 /*
1451 * Check if this file is being used.
1452 *
1453 * The namei done for the remove took a reference on the
1454 * vnode (vp). And we took a ref on the resource vnode (rvp).
1455 * Hence set 1 in the tookref parameter of ubc_isinuse().
1456 */
1457 if (UBCISVALID(vp) && ubc_isinuse(vp, 1))
1458 dataforkbusy = 1;
1459 if (rvp && UBCISVALID(rvp) && ubc_isinuse(rvp, 1))
1460 rsrcforkbusy = 1;
1461
1462 /*
1463 * Carbon semantics prohibit deleting busy files.
1464 * (enforced when NODELETEBUSY is requested)
1465 */
1466 if ((dataforkbusy || rsrcforkbusy) &&
1467 ((ap->a_cnp->cn_flags & NODELETEBUSY) ||
1468 (hfsmp->hfs_private_metadata_dir == 0))) {
1469 error = EBUSY;
1470 goto out;
1471 }
1472
1473 /* Remove our entry from the namei cache. */
1474 cache_purge(vp);
1475
1476 /*
1477 * Truncate any non-busy forks. Busy forks will
1478 * get trucated when their vnode goes inactive.
1479 *
1480 * (Note: hard links are truncated in VOP_INACTIVE)
1481 */
1482 if ((cp->c_flag & C_HARDLINK) == 0) {
1483 int mode = cp->c_mode;
1484
1485 if (!dataforkbusy && cp->c_datafork->ff_blocks != 0) {
1486 cp->c_mode = 0; /* Suppress VOP_UPDATES */
1487 error = VOP_TRUNCATE(vp, (off_t)0, IO_NDELAY, NOCRED, p);
1488 cp->c_mode = mode;
1489 if (error)
1490 goto out;
1491 truncated = 1;
1492 }
1493 if (!rsrcforkbusy && rvp) {
1494 cp->c_mode = 0; /* Suppress VOP_UPDATES */
1495 error = VOP_TRUNCATE(rvp, (off_t)0, IO_NDELAY, NOCRED, p);
1496 cp->c_mode = mode;
1497 if (error && !dataforkbusy)
1498 goto out;
1499 else {
1500 /*
1501 * XXX could also force an update on vp
1502 * and fail the remove.
1503 */
1504 error = 0;
1505 }
1506 truncated = 1;
1507 }
1508 }
1509 /*
1510 * There are 3 remove cases to consider:
1511 * 1. File is a hardlink ==> remove the link
1512 * 2. File is busy (in use) ==> move/rename the file
1513 * 3. File is not in use ==> remove the file
1514 */
1515
1516 if (cp->c_flag & C_HARDLINK) {
1517 struct cat_desc desc;
1518
1519 if ((ap->a_cnp->cn_flags & HASBUF) == 0 ||
1520 ap->a_cnp->cn_nameptr[0] == '\0') {
1521 error = ENOENT; /* name missing! */
1522 goto out;
1523 }
1524
1525 /* Setup a descriptor for the link */
1526 bzero(&desc, sizeof(desc));
1527 desc.cd_nameptr = ap->a_cnp->cn_nameptr;
1528 desc.cd_namelen = ap->a_cnp->cn_namelen;
1529 desc.cd_parentcnid = dcp->c_cnid;
1530 /* XXX - if cnid is out of sync then the wrong thread rec will get deleted. */
1531 desc.cd_cnid = cp->c_cnid;
1532
1533 /* Lock catalog b-tree */
1534 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1535 if (error)
1536 goto out;
1537
1538 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1539
1540 /* Unlock the Catalog */
1541 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1542
1543 /* All done with component name... */
1544 if ((ap->a_cnp->cn_flags & (HASBUF | SAVENAME)) == (HASBUF | SAVENAME))
1545 FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
1546
1547 if (error != 0)
1548 goto out;
1549
1550 cp->c_flag |= C_CHANGE;
1551 if (--cp->c_nlink < 1)
1552 cp->c_flag |= C_DELETED;
1553 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
1554
1555 } else if (dataforkbusy || rsrcforkbusy) {
1556 char delname[32];
1557 struct cat_desc to_desc;
1558 struct cat_desc todir_desc;
1559
1560 /*
1561 * Orphan this file (move to hidden directory).
1562 */
1563 bzero(&todir_desc, sizeof(todir_desc));
1564 todir_desc.cd_parentcnid = 2;
1565
1566 MAKE_DELETED_NAME(delname, cp->c_fileid);
1567 bzero(&to_desc, sizeof(to_desc));
1568 to_desc.cd_nameptr = delname;
1569 to_desc.cd_namelen = strlen(delname);
1570 to_desc.cd_parentcnid = hfsmp->hfs_private_metadata_dir;
1571 to_desc.cd_flags = 0;
1572 to_desc.cd_cnid = cp->c_cnid;
1573
1574 /* Lock catalog b-tree */
1575 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1576 if (error) goto out;
1577
1578 error = cat_rename(hfsmp, &cp->c_desc, &todir_desc,
1579 &to_desc, (struct cat_desc *)NULL);
1580
1581 hfsmp->hfs_privdir_attr.ca_entries++;
1582 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
1583 &hfsmp->hfs_privdir_attr, NULL, NULL);
1584
1585 /* Unlock the Catalog */
1586 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1587 if (error) goto out;
1588
1589 cp->c_flag |= C_CHANGE | C_DELETED | C_NOEXISTS;
1590 --cp->c_nlink;
1591
1592 } else /* Not busy */ {
1593
1594 /* Lock catalog b-tree */
1595 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1596 if (error) goto out;
1597
1598 if (vp->v_type == VDIR && cp->c_entries > 0)
1599 panic("hfs_remove: attempting to delete a non-empty directory!");
1600 if (vp->v_type != VDIR && cp->c_blocks > 0)
1601 panic("hfs_remove: attempting to delete a non-empty file!");
1602
1603 error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
1604
1605 if (error && truncated)
1606 panic("hfs_remove: couldn't delete a truncated file!");
1607
1608 /* Unlock the Catalog */
1609 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1610 if (error) goto out;
1611
1612 #if QUOTA
1613 if (!hfs_getinoquota(cp))
1614 (void)hfs_chkiq(cp, -1, NOCRED, 0);
1615 #endif /* QUOTA */
1616
1617 cp->c_mode = 0;
1618 cp->c_flag |= C_CHANGE | C_NOEXISTS;
1619 --cp->c_nlink;
1620 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
1621 }
1622
1623 /*
1624 * All done with this cnode's descriptor...
1625 *
1626 * Note: all future catalog calls for this cnode must be
1627 * by fileid only. This is OK for HFS (which doesn't have
1628 * file thread records) since HFS doesn't support hard
1629 * links or the removal of busy files.
1630 */
1631 cat_releasedesc(&cp->c_desc);
1632
1633 /* In all three cases the parent lost a child */
1634 if (dcp->c_entries > 0)
1635 dcp->c_entries--;
1636 if (dcp->c_nlink > 0)
1637 dcp->c_nlink--;
1638 dcp->c_flag |= C_CHANGE | C_UPDATE;
1639 tv = time;
1640 (void) VOP_UPDATE(dvp, &tv, &tv, 0);
1641
1642 if (rvp)
1643 vrele(rvp);
1644 VOP_UNLOCK(vp, 0, p);
1645 (void) ubc_uncache(vp);
1646 vrele(vp);
1647 vput(dvp);
1648 return (0);
1649 out:
1650 if (rvp)
1651 vrele(rvp);
1652
1653 /* Commit the truncation to the catalog record */
1654 if (truncated) {
1655 cp->c_flag |= C_CHANGE | C_UPDATE;
1656 tv = time;
1657 (void) VOP_UPDATE(vp, &tv, &tv, 0);
1658 }
1659 vput(vp);
1660 vput(dvp);
1661 return (error);
1662 }
1663
1664
1665 __private_extern__ void
1666 replace_desc(struct cnode *cp, struct cat_desc *cdp)
1667 {
1668 /* First release allocated name buffer */
1669 if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) {
1670 char *name = cp->c_desc.cd_nameptr;
1671
1672 cp->c_desc.cd_nameptr = 0;
1673 cp->c_desc.cd_namelen = 0;
1674 cp->c_desc.cd_flags &= ~CD_HASBUF;
1675 FREE(name, M_TEMP);
1676 }
1677 bcopy(cdp, &cp->c_desc, sizeof(cp->c_desc));
1678
1679 /* Cnode now owns the name buffer */
1680 cdp->cd_nameptr = 0;
1681 cdp->cd_namelen = 0;
1682 cdp->cd_flags &= ~CD_HASBUF;
1683 }
1684
1685
1686 /*
1687 #
1688 #% rename fdvp U U U
1689 #% rename fvp U U U
1690 #% rename tdvp L U U
1691 #% rename tvp X U U
1692 #
1693 vop_rename {
1694 IN WILLRELE struct vnode *fdvp;
1695 IN WILLRELE struct vnode *fvp;
1696 IN struct componentname *fcnp;
1697 IN WILLRELE struct vnode *tdvp;
1698 IN WILLRELE struct vnode *tvp;
1699 IN struct componentname *tcnp;
1700 };
1701 */
1702 /*
1703 * Rename a cnode.
1704 *
1705 * The VFS layer guarantees that source and destination will
1706 * either both be directories, or both not be directories.
1707 *
1708 * When the target is a directory, hfs_rename must ensure
1709 * that it is empty.
1710 */
1711
1712 static int
1713 hfs_rename(ap)
1714 struct vop_rename_args /* {
1715 struct vnode *a_fdvp;
1716 struct vnode *a_fvp;
1717 struct componentname *a_fcnp;
1718 struct vnode *a_tdvp;
1719 struct vnode *a_tvp;
1720 struct componentname *a_tcnp;
1721 } */ *ap;
1722 {
1723 struct vnode *tvp = ap->a_tvp;
1724 struct vnode *tdvp = ap->a_tdvp;
1725 struct vnode *fvp = ap->a_fvp;
1726 struct vnode *fdvp = ap->a_fdvp;
1727 struct componentname *tcnp = ap->a_tcnp;
1728 struct componentname *fcnp = ap->a_fcnp;
1729 struct cnode *fcp = NULL;
1730 struct cnode *fdcp = NULL;
1731 struct cnode *tdcp = NULL;
1732 struct cnode *tcp = NULL;
1733 struct cat_desc from_desc;
1734 struct cat_desc to_desc;
1735 struct cat_desc out_desc;
1736 struct hfsmount *hfsmp;
1737 struct proc *p = fcnp->cn_proc;
1738 struct timeval tv;
1739 int retval = 0;
1740 cnid_t oldparent = 0;
1741 cnid_t newparent = 0;
1742
1743 #if HFS_DIAGNOSTIC
1744 if ((tcnp->cn_flags & HASBUF) == 0 ||
1745 (fcnp->cn_flags & HASBUF) == 0)
1746 panic("hfs_rename: no name");
1747 #endif
1748 /*
1749 * When fvp matches tvp they must be case variants
1750 * or hard links, and if they are in the same directory then
1751 * tvp really doesn't exist (see VFS rename).
1752 * XXX Hard link rename is still broken/ignored. If they are
1753 * in different directories then we must have hard links.
1754 * Comments further down describe behaviour of hard links in same dir.
1755 * Note case insensitivity was and still is presumed.
1756 */
1757 if (fvp == tvp) {
1758 if (fdvp != tdvp) {
1759 retval = 0;
1760 goto abortop;
1761 }
1762 tvp = NULL;
1763 }
1764
1765 /*
1766 * Check for cross-device rename.
1767 */
1768 if ((fvp->v_mount != tdvp->v_mount) ||
1769 (tvp && (fvp->v_mount != tvp->v_mount))) {
1770 retval = EXDEV;
1771 goto abortop;
1772 }
1773
1774 /*
1775 * Make sure a remove of "to" vnode is permitted.
1776 */
1777 if (tvp && ((VTOC(tvp)->c_flags & (IMMUTABLE | APPEND)) ||
1778 (VTOC(tdvp)->c_flags & APPEND))) {
1779 retval = EPERM;
1780 goto abortop;
1781 }
1782
1783 if ((retval = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p)))
1784 goto abortop;
1785
1786 /*
1787 * Make sure "from" vnode and its parent are changeable.
1788 */
1789 fdcp = VTOC(fdvp);
1790 fcp = VTOC(fvp);
1791 oldparent = fdcp->c_cnid;
1792 if ((fcp->c_flags & (IMMUTABLE | APPEND)) || (fdcp->c_flags & APPEND)) {
1793 VOP_UNLOCK(fvp, 0, p);
1794 retval = EPERM;
1795 goto abortop;
1796 }
1797
1798 if (fcp->c_parentcnid != fdcp->c_cnid) {
1799 VOP_UNLOCK(fvp, 0, p);
1800 retval = EINVAL;
1801 goto abortop;
1802 }
1803
1804 /*
1805 * Check if names already match...
1806 * XXX The name being checked is from fcp rather than fcnp! If
1807 * there are hard links, fcp yields the name which was
1808 * most recently looked up (yes that design is vulnerable to races)
1809 * and the name most recently looked up was the target, so they
1810 * compare equal and we ignore the rename. XXX
1811 */
1812 if (fvp == ap->a_tvp &&
1813 (bcmp(fcp->c_desc.cd_nameptr, tcnp->cn_nameptr,
1814 fcp->c_desc.cd_namelen) == 0)) {
1815 VOP_UNLOCK(fvp, 0, p);
1816 retval = 0;
1817 goto abortop;
1818 }
1819
1820 /* XXX This doesn't make sense for HFS...
1821 *
1822 * Be sure we are not renaming ".", "..", or an alias of ".". This
1823 * leads to a crippled directory tree. It's pretty tough to do a
1824 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
1825 * doesn't work if the ".." entry is missing.
1826 */
1827 if (fvp->v_type == VDIR) {
1828 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')
1829 || fdcp == fcp
1830 || (fcnp->cn_flags&ISDOTDOT)
1831 || (fcp->c_flag & C_RENAME)) {
1832 VOP_UNLOCK(fvp, 0, p);
1833 retval = EINVAL;
1834 goto abortop;
1835 }
1836 fcp->c_flag |= C_RENAME;
1837 }
1838
1839 /* XXX UFS does vrele(fdvp) here */
1840
1841 /* From now on use bad instead of abort to exit */
1842
1843 tdcp = VTOC(tdvp);
1844 if (tvp)
1845 tcp = VTOC(tvp);
1846
1847 newparent = tdcp->c_cnid;
1848
1849 retval = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
1850 if ((fvp->v_type == VDIR) && (newparent != oldparent)) {
1851 if (retval) /* write access check above */
1852 goto bad;
1853 }
1854 retval = 0; /* Reset value from above, we dont care about it anymore */
1855
1856 /*
1857 * If the destination exists, then be sure its type (file or dir)
1858 * matches that of the source. And, if it is a directory make sure
1859 * it is empty. Then delete the destination.
1860 */
1861 if (tvp) {
1862 /*
1863 * If the parent directory is "sticky", then the user must
1864 * own the parent directory, or the destination of the rename,
1865 * otherwise the destination may not be changed (except by
1866 * root). This implements append-only directories.
1867 */
1868 if ((tdcp->c_mode & S_ISTXT) && (tcnp->cn_cred->cr_uid != 0) &&
1869 tcnp->cn_cred->cr_uid != tdcp->c_uid &&
1870 tcnp->cn_cred->cr_uid != tcp->c_uid) {
1871 retval = EPERM;
1872 goto bad;
1873 }
1874
1875 /*
1876 * Target must be empty if a directory.
1877 */
1878 if (S_ISDIR(tcp->c_mode) && (tcp->c_nlink > 2)) {
1879 retval = ENOTEMPTY;
1880 goto bad;
1881 }
1882
1883 /*
1884 * VOP_REMOVE will vput tdvp so we better bump
1885 * its ref count and relockit, always set tvp to
1886 * NULL afterwards to indicate that were done with it.
1887 */
1888 VREF(tdvp);
1889
1890 cache_purge(tvp);
1891
1892 tcnp->cn_flags &= ~SAVENAME;
1893
1894 if (tvp->v_type == VDIR)
1895 retval = VOP_RMDIR(tdvp, tvp, tcnp);
1896 else
1897 retval = VOP_REMOVE(tdvp, tvp, tcnp);
1898
1899 (void) vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY, p);
1900 tvp = NULL;
1901 tcp = NULL;
1902 if (retval)
1903 goto bad;
1904
1905 }
1906
1907 /* XXX
1908 * Prevent lock heirarchy violation (deadlock):
1909 *
1910 * If fdvp is the parent of tdvp then we must drop
1911 * tdvp lock before aquiring the lock for fdvp.
1912 */
1913 if (newparent != oldparent)
1914 vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY, p);
1915
1916 /* remove the existing entry from the namei cache: */
1917 cache_purge(fvp);
1918
1919 hfsmp = VTOHFS(fvp);
1920 bzero(&from_desc, sizeof(from_desc));
1921 from_desc.cd_nameptr = fcnp->cn_nameptr;
1922 from_desc.cd_namelen = fcnp->cn_namelen;
1923 from_desc.cd_parentcnid = fdcp->c_cnid;
1924 from_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
1925 from_desc.cd_cnid = fcp->c_cnid;
1926 bzero(&to_desc, sizeof(to_desc));
1927 to_desc.cd_nameptr = tcnp->cn_nameptr;
1928 to_desc.cd_namelen = tcnp->cn_namelen;
1929 to_desc.cd_parentcnid = tdcp->c_cnid;
1930 to_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
1931 to_desc.cd_cnid = fcp->c_cnid;
1932
1933 /* Lock catalog b-tree */
1934 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1935 if (retval) {
1936 if (newparent != oldparent) /* unlock the lock we just got */
1937 VOP_UNLOCK(fdvp, 0, p);
1938 goto bad;
1939 }
1940 retval = cat_rename(hfsmp, &from_desc, &tdcp->c_desc,
1941 &to_desc, &out_desc);
1942
1943 /* Unlock catalog b-tree */
1944 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1945
1946 if (newparent != oldparent)
1947 VOP_UNLOCK(fdvp, 0, p);
1948
1949 if (retval) goto bad;
1950
1951 /* update cnode's catalog descriptor */
1952 replace_desc(fcp, &out_desc);
1953
1954 fcp->c_flag &= ~C_RENAME;
1955
1956 /*
1957 * Time stamp both parent directories.
1958 * Note that if this is a rename within the same directory,
1959 * (where tdcp == fdcp)
1960 * the code below is still safe and correct.
1961 */
1962 if (fdcp->c_nlink > 0)
1963 fdcp->c_nlink--;
1964 if (fdcp->c_entries > 0)
1965 fdcp->c_entries--;
1966 tdcp->c_nlink++;
1967 tdcp->c_entries++;
1968 fdcp->c_flag |= C_UPDATE;
1969 tdcp->c_flag |= C_UPDATE;
1970 tv = time;
1971 CTIMES(fdcp, &tv, &tv);
1972 CTIMES(tdcp, &tv, &tv);
1973 tdcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
1974
1975 hfs_volupdate(hfsmp, fvp->v_type == VDIR ? VOL_RMDIR : VOL_RMFILE,
1976 (fdcp->c_cnid == kHFSRootFolderID));
1977 hfs_volupdate(hfsmp, fvp->v_type == VDIR ? VOL_MKDIR : VOL_MKFILE,
1978 (tdcp->c_cnid == kHFSRootFolderID));
1979
1980 vput(tdvp);
1981 vrele(fdvp);
1982 vput(fvp);
1983 return (0);
1984
1985 bad:
1986 if (fcp)
1987 fcp->c_flag &= ~C_RENAME;
1988 if (tdvp == tvp)
1989 vrele(tdvp);
1990 else
1991 vput(tdvp);
1992 if (tvp)
1993 vput(tvp);
1994 vrele(fdvp);
1995
1996 if (VOP_ISLOCKED(fvp))
1997 vput(fvp);
1998 else
1999 vrele(fvp);
2000 return (retval);
2001
2002 abortop:
2003
2004 VOP_ABORTOP(tdvp, tcnp);
2005 if (tdvp == tvp)
2006 vrele(tdvp);
2007 else
2008 vput(tdvp);
2009 if (tvp)
2010 vput(tvp);
2011 VOP_ABORTOP(fdvp, fcnp);
2012 vrele(fdvp);
2013 vrele(fvp);
2014 return (retval);
2015 }
2016
2017
2018
2019 /*
2020 * Mkdir system call
2021 #% mkdir dvp L U U
2022 #% mkdir vpp - L -
2023 #
2024 vop_mkdir {
2025 IN WILLRELE struct vnode *dvp;
2026 OUT struct vnode **vpp;
2027 IN struct componentname *cnp;
2028 IN struct vattr *vap;
2029
2030 We are responsible for freeing the namei buffer,
2031 it is done in hfs_makenode()
2032 */
2033
2034 static int
2035 hfs_mkdir(ap)
2036 struct vop_mkdir_args /* {
2037 struct vnode *a_dvp;
2038 struct vnode **a_vpp;
2039 struct componentname *a_cnp;
2040 struct vattr *a_vap;
2041 } */ *ap;
2042 {
2043 struct vattr *vap = ap->a_vap;
2044
2045 return (hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
2046 ap->a_dvp, ap->a_vpp, ap->a_cnp));
2047 }
2048
2049
2050 /*
2051 * symlink -- make a symbolic link
2052 #% symlink dvp L U U
2053 #% symlink vpp - U -
2054 #
2055 # XXX - note that the return vnode has already been VRELE'ed
2056 # by the filesystem layer. To use it you must use vget,
2057 # possibly with a further namei.
2058 #
2059 vop_symlink {
2060 IN WILLRELE struct vnode *dvp;
2061 OUT WILLRELE struct vnode **vpp;
2062 IN struct componentname *cnp;
2063 IN struct vattr *vap;
2064 IN char *target;
2065
2066 We are responsible for freeing the namei buffer,
2067 it is done in hfs_makenode().
2068
2069 */
2070
2071 static int
2072 hfs_symlink(ap)
2073 struct vop_symlink_args /* {
2074 struct vnode *a_dvp;
2075 struct vnode **a_vpp;
2076 struct componentname *a_cnp;
2077 struct vattr *a_vap;
2078 char *a_target;
2079 } */ *ap;
2080 {
2081 register struct vnode *vp, **vpp = ap->a_vpp;
2082 struct filefork *fp;
2083 int len, error;
2084 struct buf *bp = NULL;
2085
2086 /* HFS standard disks don't support symbolic links */
2087 if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
2088 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
2089 vput(ap->a_dvp);
2090 return (EOPNOTSUPP);
2091 }
2092
2093 /* Check for empty target name */
2094 if (ap->a_target[0] == 0) {
2095 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
2096 vput(ap->a_dvp);
2097 return (EINVAL);
2098 }
2099
2100 /* Create the vnode */
2101 if ((error = hfs_makenode(S_IFLNK | ap->a_vap->va_mode,
2102 ap->a_dvp, vpp, ap->a_cnp)))
2103 return (error);
2104
2105 vp = *vpp;
2106 len = strlen(ap->a_target);
2107 fp = VTOF(vp);
2108 fp->ff_clumpsize = VTOVCB(vp)->blockSize;
2109
2110 /* Allocate space for the link */
2111 error = VOP_TRUNCATE(vp, len, IO_NOZEROFILL,
2112 ap->a_cnp->cn_cred, ap->a_cnp->cn_proc);
2113 if (error)
2114 goto out; /* XXX need to remove link */
2115
2116 /* Write the link to disk */
2117 bp = getblk(vp, 0, roundup((int)fp->ff_size, VTOHFS(vp)->hfs_phys_block_size),
2118 0, 0, BLK_META);
2119 bzero(bp->b_data, bp->b_bufsize);
2120 bcopy(ap->a_target, bp->b_data, len);
2121 bawrite(bp);
2122 out:
2123 vput(vp);
2124 return (error);
2125 }
2126
2127
2128 /*
2129 * Dummy dirents to simulate the "." and ".." entries of the directory
2130 * in a hfs filesystem. HFS doesn't provide these on disk. Note that
2131 * the size of these entries is the smallest needed to represent them
2132 * (only 12 byte each).
2133 */
2134 static hfsdotentry rootdots[2] = {
2135 {
2136 1, /* d_fileno */
2137 sizeof(struct hfsdotentry), /* d_reclen */
2138 DT_DIR, /* d_type */
2139 1, /* d_namlen */
2140 "." /* d_name */
2141 },
2142 {
2143 1, /* d_fileno */
2144 sizeof(struct hfsdotentry), /* d_reclen */
2145 DT_DIR, /* d_type */
2146 2, /* d_namlen */
2147 ".." /* d_name */
2148 }
2149 };
2150
2151 /* 4.3 Note:
2152 * There is some confusion as to what the semantics of uio_offset are.
2153 * In ufs, it represents the actual byte offset within the directory
2154 * "file." HFS, however, just uses it as an entry counter - essentially
2155 * assuming that it has no meaning except to the hfs_readdir function.
2156 * This approach would be more efficient here, but some callers may
2157 * assume the uio_offset acts like a byte offset. NFS in fact
2158 * monkeys around with the offset field a lot between readdir calls.
2159 *
2160 * The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
2161 * fields is a mess as well. The libc function readdir() returns
2162 * NULL (indicating the end of a directory) when either
2163 * the getdirentries() syscall (which calls this and returns
2164 * the size of the buffer passed in less the value of uiop->uio_resid)
2165 * returns 0, or a direct record with a d_reclen of zero.
2166 * nfs_server.c:rfs_readdir(), on the other hand, checks for the end
2167 * of the directory by testing uiop->uio_resid == 0. The solution
2168 * is to pad the size of the last struct direct in a given
2169 * block to fill the block if we are not at the end of the directory.
2170 */
2171
2172
2173 /*
2174 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
2175 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
2176 * "Nbytes must be greater than or equal to the block size associated with the file,
2177 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
2178 * room for the . and .. entries that have to added manually.
2179 */
2180
2181 /*
2182 #% readdir vp L L L
2183 #
2184 vop_readdir {
2185 IN struct vnode *vp;
2186 INOUT struct uio *uio;
2187 IN struct ucred *cred;
2188 INOUT int *eofflag;
2189 OUT int *ncookies;
2190 INOUT u_long **cookies;
2191 */
2192 static int
2193 hfs_readdir(ap)
2194 struct vop_readdir_args /* {
2195 struct vnode *vp;
2196 struct uio *uio;
2197 struct ucred *cred;
2198 int *eofflag;
2199 int *ncookies;
2200 u_long **cookies;
2201 } */ *ap;
2202 {
2203 register struct uio *uio = ap->a_uio;
2204 struct cnode *cp = VTOC(ap->a_vp);
2205 struct hfsmount *hfsmp = VTOHFS(ap->a_vp);
2206 struct proc *p = current_proc();
2207 off_t off = uio->uio_offset;
2208 int retval = 0;
2209 int eofflag = 0;
2210
2211 /* We assume it's all one big buffer... */
2212 if (uio->uio_iovcnt > 1 || uio->uio_resid < AVERAGE_HFSDIRENTRY_SIZE)
2213 return EINVAL;
2214
2215 /* Create the entries for . and .. */
2216 if (uio->uio_offset < sizeof(rootdots)) {
2217 caddr_t dep;
2218 size_t dotsize;
2219
2220 rootdots[0].d_fileno = cp->c_cnid;
2221 rootdots[1].d_fileno = cp->c_parentcnid;
2222
2223 if (uio->uio_offset == 0) {
2224 dep = (caddr_t) &rootdots[0];
2225 dotsize = 2* sizeof(struct hfsdotentry);
2226 } else if (uio->uio_offset == sizeof(struct hfsdotentry)) {
2227 dep = (caddr_t) &rootdots[1];
2228 dotsize = sizeof(struct hfsdotentry);
2229 } else {
2230 retval = EINVAL;
2231 goto Exit;
2232 }
2233
2234 retval = uiomove(dep, dotsize, uio);
2235 if (retval != 0)
2236 goto Exit;
2237 }
2238
2239 /* If there are no children then we're done */
2240 if (cp->c_entries == 0) {
2241 eofflag = 1;
2242 retval = 0;
2243 goto Exit;
2244 }
2245
2246 /* Lock catalog b-tree */
2247 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p);
2248 if (retval) goto Exit;
2249
2250 retval = cat_getdirentries(hfsmp, &cp->c_desc, uio, &eofflag);
2251
2252 /* Unlock catalog b-tree */
2253 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
2254
2255 if (retval != E_NONE) {
2256 goto Exit;
2257 }
2258
2259 /* were we already past eof ? */
2260 if (uio->uio_offset == off) {
2261 retval = E_NONE;
2262 goto Exit;
2263 }
2264
2265 cp->c_flag |= C_ACCESS;
2266 /* Bake any cookies */
2267 if (!retval && ap->a_ncookies != NULL) {
2268 struct dirent* dpStart;
2269 struct dirent* dpEnd;
2270 struct dirent* dp;
2271 int ncookies;
2272 u_long *cookies;
2273 u_long *cookiep;
2274
2275 /*
2276 * Only the NFS server uses cookies, and it loads the
2277 * directory block into system space, so we can just look at
2278 * it directly.
2279 */
2280 if (uio->uio_segflg != UIO_SYSSPACE)
2281 panic("hfs_readdir: unexpected uio from NFS server");
2282 dpStart = (struct dirent *)(uio->uio_iov->iov_base - (uio->uio_offset - off));
2283 dpEnd = (struct dirent *) uio->uio_iov->iov_base;
2284 for (dp = dpStart, ncookies = 0;
2285 dp < dpEnd && dp->d_reclen != 0;
2286 dp = (struct dirent *)((caddr_t)dp + dp->d_reclen))
2287 ncookies++;
2288 MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK);
2289 for (dp = dpStart, cookiep = cookies;
2290 dp < dpEnd;
2291 dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
2292 off += dp->d_reclen;
2293 *cookiep++ = (u_long) off;
2294 }
2295 *ap->a_ncookies = ncookies;
2296 *ap->a_cookies = cookies;
2297 }
2298
2299 Exit:;
2300 if (ap->a_eofflag)
2301 *ap->a_eofflag = eofflag;
2302
2303 return (retval);
2304 }
2305
2306
2307 /*
2308 * Return target name of a symbolic link
2309 #% readlink vp L L L
2310 #
2311 vop_readlink {
2312 IN struct vnode *vp;
2313 INOUT struct uio *uio;
2314 IN struct ucred *cred;
2315 */
2316
2317 static int
2318 hfs_readlink(ap)
2319 struct vop_readlink_args /* {
2320 struct vnode *a_vp;
2321 struct uio *a_uio;
2322 struct ucred *a_cred;
2323 } */ *ap;
2324 {
2325 int retval;
2326 struct vnode *vp = ap->a_vp;
2327 struct cnode *cp;
2328 struct filefork *fp;
2329
2330 if (vp->v_type != VLNK)
2331 return (EINVAL);
2332
2333 cp = VTOC(vp);
2334 fp = VTOF(vp);
2335
2336 /* Zero length sym links are not allowed */
2337 if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
2338 VTOVCB(vp)->vcbFlags |= kHFS_DamagedVolume;
2339 return (EINVAL);
2340 }
2341
2342 /* Cache the path so we don't waste buffer cache resources */
2343 if (fp->ff_symlinkptr == NULL) {
2344 struct buf *bp = NULL;
2345
2346 MALLOC(fp->ff_symlinkptr, char *, fp->ff_size, M_TEMP, M_WAITOK);
2347 retval = meta_bread(vp, 0,
2348 roundup((int)fp->ff_size,
2349 VTOHFS(vp)->hfs_phys_block_size),
2350 ap->a_cred, &bp);
2351 if (retval) {
2352 if (bp)
2353 brelse(bp);
2354 if (fp->ff_symlinkptr) {
2355 FREE(fp->ff_symlinkptr, M_TEMP);
2356 fp->ff_symlinkptr = NULL;
2357 }
2358 return (retval);
2359 }
2360 bcopy(bp->b_data, fp->ff_symlinkptr, (size_t)fp->ff_size);
2361 if (bp) {
2362 bp->b_flags |= B_INVAL; /* data no longer needed */
2363 brelse(bp);
2364 }
2365 }
2366 retval = uiomove((caddr_t)fp->ff_symlinkptr, (int)fp->ff_size, ap->a_uio);
2367
2368 return (retval);
2369 }
2370
2371
2372 /*
2373 * hfs abort op, called after namei() when a CREATE/DELETE isn't actually
2374 * done. If a buffer has been saved in anticipation of a CREATE, delete it.
2375 #% abortop dvp = = =
2376 #
2377 vop_abortop {
2378 IN struct vnode *dvp;
2379 IN struct componentname *cnp;
2380
2381 */
2382
2383 /* ARGSUSED */
2384
2385 static int
2386 hfs_abortop(ap)
2387 struct vop_abortop_args /* {
2388 struct vnode *a_dvp;
2389 struct componentname *a_cnp;
2390 } */ *ap;
2391 {
2392 if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
2393 FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
2394
2395 return (0);
2396 }
2397
2398
2399 /*
2400 * Lock an cnode. If its already locked, set the WANT bit and sleep.
2401 #% lock vp U L U
2402 #
2403 vop_lock {
2404 IN struct vnode *vp;
2405 IN int flags;
2406 IN struct proc *p;
2407 */
2408
2409 static int
2410 hfs_lock(ap)
2411 struct vop_lock_args /* {
2412 struct vnode *a_vp;
2413 int a_flags;
2414 struct proc *a_p;
2415 } */ *ap;
2416 {
2417 struct vnode *vp = ap->a_vp;
2418 struct cnode *cp = VTOC(vp);
2419
2420 if (cp == NULL)
2421 panic("hfs_lock: cnode in vnode is null\n");
2422
2423 return (lockmgr(&cp->c_lock, ap->a_flags, &vp->v_interlock, ap->a_p));
2424 }
2425
2426 /*
2427 * Unlock an cnode.
2428 #% unlock vp L U L
2429 #
2430 vop_unlock {
2431 IN struct vnode *vp;
2432 IN int flags;
2433 IN struct proc *p;
2434
2435 */
2436 static int
2437 hfs_unlock(ap)
2438 struct vop_unlock_args /* {
2439 struct vnode *a_vp;
2440 int a_flags;
2441 struct proc *a_p;
2442 } */ *ap;
2443 {
2444 struct vnode *vp = ap->a_vp;
2445 struct cnode *cp = VTOC(vp);
2446
2447 if (cp == NULL)
2448 panic("hfs_unlock: cnode in vnode is null\n");
2449
2450 return (lockmgr(&cp->c_lock, ap->a_flags | LK_RELEASE,
2451 &vp->v_interlock, ap->a_p));
2452 }
2453
2454
2455 /*
2456 * Print out the contents of a cnode.
2457 #% print vp = = =
2458 #
2459 vop_print {
2460 IN struct vnode *vp;
2461 */
2462 static int
2463 hfs_print(ap)
2464 struct vop_print_args /* {
2465 struct vnode *a_vp;
2466 } */ *ap;
2467 {
2468 struct vnode * vp = ap->a_vp;
2469 struct cnode *cp = VTOC(vp);
2470
2471 printf("tag VT_HFS, cnid %d, on dev %d, %d", cp->c_cnid,
2472 major(cp->c_dev), minor(cp->c_dev));
2473 #if FIFO
2474 if (vp->v_type == VFIFO)
2475 fifo_printinfo(vp);
2476 #endif /* FIFO */
2477 lockmgr_printinfo(&cp->c_lock);
2478 printf("\n");
2479 return (0);
2480 }
2481
2482
2483 /*
2484 * Check for a locked cnode.
2485 #% islocked vp = = =
2486 #
2487 vop_islocked {
2488 IN struct vnode *vp;
2489
2490 */
2491 static int
2492 hfs_islocked(ap)
2493 struct vop_islocked_args /* {
2494 struct vnode *a_vp;
2495 } */ *ap;
2496 {
2497 return (lockstatus(&VTOC(ap->a_vp)->c_lock));
2498 }
2499
2500 /*
2501
2502 #% pathconf vp L L L
2503 #
2504 vop_pathconf {
2505 IN struct vnode *vp;
2506 IN int name;
2507 OUT register_t *retval;
2508
2509 */
2510 static int
2511 hfs_pathconf(ap)
2512 struct vop_pathconf_args /* {
2513 struct vnode *a_vp;
2514 int a_name;
2515 int *a_retval;
2516 } */ *ap;
2517 {
2518 int retval = 0;
2519
2520 switch (ap->a_name) {
2521 case _PC_LINK_MAX:
2522 if (VTOVCB(ap->a_vp)->vcbSigWord == kHFSPlusSigWord)
2523 *ap->a_retval = HFS_LINK_MAX;
2524 else
2525 *ap->a_retval = 1;
2526 break;
2527 case _PC_NAME_MAX:
2528 *ap->a_retval = kHFSPlusMaxFileNameBytes; /* max # of characters x max utf8 representation */
2529 break;
2530 case _PC_PATH_MAX:
2531 *ap->a_retval = PATH_MAX; /* 1024 */
2532 break;
2533 case _PC_CHOWN_RESTRICTED:
2534 *ap->a_retval = 1;
2535 break;
2536 case _PC_NO_TRUNC:
2537 *ap->a_retval = 0;
2538 break;
2539 case _PC_NAME_CHARS_MAX:
2540 *ap->a_retval = kHFSPlusMaxFileNameChars;
2541 break;
2542 case _PC_CASE_SENSITIVE:
2543 *ap->a_retval = 0;
2544 break;
2545 case _PC_CASE_PRESERVING:
2546 *ap->a_retval = 1;
2547 break;
2548 default:
2549 retval = EINVAL;
2550 }
2551
2552 return (retval);
2553 }
2554
2555
2556 /*
2557 * Advisory record locking support
2558 #% advlock vp U U U
2559 #
2560 vop_advlock {
2561 IN struct vnode *vp;
2562 IN caddr_t id;
2563 IN int op;
2564 IN struct flock *fl;
2565 IN int flags;
2566
2567 */
2568 static int
2569 hfs_advlock(ap)
2570 struct vop_advlock_args /* {
2571 struct vnode *a_vp;
2572 caddr_t a_id;
2573 int a_op;
2574 struct flock *a_fl;
2575 int a_flags;
2576 } */ *ap;
2577 {
2578 struct vnode *vp = ap->a_vp;
2579 struct flock *fl = ap->a_fl;
2580 struct hfslockf *lock;
2581 struct filefork *fork;
2582 off_t start, end;
2583 int retval;
2584
2585 /* Only regular files can have locks */
2586 if (vp->v_type != VREG)
2587 return (EISDIR);
2588
2589 fork = VTOF(ap->a_vp);
2590 /*
2591 * Avoid the common case of unlocking when cnode has no locks.
2592 */
2593 if (fork->ff_lockf == (struct hfslockf *)0) {
2594 if (ap->a_op != F_SETLK) {
2595 fl->l_type = F_UNLCK;
2596 return (0);
2597 }
2598 }
2599 /*
2600 * Convert the flock structure into a start and end.
2601 */
2602 start = 0;
2603 switch (fl->l_whence) {
2604 case SEEK_SET:
2605 case SEEK_CUR:
2606 /*
2607 * Caller is responsible for adding any necessary offset
2608 * when SEEK_CUR is used.
2609 */
2610 start = fl->l_start;
2611 break;
2612 case SEEK_END:
2613 start = fork->ff_size + fl->l_start;
2614 break;
2615 default:
2616 return (EINVAL);
2617 }
2618
2619 if (start < 0)
2620 return (EINVAL);
2621 if (fl->l_len == 0)
2622 end = -1;
2623 else
2624 end = start + fl->l_len - 1;
2625
2626 /*
2627 * Create the hfslockf structure
2628 */
2629 MALLOC(lock, struct hfslockf *, sizeof *lock, M_LOCKF, M_WAITOK);
2630 lock->lf_start = start;
2631 lock->lf_end = end;
2632 lock->lf_id = ap->a_id;
2633 lock->lf_fork = fork;
2634 lock->lf_type = fl->l_type;
2635 lock->lf_next = (struct hfslockf *)0;
2636 TAILQ_INIT(&lock->lf_blkhd);
2637 lock->lf_flags = ap->a_flags;
2638 /*
2639 * Do the requested operation.
2640 */
2641 switch(ap->a_op) {
2642 case F_SETLK:
2643 retval = hfs_setlock(lock);
2644 break;
2645 case F_UNLCK:
2646 retval = hfs_clearlock(lock);
2647 FREE(lock, M_LOCKF);
2648 break;
2649 case F_GETLK:
2650 retval = hfs_getlock(lock, fl);
2651 FREE(lock, M_LOCKF);
2652 break;
2653 default:
2654 retval = EINVAL;
2655 _FREE(lock, M_LOCKF);
2656 break;
2657 }
2658
2659 return (retval);
2660 }
2661
2662
2663
2664 /*
2665 * Update the access, modified, and node change times as specified
2666 * by the C_ACCESS, C_UPDATE, and C_CHANGE flags respectively. The
2667 * C_MODIFIED flag is used to specify that the node needs to be
2668 * updated but that the times have already been set. The access and
2669 * modified times are input parameters but the node change time is
2670 * always taken from the current time. If waitfor is set, then wait
2671 * for the disk write of the node to complete.
2672 */
2673 /*
2674 #% update vp L L L
2675 IN struct vnode *vp;
2676 IN struct timeval *access;
2677 IN struct timeval *modify;
2678 IN int waitfor;
2679 */
2680 static int
2681 hfs_update(ap)
2682 struct vop_update_args /* {
2683 struct vnode *a_vp;
2684 struct timeval *a_access;
2685 struct timeval *a_modify;
2686 int a_waitfor;
2687 } */ *ap;
2688 {
2689 struct vnode *vp = ap->a_vp;
2690 struct cnode *cp = VTOC(ap->a_vp);
2691 struct proc *p;
2692 struct cat_fork *dataforkp = NULL;
2693 struct cat_fork *rsrcforkp = NULL;
2694 struct cat_fork datafork;
2695 int updateflag;
2696 int error;
2697
2698 /* XXX do we really want to clear the sytem cnode flags here???? */
2699 if ((vp->v_flag & VSYSTEM) ||
2700 (VTOVFS(vp)->mnt_flag & MNT_RDONLY) ||
2701 (cp->c_mode == 0)) {
2702 cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE);
2703 return (0);
2704 }
2705
2706 updateflag = cp->c_flag & (C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE);
2707
2708 /* Nothing to update. */
2709 if (updateflag == 0)
2710 return (0);
2711 /* HFS standard doesn't have access times. */
2712 if ((updateflag == C_ACCESS) && (VTOVCB(vp)->vcbSigWord == kHFSSigWord))
2713 return (0);
2714 if (updateflag & C_ACCESS) {
2715 /*
2716 * If only the access time is changing then defer
2717 * updating it on-disk util later (in hfs_inactive).
2718 * If it was recently updated then skip the update.
2719 */
2720 if (updateflag == C_ACCESS) {
2721 cp->c_flag &= ~C_ACCESS;
2722
2723 /* Its going to disk or its sufficiently newer... */
2724 if ((cp->c_flag & C_ATIMEMOD) ||
2725 (ap->a_access->tv_sec > (cp->c_atime + ATIME_ACCURACY))) {
2726 cp->c_atime = ap->a_access->tv_sec;
2727 cp->c_flag |= C_ATIMEMOD;
2728 }
2729 return (0);
2730 } else {
2731 cp->c_atime = ap->a_access->tv_sec;
2732 }
2733 }
2734 if (updateflag & C_UPDATE) {
2735 cp->c_mtime = ap->a_modify->tv_sec;
2736 cp->c_mtime_nsec = ap->a_modify->tv_usec * 1000;
2737 }
2738 if (updateflag & C_CHANGE) {
2739 cp->c_ctime = time.tv_sec;
2740 /*
2741 * HFS dates that WE set must be adjusted for DST
2742 */
2743 if ((VTOVCB(vp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
2744 cp->c_ctime += 3600;
2745 cp->c_mtime = cp->c_ctime;
2746 }
2747 }
2748
2749 if (cp->c_datafork)
2750 dataforkp = &cp->c_datafork->ff_data;
2751 if (cp->c_rsrcfork)
2752 rsrcforkp = &cp->c_rsrcfork->ff_data;
2753
2754 p = current_proc();
2755
2756 /*
2757 * For delayed allocations updates are
2758 * postponed until an fsync or the file
2759 * gets written to disk.
2760 *
2761 * Deleted files can defer meta data updates until inactive.
2762 */
2763 if (ISSET(cp->c_flag, C_DELETED) ||
2764 (dataforkp && cp->c_datafork->ff_unallocblocks) ||
2765 (rsrcforkp && cp->c_rsrcfork->ff_unallocblocks)) {
2766 if (updateflag & (C_CHANGE | C_UPDATE))
2767 hfs_volupdate(VTOHFS(vp), VOL_UPDATE, 0);
2768 cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE);
2769 cp->c_flag |= C_MODIFIED;
2770 return (0);
2771 }
2772
2773 /*
2774 * For files with invalid ranges (holes) the on-disk
2775 * field representing the size of the file (cf_size)
2776 * must be no larger than the start of the first hole.
2777 */
2778 if (dataforkp && !CIRCLEQ_EMPTY(&cp->c_datafork->ff_invalidranges)) {
2779 bcopy(dataforkp, &datafork, sizeof(datafork));
2780 datafork.cf_size = CIRCLEQ_FIRST(&cp->c_datafork->ff_invalidranges)->rl_start;
2781 dataforkp = &datafork;
2782 }
2783
2784 /*
2785 * Lock the Catalog b-tree file.
2786 * A shared lock is sufficient since an update doesn't change
2787 * the tree and the lock on vp protects the cnode.
2788 */
2789 error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, p);
2790 if (error)
2791 return (error);
2792
2793 /* XXX - waitfor is not enforced */
2794 error = cat_update(VTOHFS(vp), &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
2795
2796 /* Unlock the Catalog b-tree file. */
2797 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
2798
2799 if (updateflag & (C_CHANGE | C_UPDATE))
2800 hfs_volupdate(VTOHFS(vp), VOL_UPDATE, 0);
2801
2802 /* After the updates are finished, clear the flags */
2803 cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE | C_ATIMEMOD);
2804
2805 return (error);
2806 }
2807
2808 /*
2809 * Allocate a new node
2810 *
2811 * Upon leaving, namei buffer must be freed.
2812 *
2813 */
2814 static int
2815 hfs_makenode(mode, dvp, vpp, cnp)
2816 int mode;
2817 struct vnode *dvp;
2818 struct vnode **vpp;
2819 struct componentname *cnp;
2820 {
2821 struct cnode *cp;
2822 struct cnode *dcp;
2823 struct vnode *tvp;
2824 struct hfsmount *hfsmp;
2825 struct timeval tv;
2826 struct proc *p;
2827 struct cat_desc in_desc, out_desc;
2828 struct cat_attr attr;
2829 int error;
2830 enum vtype vnodetype;
2831
2832 p = cnp->cn_proc;
2833 dcp = VTOC(dvp);
2834 hfsmp = VTOHFS(dvp);
2835 *vpp = NULL;
2836 tvp = NULL;
2837 bzero(&out_desc, sizeof(out_desc));
2838
2839 if ((mode & S_IFMT) == 0)
2840 mode |= S_IFREG;
2841 vnodetype = IFTOVT(mode);
2842
2843 /* Check if unmount in progress */
2844 if (VTOVFS(dvp)->mnt_kern_flag & MNTK_UNMOUNT) {
2845 error = EPERM;
2846 goto exit;
2847 }
2848 /* Check if were out of usable disk space. */
2849 if ((suser(cnp->cn_cred, NULL) != 0) && (hfs_freeblks(hfsmp, 1) <= 0)) {
2850 error = ENOSPC;
2851 goto exit;
2852 }
2853
2854 /* Setup the default attributes */
2855 bzero(&attr, sizeof(attr));
2856 attr.ca_mode = mode;
2857 attr.ca_nlink = vnodetype == VDIR ? 2 : 1;
2858 attr.ca_mtime = time.tv_sec;
2859 attr.ca_mtime_nsec = time.tv_usec * 1000;
2860 if ((VTOVCB(dvp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
2861 attr.ca_mtime += 3600; /* Same as what hfs_update does */
2862 }
2863 attr.ca_atime = attr.ca_ctime = attr.ca_itime = attr.ca_mtime;
2864 if (VTOVFS(dvp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
2865 attr.ca_uid = hfsmp->hfs_uid;
2866 attr.ca_gid = hfsmp->hfs_gid;
2867 } else {
2868 if (vnodetype == VLNK)
2869 attr.ca_uid = dcp->c_uid;
2870 else
2871 attr.ca_uid = cnp->cn_cred->cr_uid;
2872 attr.ca_gid = dcp->c_gid;
2873 }
2874 /*
2875 * Don't tag as a special file (BLK or CHR) until *after*
2876 * hfs_getnewvnode is called. This insures that any
2877 * alias checking is defered until hfs_mknod completes.
2878 */
2879 if (vnodetype == VBLK || vnodetype == VCHR)
2880 attr.ca_mode = (attr.ca_mode & ~S_IFMT) | S_IFREG;
2881
2882 /* Tag symlinks with a type and creator. */
2883 if (vnodetype == VLNK) {
2884 struct FndrFileInfo *fip;
2885
2886 fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
2887 fip->fdType = SWAP_BE32(kSymLinkFileType);
2888 fip->fdCreator = SWAP_BE32(kSymLinkCreator);
2889 }
2890 if ((attr.ca_mode & S_ISGID) &&
2891 !groupmember(dcp->c_gid, cnp->cn_cred) &&
2892 suser(cnp->cn_cred, NULL)) {
2893 attr.ca_mode &= ~S_ISGID;
2894 }
2895 if (cnp->cn_flags & ISWHITEOUT)
2896 attr.ca_flags |= UF_OPAQUE;
2897
2898 /* Setup the descriptor */
2899 bzero(&in_desc, sizeof(in_desc));
2900 in_desc.cd_nameptr = cnp->cn_nameptr;
2901 in_desc.cd_namelen = cnp->cn_namelen;
2902 in_desc.cd_parentcnid = dcp->c_cnid;
2903 in_desc.cd_flags = S_ISDIR(mode) ? CD_ISDIR : 0;
2904
2905 /* Lock catalog b-tree */
2906 error = hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
2907 if (error)
2908 goto exit;
2909
2910 error = cat_create(hfsmp, &in_desc, &attr, &out_desc);
2911
2912 /* Unlock catalog b-tree */
2913 (void) hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_RELEASE, p);
2914 if (error)
2915 goto exit;
2916
2917 /* Update the parent directory */
2918 dcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
2919 dcp->c_nlink++;
2920 dcp->c_entries++;
2921 dcp->c_flag |= C_CHANGE | C_UPDATE;
2922 tv = time;
2923 (void) VOP_UPDATE(dvp, &tv, &tv, 0);
2924 hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE,
2925 (dcp->c_cnid == kHFSRootFolderID));
2926
2927 /* Create a vnode for the object just created: */
2928 error = hfs_getnewvnode(hfsmp, NULL, &out_desc, 0, &attr, NULL, &tvp);
2929 if (error)
2930 goto exit;
2931
2932 #if QUOTA
2933 cp = VTOC(tvp);
2934 /*
2935 * We call hfs_chkiq with FORCE flag so that if we
2936 * fall through to the rmdir we actually have
2937 * accounted for the inode
2938 */
2939 if ((error = hfs_getinoquota(cp)) ||
2940 (error = hfs_chkiq(cp, 1, cnp->cn_cred, FORCE))) {
2941 if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
2942 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
2943 }
2944 if (tvp->v_type == VDIR)
2945 VOP_RMDIR(dvp,tvp, cnp);
2946 else
2947 VOP_REMOVE(dvp,tvp, cnp);
2948 return (error);
2949 }
2950 #endif /* QUOTA */
2951
2952 /*
2953 * restore vtype and mode for VBLK and VCHR
2954 */
2955 if (vnodetype == VBLK || vnodetype == VCHR) {
2956 struct cnode *cp;
2957
2958 cp = VTOC(tvp);
2959 cp->c_mode = mode;
2960 tvp->v_type = IFTOVT(mode);
2961 cp->c_flag |= C_CHANGE;
2962 tv = time;
2963 if ((error = VOP_UPDATE(tvp, &tv, &tv, 1))) {
2964 vput(tvp);
2965 goto exit;
2966 }
2967 }
2968
2969 *vpp = tvp;
2970 exit:
2971 cat_releasedesc(&out_desc);
2972
2973 if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
2974 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
2975 vput(dvp);
2976
2977 return (error);
2978 }
2979
2980
2981 static int
2982 hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp, struct vnode **rvpp, struct proc *p)
2983 {
2984 struct vnode *rvp;
2985 struct cnode *cp = VTOC(vp);
2986 int error;
2987
2988 if ((rvp = cp->c_rsrc_vp)) {
2989 /* Use exising vnode */
2990 error = vget(rvp, 0, p);
2991 if (error) {
2992 char * name = VTOC(vp)->c_desc.cd_nameptr;
2993
2994 if (name)
2995 printf("hfs_vgetrsrc: couldn't get"
2996 " resource fork for %s\n", name);
2997 return (error);
2998 }
2999 } else {
3000 struct cat_fork rsrcfork;
3001
3002 /* Lock catalog b-tree */
3003 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p);
3004 if (error)
3005 return (error);
3006
3007 /* Get resource fork data */
3008 error = cat_lookup(hfsmp, &cp->c_desc, 1, (struct cat_desc *)0,
3009 (struct cat_attr *)0, &rsrcfork);
3010
3011 /* Unlock the Catalog */
3012 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
3013 if (error)
3014 return (error);
3015
3016 error = hfs_getnewvnode(hfsmp, cp, &cp->c_desc, 1, &cp->c_attr,
3017 &rsrcfork, &rvp);
3018 if (error)
3019 return (error);
3020 }
3021
3022 *rvpp = rvp;
3023 return (0);
3024 }
3025
3026
3027 /*
3028 * Wrapper for special device reads
3029 */
3030 static int
3031 hfsspec_read(ap)
3032 struct vop_read_args /* {
3033 struct vnode *a_vp;
3034 struct uio *a_uio;
3035 int a_ioflag;
3036 struct ucred *a_cred;
3037 } */ *ap;
3038 {
3039 /*
3040 * Set access flag.
3041 */
3042 VTOC(ap->a_vp)->c_flag |= C_ACCESS;
3043 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
3044 }
3045
3046 /*
3047 * Wrapper for special device writes
3048 */
3049 static int
3050 hfsspec_write(ap)
3051 struct vop_write_args /* {
3052 struct vnode *a_vp;
3053 struct uio *a_uio;
3054 int a_ioflag;
3055 struct ucred *a_cred;
3056 } */ *ap;
3057 {
3058 /*
3059 * Set update and change flags.
3060 */
3061 VTOC(ap->a_vp)->c_flag |= C_CHANGE | C_UPDATE;
3062 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
3063 }
3064
3065 /*
3066 * Wrapper for special device close
3067 *
3068 * Update the times on the cnode then do device close.
3069 */
3070 static int
3071 hfsspec_close(ap)
3072 struct vop_close_args /* {
3073 struct vnode *a_vp;
3074 int a_fflag;
3075 struct ucred *a_cred;
3076 struct proc *a_p;
3077 } */ *ap;
3078 {
3079 struct vnode *vp = ap->a_vp;
3080 struct cnode *cp = VTOC(vp);
3081
3082 simple_lock(&vp->v_interlock);
3083 if (ap->a_vp->v_usecount > 1)
3084 CTIMES(cp, &time, &time);
3085 simple_unlock(&vp->v_interlock);
3086 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
3087 }
3088
3089 #if FIFO
3090 /*
3091 * Wrapper for fifo reads
3092 */
3093 static int
3094 hfsfifo_read(ap)
3095 struct vop_read_args /* {
3096 struct vnode *a_vp;
3097 struct uio *a_uio;
3098 int a_ioflag;
3099 struct ucred *a_cred;
3100 } */ *ap;
3101 {
3102 extern int (**fifo_vnodeop_p)(void *);
3103
3104 /*
3105 * Set access flag.
3106 */
3107 VTOC(ap->a_vp)->c_flag |= C_ACCESS;
3108 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap));
3109 }
3110
3111 /*
3112 * Wrapper for fifo writes
3113 */
3114 static int
3115 hfsfifo_write(ap)
3116 struct vop_write_args /* {
3117 struct vnode *a_vp;
3118 struct uio *a_uio;
3119 int a_ioflag;
3120 struct ucred *a_cred;
3121 } */ *ap;
3122 {
3123 extern int (**fifo_vnodeop_p)(void *);
3124
3125 /*
3126 * Set update and change flags.
3127 */
3128 VTOC(ap->a_vp)->c_flag |= C_CHANGE | C_UPDATE;
3129 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap));
3130 }
3131
3132 /*
3133 * Wrapper for fifo close
3134 *
3135 * Update the times on the cnode then do device close.
3136 */
3137 static int
3138 hfsfifo_close(ap)
3139 struct vop_close_args /* {
3140 struct vnode *a_vp;
3141 int a_fflag;
3142 struct ucred *a_cred;
3143 struct proc *a_p;
3144 } */ *ap;
3145 {
3146 extern int (**fifo_vnodeop_p)(void *);
3147 struct vnode *vp = ap->a_vp;
3148 struct cnode *cp = VTOC(vp);
3149
3150 simple_lock(&vp->v_interlock);
3151 if (ap->a_vp->v_usecount > 1)
3152 CTIMES(cp, &time, &time);
3153 simple_unlock(&vp->v_interlock);
3154 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap));
3155 }
3156 #endif /* FIFO */
3157
3158
3159 /*****************************************************************************
3160 *
3161 * VOP Tables
3162 *
3163 *****************************************************************************/
3164 int hfs_cache_lookup(); /* in hfs_lookup.c */
3165 int hfs_lookup(); /* in hfs_lookup.c */
3166 int hfs_read(); /* in hfs_readwrite.c */
3167 int hfs_write(); /* in hfs_readwrite.c */
3168 int hfs_ioctl(); /* in hfs_readwrite.c */
3169 int hfs_select(); /* in hfs_readwrite.c */
3170 int hfs_bmap(); /* in hfs_readwrite.c */
3171 int hfs_strategy(); /* in hfs_readwrite.c */
3172 int hfs_truncate(); /* in hfs_readwrite.c */
3173 int hfs_allocate(); /* in hfs_readwrite.c */
3174 int hfs_pagein(); /* in hfs_readwrite.c */
3175 int hfs_pageout(); /* in hfs_readwrite.c */
3176 int hfs_search(); /* in hfs_search.c */
3177 int hfs_bwrite(); /* in hfs_readwrite.c */
3178 int hfs_link(); /* in hfs_link.c */
3179 int hfs_blktooff(); /* in hfs_readwrite.c */
3180 int hfs_offtoblk(); /* in hfs_readwrite.c */
3181 int hfs_cmap(); /* in hfs_readwrite.c */
3182 int hfs_getattrlist(); /* in hfs_attrlist.c */
3183 int hfs_setattrlist(); /* in hfs_attrlist.c */
3184 int hfs_readdirattr(); /* in hfs_attrlist.c */
3185 int hfs_inactive(); /* in hfs_cnode.c */
3186 int hfs_reclaim(); /* in hfs_cnode.c */
3187
3188 int (**hfs_vnodeop_p)(void *);
3189
3190 #define VOPFUNC int (*)(void *)
3191
3192 struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
3193 { &vop_default_desc, (VOPFUNC)vn_default_error },
3194 { &vop_lookup_desc, (VOPFUNC)hfs_cache_lookup }, /* lookup */
3195 { &vop_create_desc, (VOPFUNC)hfs_create }, /* create */
3196 { &vop_mknod_desc, (VOPFUNC)hfs_mknod }, /* mknod */
3197 { &vop_open_desc, (VOPFUNC)hfs_open }, /* open */
3198 { &vop_close_desc, (VOPFUNC)hfs_close }, /* close */
3199 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
3200 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
3201 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
3202 { &vop_read_desc, (VOPFUNC)hfs_read }, /* read */
3203 { &vop_write_desc, (VOPFUNC)hfs_write }, /* write */
3204 { &vop_ioctl_desc, (VOPFUNC)hfs_ioctl }, /* ioctl */
3205 { &vop_select_desc, (VOPFUNC)hfs_select }, /* select */
3206 { &vop_exchange_desc, (VOPFUNC)hfs_exchange }, /* exchange */
3207 { &vop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */
3208 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
3209 { &vop_seek_desc, (VOPFUNC)nop_seek }, /* seek */
3210 { &vop_remove_desc, (VOPFUNC)hfs_remove }, /* remove */
3211 { &vop_link_desc, (VOPFUNC)hfs_link }, /* link */
3212 { &vop_rename_desc, (VOPFUNC)hfs_rename }, /* rename */
3213 { &vop_mkdir_desc, (VOPFUNC)hfs_mkdir }, /* mkdir */
3214 { &vop_rmdir_desc, (VOPFUNC)hfs_rmdir }, /* rmdir */
3215 { &vop_mkcomplex_desc, (VOPFUNC)err_mkcomplex }, /* mkcomplex */
3216 { &vop_getattrlist_desc, (VOPFUNC)hfs_getattrlist }, /* getattrlist */
3217 { &vop_setattrlist_desc, (VOPFUNC)hfs_setattrlist }, /* setattrlist */
3218 { &vop_symlink_desc, (VOPFUNC)hfs_symlink }, /* symlink */
3219 { &vop_readdir_desc, (VOPFUNC)hfs_readdir }, /* readdir */
3220 { &vop_readdirattr_desc, (VOPFUNC)hfs_readdirattr }, /* readdirattr */
3221 { &vop_readlink_desc, (VOPFUNC)hfs_readlink }, /* readlink */
3222 { &vop_abortop_desc, (VOPFUNC)hfs_abortop }, /* abortop */
3223 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
3224 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
3225 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
3226 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
3227 { &vop_bmap_desc, (VOPFUNC)hfs_bmap }, /* bmap */
3228 { &vop_strategy_desc, (VOPFUNC)hfs_strategy }, /* strategy */
3229 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
3230 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
3231 { &vop_pathconf_desc, (VOPFUNC)hfs_pathconf }, /* pathconf */
3232 { &vop_advlock_desc, (VOPFUNC)hfs_advlock }, /* advlock */
3233 { &vop_reallocblks_desc, (VOPFUNC)err_reallocblks }, /* reallocblks */
3234 { &vop_truncate_desc, (VOPFUNC)hfs_truncate }, /* truncate */
3235 { &vop_allocate_desc, (VOPFUNC)hfs_allocate }, /* allocate */
3236 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
3237 { &vop_searchfs_desc, (VOPFUNC)hfs_search }, /* search fs */
3238 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite }, /* bwrite */
3239 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* pagein */
3240 { &vop_pageout_desc,(VOPFUNC) hfs_pageout }, /* pageout */
3241 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3242 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
3243 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
3244 { &vop_cmap_desc, (VOPFUNC)hfs_cmap }, /* cmap */
3245 { NULL, (VOPFUNC)NULL }
3246 };
3247
3248 struct vnodeopv_desc hfs_vnodeop_opv_desc =
3249 { &hfs_vnodeop_p, hfs_vnodeop_entries };
3250
3251 int (**hfs_specop_p)(void *);
3252 struct vnodeopv_entry_desc hfs_specop_entries[] = {
3253 { &vop_default_desc, (VOPFUNC)vn_default_error },
3254 { &vop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */
3255 { &vop_create_desc, (VOPFUNC)spec_create }, /* create */
3256 { &vop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */
3257 { &vop_open_desc, (VOPFUNC)spec_open }, /* open */
3258 { &vop_close_desc, (VOPFUNC)hfsspec_close }, /* close */
3259 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
3260 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
3261 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
3262 { &vop_read_desc, (VOPFUNC)hfsspec_read }, /* read */
3263 { &vop_write_desc, (VOPFUNC)hfsspec_write }, /* write */
3264 { &vop_lease_desc, (VOPFUNC)spec_lease_check }, /* lease */
3265 { &vop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */
3266 { &vop_select_desc, (VOPFUNC)spec_select }, /* select */
3267 { &vop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */
3268 { &vop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */
3269 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
3270 { &vop_seek_desc, (VOPFUNC)spec_seek }, /* seek */
3271 { &vop_remove_desc, (VOPFUNC)spec_remove }, /* remove */
3272 { &vop_link_desc, (VOPFUNC)spec_link }, /* link */
3273 { &vop_rename_desc, (VOPFUNC)spec_rename }, /* rename */
3274 { &vop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */
3275 { &vop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */
3276 { &vop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */
3277 { &vop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */
3278 { &vop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */
3279 { &vop_abortop_desc, (VOPFUNC)spec_abortop }, /* abortop */
3280 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
3281 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
3282 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
3283 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
3284 { &vop_bmap_desc, (VOPFUNC)spec_bmap }, /* bmap */
3285 { &vop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */
3286 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
3287 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
3288 { &vop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */
3289 { &vop_advlock_desc, (VOPFUNC)spec_advlock }, /* advlock */
3290 { &vop_blkatoff_desc, (VOPFUNC)spec_blkatoff }, /* blkatoff */
3291 { &vop_valloc_desc, (VOPFUNC)spec_valloc }, /* valloc */
3292 { &vop_reallocblks_desc, (VOPFUNC)spec_reallocblks }, /* reallocblks */
3293 { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */
3294 { &vop_truncate_desc, (VOPFUNC)spec_truncate }, /* truncate */
3295 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
3296 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
3297 { &vop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */
3298 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* Pagein */
3299 { &vop_pageout_desc, (VOPFUNC)hfs_pageout }, /* Pageout */
3300 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3301 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
3302 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
3303 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
3304 };
3305 struct vnodeopv_desc hfs_specop_opv_desc =
3306 { &hfs_specop_p, hfs_specop_entries };
3307
3308 #if FIFO
3309 int (**hfs_fifoop_p)(void *);
3310 struct vnodeopv_entry_desc hfs_fifoop_entries[] = {
3311 { &vop_default_desc, (VOPFUNC)vn_default_error },
3312 { &vop_lookup_desc, (VOPFUNC)fifo_lookup }, /* lookup */
3313 { &vop_create_desc, (VOPFUNC)fifo_create }, /* create */
3314 { &vop_mknod_desc, (VOPFUNC)fifo_mknod }, /* mknod */
3315 { &vop_open_desc, (VOPFUNC)fifo_open }, /* open */
3316 { &vop_close_desc, (VOPFUNC)hfsfifo_close }, /* close */
3317 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
3318 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
3319 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
3320 { &vop_read_desc, (VOPFUNC)hfsfifo_read }, /* read */
3321 { &vop_write_desc, (VOPFUNC)hfsfifo_write }, /* write */
3322 { &vop_lease_desc, (VOPFUNC)fifo_lease_check }, /* lease */
3323 { &vop_ioctl_desc, (VOPFUNC)fifo_ioctl }, /* ioctl */
3324 { &vop_select_desc, (VOPFUNC)fifo_select }, /* select */
3325 { &vop_revoke_desc, (VOPFUNC)fifo_revoke }, /* revoke */
3326 { &vop_mmap_desc, (VOPFUNC)fifo_mmap }, /* mmap */
3327 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
3328 { &vop_seek_desc, (VOPFUNC)fifo_seek }, /* seek */
3329 { &vop_remove_desc, (VOPFUNC)fifo_remove }, /* remove */
3330 { &vop_link_desc, (VOPFUNC)fifo_link }, /* link */
3331 { &vop_rename_desc, (VOPFUNC)fifo_rename }, /* rename */
3332 { &vop_mkdir_desc, (VOPFUNC)fifo_mkdir }, /* mkdir */
3333 { &vop_rmdir_desc, (VOPFUNC)fifo_rmdir }, /* rmdir */
3334 { &vop_symlink_desc, (VOPFUNC)fifo_symlink }, /* symlink */
3335 { &vop_readdir_desc, (VOPFUNC)fifo_readdir }, /* readdir */
3336 { &vop_readlink_desc, (VOPFUNC)fifo_readlink }, /* readlink */
3337 { &vop_abortop_desc, (VOPFUNC)fifo_abortop }, /* abortop */
3338 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
3339 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
3340 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
3341 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
3342 { &vop_bmap_desc, (VOPFUNC)fifo_bmap }, /* bmap */
3343 { &vop_strategy_desc, (VOPFUNC)fifo_strategy }, /* strategy */
3344 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
3345 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
3346 { &vop_pathconf_desc, (VOPFUNC)fifo_pathconf }, /* pathconf */
3347 { &vop_advlock_desc, (VOPFUNC)fifo_advlock }, /* advlock */
3348 { &vop_blkatoff_desc, (VOPFUNC)fifo_blkatoff }, /* blkatoff */
3349 { &vop_valloc_desc, (VOPFUNC)fifo_valloc }, /* valloc */
3350 { &vop_reallocblks_desc, (VOPFUNC)fifo_reallocblks }, /* reallocblks */
3351 { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */
3352 { &vop_truncate_desc, (VOPFUNC)fifo_truncate }, /* truncate */
3353 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
3354 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
3355 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* Pagein */
3356 { &vop_pageout_desc, (VOPFUNC)hfs_pageout }, /* Pageout */
3357 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3358 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
3359 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
3360 { &vop_cmap_desc, (VOPFUNC)hfs_cmap }, /* cmap */
3361 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
3362 };
3363 struct vnodeopv_desc hfs_fifoop_opv_desc =
3364 { &hfs_fifoop_p, hfs_fifoop_entries };
3365 #endif /* FIFO */
3366
3367
3368