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