]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vnops.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vnops.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 #include <sys/file_internal.h>
32 #include <sys/dirent.h>
33 #include <sys/stat.h>
34 #include <sys/buf.h>
35 #include <sys/mount.h>
36 #include <sys/vnode_internal.h>
37 #include <sys/malloc.h>
38 #include <sys/ubc.h>
39 #include <sys/paths.h>
40 #include <sys/quota.h>
41 #include <sys/time.h>
42 #include <sys/disk.h>
43 #include <sys/kauth.h>
44 #include <sys/uio_internal.h>
45
46 #include <miscfs/specfs/specdev.h>
47 #include <miscfs/fifofs/fifo.h>
48 #include <vfs/vfs_support.h>
49 #include <machine/spl.h>
50
51 #include <sys/kdebug.h>
52 #include <sys/sysctl.h>
53
54 #include "hfs.h"
55 #include "hfs_catalog.h"
56 #include "hfs_cnode.h"
57 #include "hfs_dbg.h"
58 #include "hfs_mount.h"
59 #include "hfs_quota.h"
60 #include "hfs_endian.h"
61
62 #include "hfscommon/headers/BTreesInternal.h"
63 #include "hfscommon/headers/FileMgrInternal.h"
64
65 #define MAKE_DELETED_NAME(NAME,FID) \
66 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
67
68 #define KNDETACH_VNLOCKED 0x00000001
69
70 #define CARBON_TEMP_DIR_NAME "Cleanup At Startup"
71
72
73 /* Global vfs data structures for hfs */
74
75 /* Always F_FULLFSYNC? 1=yes,0=no (default due to "various" reasons is 'no') */
76 int always_do_fullfsync = 0;
77 SYSCTL_INT (_kern, OID_AUTO, always_do_fullfsync, CTLFLAG_RW, &always_do_fullfsync, 0, "always F_FULLFSYNC when fsync is called");
78
79 extern unsigned long strtoul(const char *, char **, int);
80
81 static int hfs_makenode(struct vnode *dvp, struct vnode **vpp,
82 struct componentname *cnp, struct vnode_attr *vap,
83 vfs_context_t ctx);
84
85 static int hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, struct proc *p);
86
87 static int hfs_removedir(struct vnode *, struct vnode *, struct componentname *,
88 int);
89
90 static int hfs_removefile(struct vnode *, struct vnode *, struct componentname *,
91 int, int);
92
93 static int hfs_vnop_close(struct vnop_close_args*);
94 static int hfs_vnop_create(struct vnop_create_args*);
95 static int hfs_vnop_exchange(struct vnop_exchange_args*);
96 static int hfs_vnop_fsync(struct vnop_fsync_args*);
97 static int hfs_vnop_mkdir(struct vnop_mkdir_args*);
98 static int hfs_vnop_mknod(struct vnop_mknod_args*);
99 static int hfs_vnop_getattr(struct vnop_getattr_args*);
100 static int hfs_vnop_open(struct vnop_open_args*);
101 static int hfs_vnop_readdir(struct vnop_readdir_args*);
102 static int hfs_vnop_remove(struct vnop_remove_args*);
103 static int hfs_vnop_rename(struct vnop_rename_args*);
104 static int hfs_vnop_rmdir(struct vnop_rmdir_args*);
105 static int hfs_vnop_symlink(struct vnop_symlink_args*);
106 static int hfs_vnop_setattr(struct vnop_setattr_args*);
107
108 /* Options for hfs_removedir and hfs_removefile */
109 #define HFSRM_SKIP_RESERVE 0x01
110
111
112 int hfs_write_access(struct vnode *vp, kauth_cred_t cred, struct proc *p, Boolean considerFlags);
113
114 int hfs_chmod(struct vnode *vp, int mode, kauth_cred_t cred,
115 struct proc *p);
116 int hfs_chown(struct vnode *vp, uid_t uid, gid_t gid,
117 kauth_cred_t cred, struct proc *p);
118
119 /*****************************************************************************
120 *
121 * Common Operations on vnodes
122 *
123 *****************************************************************************/
124
125 /*
126 * Create a regular file.
127 */
128 static int
129 hfs_vnop_create(struct vnop_create_args *ap)
130 {
131 return hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context);
132 }
133
134 /*
135 * Make device special file.
136 */
137 static int
138 hfs_vnop_mknod(struct vnop_mknod_args *ap)
139 {
140 struct vnode_attr *vap = ap->a_vap;
141 struct vnode *dvp = ap->a_dvp;
142 struct vnode **vpp = ap->a_vpp;
143 struct cnode *cp;
144 int error;
145
146 if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord) {
147 return (ENOTSUP);
148 }
149
150 /* Create the vnode */
151 error = hfs_makenode(dvp, vpp, ap->a_cnp, vap, ap->a_context);
152 if (error)
153 return (error);
154
155 cp = VTOC(*vpp);
156 cp->c_touch_acctime = TRUE;
157 cp->c_touch_chgtime = TRUE;
158 cp->c_touch_modtime = TRUE;
159
160 if ((vap->va_rdev != VNOVAL) &&
161 (vap->va_type == VBLK || vap->va_type == VCHR))
162 cp->c_rdev = vap->va_rdev;
163
164 return (0);
165 }
166
167 /*
168 * Open a file/directory.
169 */
170 static int
171 hfs_vnop_open(struct vnop_open_args *ap)
172 {
173 struct vnode *vp = ap->a_vp;
174 struct filefork *fp;
175 struct timeval tv;
176 int error;
177
178 /*
179 * Files marked append-only must be opened for appending.
180 */
181 if ((VTOC(vp)->c_flags & APPEND) && !vnode_isdir(vp) &&
182 (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
183 return (EPERM);
184
185 if (vnode_isreg(vp) && !UBCINFOEXISTS(vp))
186 return (EBUSY); /* file is in use by the kernel */
187
188 /* Don't allow journal file to be opened externally. */
189 if (VTOC(vp)->c_fileid == VTOHFS(vp)->hfs_jnlfileid)
190 return (EPERM);
191 /*
192 * On the first (non-busy) open of a fragmented
193 * file attempt to de-frag it (if its less than 20MB).
194 */
195 if ((VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) ||
196 (VTOHFS(vp)->jnl == NULL) ||
197 !vnode_isreg(vp) || vnode_isinuse(vp, 0)) {
198 return (0);
199 }
200
201 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
202 return (error);
203 fp = VTOF(vp);
204 if (fp->ff_blocks &&
205 fp->ff_extents[7].blockCount != 0 &&
206 fp->ff_size <= (20 * 1024 * 1024)) {
207 /*
208 * Wait until system bootup is done (3 min).
209 */
210 microuptime(&tv);
211 if (tv.tv_sec > (60 * 3)) {
212 (void) hfs_relocate(vp, VTOVCB(vp)->nextAllocation + 4096,
213 vfs_context_ucred(ap->a_context),
214 vfs_context_proc(ap->a_context));
215 }
216 }
217 hfs_unlock(VTOC(vp));
218
219 return (0);
220 }
221
222
223 /*
224 * Close a file/directory.
225 */
226 static int
227 hfs_vnop_close(ap)
228 struct vnop_close_args /* {
229 struct vnode *a_vp;
230 int a_fflag;
231 vfs_context_t a_context;
232 } */ *ap;
233 {
234 register struct vnode *vp = ap->a_vp;
235 register struct cnode *cp;
236 struct proc *p = vfs_context_proc(ap->a_context);
237 struct hfsmount *hfsmp;
238 int busy;
239
240 if ( hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) != 0)
241 return (0);
242 cp = VTOC(vp);
243 hfsmp = VTOHFS(vp);
244
245 // if we froze the fs and we're exiting, then "thaw" the fs
246 if (hfsmp->hfs_freezing_proc == p && proc_exiting(p)) {
247 hfsmp->hfs_freezing_proc = NULL;
248 hfs_global_exclusive_lock_release(hfsmp);
249 lck_rw_unlock_exclusive(&hfsmp->hfs_insync);
250 }
251
252 busy = vnode_isinuse(vp, 1);
253
254 if (busy) {
255 hfs_touchtimes(VTOHFS(vp), cp);
256 }
257 if (vnode_isdir(vp)) {
258 hfs_reldirhints(cp, busy);
259 } else if (vnode_issystem(vp) && !busy) {
260 vnode_recycle(vp);
261 }
262
263 hfs_unlock(cp);
264 return (0);
265 }
266
267 /*
268 * Get basic attributes.
269 */
270 static int
271 hfs_vnop_getattr(struct vnop_getattr_args *ap)
272 {
273 struct vnode *vp = ap->a_vp;
274 struct vnode_attr *vap = ap->a_vap;
275 struct vnode *rvp = NULL;
276 struct hfsmount *hfsmp;
277 struct cnode *cp;
278 enum vtype v_type;
279 int error = 0;
280
281 if ((error = hfs_lock(VTOC(vp), HFS_SHARED_LOCK))) {
282 return (error);
283 }
284 cp = VTOC(vp);
285 hfsmp = VTOHFS(vp);
286 hfs_touchtimes(hfsmp, cp);
287 v_type = vnode_vtype(vp);
288
289 VATTR_RETURN(vap, va_rdev, (v_type == VBLK || v_type == VCHR) ? cp->c_rdev : 0);
290 if (v_type == VDIR) {
291 if (VATTR_IS_ACTIVE(vap, va_nlink)) {
292 int entries;
293
294 entries = cp->c_nlink;
295 if (vnode_isvroot(vp)) {
296 if (hfsmp->hfs_privdir_desc.cd_cnid != 0)
297 --entries; /* hide private dir */
298 if (hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))
299 entries -= 2; /* hide the journal files */
300 }
301 VATTR_RETURN(vap, va_nlink, (uint64_t)entries);
302 }
303
304 if (VATTR_IS_ACTIVE(vap, va_nchildren)) {
305 int entries;
306
307 entries = cp->c_entries;
308 if (vnode_isvroot(vp)) {
309 if (hfsmp->hfs_privdir_desc.cd_cnid != 0)
310 --entries; /* hide private dir */
311 if (hfsmp->jnl)
312 entries -= 2; /* hide the journal files */
313 }
314 VATTR_RETURN(vap, va_nchildren, entries);
315 }
316 } else {
317 VATTR_RETURN(vap, va_nlink, (uint64_t)cp->c_nlink);
318 }
319
320 /* conditional because 64-bit arithmetic can be expensive */
321 if (VATTR_IS_ACTIVE(vap, va_total_size)) {
322 if (v_type == VDIR) {
323 VATTR_RETURN(vap, va_total_size, cp->c_nlink * AVERAGE_HFSDIRENTRY_SIZE);
324 } else {
325 uint64_t total_size = 0;
326 struct cnode *rcp;
327
328 if (cp->c_datafork) {
329 total_size = cp->c_datafork->ff_size;
330 }
331
332 if (cp->c_blocks - VTOF(vp)->ff_blocks) {
333 /* hfs_vgetrsrc does not use struct proc - therefore passing NULL */
334 error = hfs_vgetrsrc(hfsmp, vp, &rvp, NULL);
335 if (error) {
336 goto out;
337 }
338
339 rcp = VTOC(rvp);
340 if (rcp && rcp->c_rsrcfork) {
341 total_size += rcp->c_rsrcfork->ff_size;
342 }
343 }
344
345 VATTR_RETURN(vap, va_total_size, total_size);
346 /* Include size of attibute data (extents), if any */
347 if (cp->c_attrblks) {
348 vap->va_total_size += (uint64_t)cp->c_attrblks * (uint64_t)hfsmp->blockSize;
349 }
350 }
351 }
352 if (VATTR_IS_ACTIVE(vap, va_total_alloc)) {
353 if (v_type == VDIR) {
354 VATTR_RETURN(vap, va_total_alloc, 0);
355 } else {
356 VATTR_RETURN(vap, va_total_alloc, (uint64_t)cp->c_blocks * (uint64_t)hfsmp->blockSize);
357 /* Include size of attibute data (extents), if any */
358 if (cp->c_attrblks) {
359 vap->va_total_alloc += (uint64_t)cp->c_attrblks * (uint64_t)hfsmp->blockSize;
360 }
361 }
362 }
363 /* XXX broken... if ask for "data size" of rsrc fork vp you get rsrc fork size! */
364 if (v_type == VDIR) {
365 VATTR_RETURN(vap, va_data_size, cp->c_nlink * AVERAGE_HFSDIRENTRY_SIZE);
366 } else {
367 VATTR_RETURN(vap, va_data_size, VTOF(vp)->ff_size);
368 }
369 if (VATTR_IS_ACTIVE(vap, va_data_alloc) && (v_type != VDIR)) {
370 /* XXX do we need to account for ff_unallocblocks ? */
371 VATTR_RETURN(vap, va_data_alloc, (uint64_t)VTOF(vp)->ff_blocks * (uint64_t)hfsmp->blockSize);
372 }
373 /* XXX is this really a good 'optimal I/O size'? */
374 VATTR_RETURN(vap, va_iosize, hfsmp->hfs_logBlockSize);
375 VATTR_RETURN(vap, va_uid, cp->c_uid);
376 VATTR_RETURN(vap, va_gid, cp->c_gid);
377 VATTR_RETURN(vap, va_mode, cp->c_mode);
378 #if 0
379 /* XXX is S_IFXATTR still needed ??? */
380 if (VNODE_IS_RSRC(vp))
381 vap->va_mode |= S_IFXATTR;
382 #endif
383 VATTR_RETURN(vap, va_flags, cp->c_flags);
384
385 /*
386 * If the VFS wants extended security data, and we know that we
387 * don't have any (because it never told us it was setting any)
388 * then we can return the supported bit and no data. If we do
389 * have extended security, we can just leave the bit alone and
390 * the VFS will use the fallback path to fetch it.
391 */
392 if (VATTR_IS_ACTIVE(vap, va_acl)) {
393 if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
394 vap->va_acl = KAUTH_FILESEC_NONE;
395 VATTR_SET_SUPPORTED(vap, va_acl);
396 }
397 }
398 vap->va_create_time.tv_sec = cp->c_itime;
399 vap->va_create_time.tv_nsec = 0;
400 VATTR_SET_SUPPORTED(vap, va_create_time);
401
402 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
403 /* Access times are lazyily updated, get current time if needed */
404 if (cp->c_touch_acctime) {
405 struct timeval tv;
406
407 microtime(&tv);
408 vap->va_access_time.tv_sec = tv.tv_sec;
409 } else {
410 vap->va_access_time.tv_sec = cp->c_atime;
411 }
412 vap->va_access_time.tv_nsec = 0;
413 VATTR_SET_SUPPORTED(vap, va_access_time);
414 }
415 vap->va_modify_time.tv_sec = cp->c_mtime;
416 vap->va_modify_time.tv_nsec = 0;
417 VATTR_SET_SUPPORTED(vap, va_modify_time);
418 vap->va_change_time.tv_sec = cp->c_ctime;
419 vap->va_change_time.tv_nsec = 0;
420 VATTR_SET_SUPPORTED(vap, va_change_time);
421 vap->va_backup_time.tv_sec = cp->c_btime;
422 vap->va_backup_time.tv_nsec = 0;
423 VATTR_SET_SUPPORTED(vap, va_backup_time);
424
425 /*
426 * Exporting file IDs from HFS Plus:
427 *
428 * For "normal" files the c_fileid is the same value as the
429 * c_cnid. But for hard link files, they are different - the
430 * c_cnid belongs to the active directory entry (ie the link)
431 * and the c_fileid is for the actual inode (ie the data file).
432 *
433 * The stat call (getattr) uses va_fileid and the Carbon APIs,
434 * which are hardlink-ignorant, will ask for va_linkid.
435 */
436 VATTR_RETURN(vap, va_fileid, (uint64_t)cp->c_fileid);
437 VATTR_RETURN(vap, va_linkid, (uint64_t)cp->c_cnid);
438 VATTR_RETURN(vap, va_parentid, (uint64_t)cp->c_parentcnid);
439 VATTR_RETURN(vap, va_fsid, cp->c_dev);
440 VATTR_RETURN(vap, va_filerev, 0);
441
442 VATTR_RETURN(vap, va_encoding, cp->c_encoding);
443
444 /* if this is the root, let VFS to find out the mount name, which may be different from the real name */
445 if (VATTR_IS_ACTIVE(vap, va_name) && !vnode_isvroot(vp)) {
446 /* Return the name for ATTR_CMN_NAME */
447 if (cp->c_desc.cd_namelen == 0) {
448 error = ENOENT;
449 goto out;
450 }
451
452 strncpy(vap->va_name, cp->c_desc.cd_nameptr, MAXPATHLEN);
453 vap->va_name[MAXPATHLEN-1] = '\0';
454 VATTR_SET_SUPPORTED(vap, va_name);
455 }
456
457 out:
458 hfs_unlock(cp);
459 if (rvp) {
460 vnode_put(rvp);
461 }
462 return (error);
463 }
464
465 static int
466 hfs_vnop_setattr(ap)
467 struct vnop_setattr_args /* {
468 struct vnode *a_vp;
469 struct vnode_attr *a_vap;
470 vfs_context_t a_context;
471 } */ *ap;
472 {
473 struct vnode_attr *vap = ap->a_vap;
474 struct vnode *vp = ap->a_vp;
475 struct cnode *cp = NULL;
476 struct hfsmount *hfsmp;
477 kauth_cred_t cred = vfs_context_ucred(ap->a_context);
478 struct proc *p = vfs_context_proc(ap->a_context);
479 int error = 0;
480 uid_t nuid;
481 gid_t ngid;
482
483 hfsmp = VTOHFS(vp);
484
485 /* Don't allow modification of the journal file. */
486 if (hfsmp->hfs_jnlfileid == VTOC(vp)->c_fileid) {
487 return (EPERM);
488 }
489
490 /*
491 * File size change request.
492 * We are guaranteed that this is not a directory, and that
493 * the filesystem object is writeable.
494 */
495 VATTR_SET_SUPPORTED(vap, va_data_size);
496 if (VATTR_IS_ACTIVE(vap, va_data_size) && !vnode_islnk(vp)) {
497
498 /* Take truncate lock before taking cnode lock. */
499 hfs_lock_truncate(VTOC(vp), TRUE);
500 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
501 hfs_unlock_truncate(VTOC(vp));
502 return (error);
503 }
504 cp = VTOC(vp);
505
506 error = hfs_truncate(vp, vap->va_data_size, vap->va_vaflags & 0xffff, 0, ap->a_context);
507
508 hfs_unlock_truncate(cp);
509 if (error)
510 goto out;
511 }
512 if (cp == NULL) {
513 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
514 return (error);
515 cp = VTOC(vp);
516 }
517
518 /*
519 * Owner/group change request.
520 * We are guaranteed that the new owner/group is valid and legal.
521 */
522 VATTR_SET_SUPPORTED(vap, va_uid);
523 VATTR_SET_SUPPORTED(vap, va_gid);
524 nuid = VATTR_IS_ACTIVE(vap, va_uid) ? vap->va_uid : (uid_t)VNOVAL;
525 ngid = VATTR_IS_ACTIVE(vap, va_gid) ? vap->va_gid : (gid_t)VNOVAL;
526 if (((nuid != (uid_t)VNOVAL) || (ngid != (gid_t)VNOVAL)) &&
527 ((error = hfs_chown(vp, nuid, ngid, cred, p)) != 0))
528 goto out;
529
530 /*
531 * Mode change request.
532 * We are guaranteed that the mode value is valid and that in
533 * conjunction with the owner and group, this change is legal.
534 */
535 VATTR_SET_SUPPORTED(vap, va_mode);
536 if (VATTR_IS_ACTIVE(vap, va_mode) &&
537 ((error = hfs_chmod(vp, (int)vap->va_mode, cred, p)) != 0))
538 goto out;
539
540 /*
541 * File flags change.
542 * We are guaranteed that only flags allowed to change given the
543 * current securelevel are being changed.
544 */
545 VATTR_SET_SUPPORTED(vap, va_flags);
546 if (VATTR_IS_ACTIVE(vap, va_flags)) {
547 cp->c_flags = vap->va_flags;
548 cp->c_touch_chgtime = TRUE;
549 }
550
551 /*
552 * If the file's extended security data is being changed, we
553 * need to note the change. Note that because we don't store
554 * the data, we do not set the SUPPORTED bit; this will cause
555 * the VFS to use a fallback strategy.
556 */
557 if (VATTR_IS_ACTIVE(vap, va_acl)) {
558 /* Remember if any ACL data was set or cleared. */
559 if (vap->va_acl == NULL) {
560 /* being cleared */
561 if (cp->c_attr.ca_recflags & kHFSHasSecurityMask) {
562 cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask;
563 cp->c_touch_chgtime = TRUE;
564 }
565 } else {
566 /* being set */
567 if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
568 cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
569 cp->c_touch_chgtime = TRUE;
570 }
571 }
572 }
573
574 /*
575 * Timestamp updates.
576 */
577 VATTR_SET_SUPPORTED(vap, va_create_time);
578 VATTR_SET_SUPPORTED(vap, va_access_time);
579 VATTR_SET_SUPPORTED(vap, va_modify_time);
580 VATTR_SET_SUPPORTED(vap, va_backup_time);
581 VATTR_SET_SUPPORTED(vap, va_change_time);
582 if (VATTR_IS_ACTIVE(vap, va_create_time) ||
583 VATTR_IS_ACTIVE(vap, va_access_time) ||
584 VATTR_IS_ACTIVE(vap, va_modify_time) ||
585 VATTR_IS_ACTIVE(vap, va_backup_time)) {
586 if (vnode_islnk(vp))
587 goto done;
588 if (VATTR_IS_ACTIVE(vap, va_create_time))
589 cp->c_itime = vap->va_create_time.tv_sec;
590 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
591 cp->c_atime = vap->va_access_time.tv_sec;
592 cp->c_touch_acctime = FALSE;
593 }
594 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
595 cp->c_mtime = vap->va_modify_time.tv_sec;
596 cp->c_touch_modtime = FALSE;
597 cp->c_touch_chgtime = TRUE;
598
599 /*
600 * The utimes system call can reset the modification
601 * time but it doesn't know about HFS create times.
602 * So we need to ensure that the creation time is
603 * always at least as old as the modification time.
604 */
605 if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) &&
606 (cp->c_cnid != kHFSRootFolderID) &&
607 (cp->c_mtime < cp->c_itime)) {
608 cp->c_itime = cp->c_mtime;
609 }
610 }
611 if (VATTR_IS_ACTIVE(vap, va_backup_time))
612 cp->c_btime = vap->va_backup_time.tv_sec;
613 cp->c_flag |= C_MODIFIED;
614 }
615
616 /*
617 * Set name encoding.
618 */
619 VATTR_SET_SUPPORTED(vap, va_encoding);
620 if (VATTR_IS_ACTIVE(vap, va_encoding)) {
621 cp->c_encoding = vap->va_encoding;
622 hfs_setencodingbits(hfsmp, cp->c_encoding);
623 }
624
625 done:
626 if ((error = hfs_update(vp, TRUE)) != 0)
627 goto out;
628 HFS_KNOTE(vp, NOTE_ATTRIB);
629 out:
630 if (cp)
631 hfs_unlock(cp);
632 return (error);
633 }
634
635
636 /*
637 * Change the mode on a file.
638 * cnode must be locked before calling.
639 */
640 __private_extern__
641 int
642 hfs_chmod(struct vnode *vp, int mode, kauth_cred_t cred, struct proc *p)
643 {
644 register struct cnode *cp = VTOC(vp);
645 int error;
646
647 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
648 return (0);
649
650 // XXXdbg - don't allow modification of the journal or journal_info_block
651 if (VTOHFS(vp)->jnl && cp && cp->c_datafork) {
652 struct HFSPlusExtentDescriptor *extd;
653
654 extd = &cp->c_datafork->ff_extents[0];
655 if (extd->startBlock == VTOVCB(vp)->vcbJinfoBlock || extd->startBlock == VTOHFS(vp)->jnl_start) {
656 return EPERM;
657 }
658 }
659
660 #if OVERRIDE_UNKNOWN_PERMISSIONS
661 if (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS) {
662 return (0);
663 };
664 #endif
665 cp->c_mode &= ~ALLPERMS;
666 cp->c_mode |= (mode & ALLPERMS);
667 cp->c_touch_chgtime = TRUE;
668 return (0);
669 }
670
671
672 __private_extern__
673 int
674 hfs_write_access(struct vnode *vp, kauth_cred_t cred, struct proc *p, Boolean considerFlags)
675 {
676 struct cnode *cp = VTOC(vp);
677 int retval = 0;
678 int is_member;
679
680 /*
681 * Disallow write attempts on read-only file systems;
682 * unless the file is a socket, fifo, or a block or
683 * character device resident on the file system.
684 */
685 switch (vnode_vtype(vp)) {
686 case VDIR:
687 case VLNK:
688 case VREG:
689 if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
690 return (EROFS);
691 break;
692 default:
693 break;
694 }
695
696 /* If immutable bit set, nobody gets to write it. */
697 if (considerFlags && (cp->c_flags & IMMUTABLE))
698 return (EPERM);
699
700 /* Otherwise, user id 0 always gets access. */
701 if (!suser(cred, NULL))
702 return (0);
703
704 /* Otherwise, check the owner. */
705 if ((retval = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, false)) == 0)
706 return ((cp->c_mode & S_IWUSR) == S_IWUSR ? 0 : EACCES);
707
708 /* Otherwise, check the groups. */
709 if (kauth_cred_ismember_gid(cred, cp->c_gid, &is_member) == 0 && is_member) {
710 return ((cp->c_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES);
711 }
712
713 /* Otherwise, check everyone else. */
714 return ((cp->c_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES);
715 }
716
717
718 /*
719 * Perform chown operation on cnode cp;
720 * code must be locked prior to call.
721 */
722 __private_extern__
723 int
724 hfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
725 struct proc *p)
726 {
727 register struct cnode *cp = VTOC(vp);
728 uid_t ouid;
729 gid_t ogid;
730 int error = 0;
731 int is_member;
732 #if QUOTA
733 register int i;
734 int64_t change;
735 #endif /* QUOTA */
736
737 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
738 return (ENOTSUP);
739
740 if (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS)
741 return (0);
742
743 if (uid == (uid_t)VNOVAL)
744 uid = cp->c_uid;
745 if (gid == (gid_t)VNOVAL)
746 gid = cp->c_gid;
747
748 #if 0 /* we are guaranteed that this is already the case */
749 /*
750 * If we don't own the file, are trying to change the owner
751 * of the file, or are not a member of the target group,
752 * the caller must be superuser or the call fails.
753 */
754 if ((kauth_cred_getuid(cred) != cp->c_uid || uid != cp->c_uid ||
755 (gid != cp->c_gid &&
756 (kauth_cred_ismember_gid(cred, gid, &is_member) || !is_member))) &&
757 (error = suser(cred, 0)))
758 return (error);
759 #endif
760
761 ogid = cp->c_gid;
762 ouid = cp->c_uid;
763 #if QUOTA
764 if ((error = hfs_getinoquota(cp)))
765 return (error);
766 if (ouid == uid) {
767 dqrele(cp->c_dquot[USRQUOTA]);
768 cp->c_dquot[USRQUOTA] = NODQUOT;
769 }
770 if (ogid == gid) {
771 dqrele(cp->c_dquot[GRPQUOTA]);
772 cp->c_dquot[GRPQUOTA] = NODQUOT;
773 }
774
775 /*
776 * Eventually need to account for (fake) a block per directory
777 * if (vnode_isdir(vp))
778 * change = VTOHFS(vp)->blockSize;
779 * else
780 */
781
782 change = (int64_t)(cp->c_blocks) * (int64_t)VTOVCB(vp)->blockSize;
783 (void) hfs_chkdq(cp, -change, cred, CHOWN);
784 (void) hfs_chkiq(cp, -1, cred, CHOWN);
785 for (i = 0; i < MAXQUOTAS; i++) {
786 dqrele(cp->c_dquot[i]);
787 cp->c_dquot[i] = NODQUOT;
788 }
789 #endif /* QUOTA */
790 cp->c_gid = gid;
791 cp->c_uid = uid;
792 #if QUOTA
793 if ((error = hfs_getinoquota(cp)) == 0) {
794 if (ouid == uid) {
795 dqrele(cp->c_dquot[USRQUOTA]);
796 cp->c_dquot[USRQUOTA] = NODQUOT;
797 }
798 if (ogid == gid) {
799 dqrele(cp->c_dquot[GRPQUOTA]);
800 cp->c_dquot[GRPQUOTA] = NODQUOT;
801 }
802 if ((error = hfs_chkdq(cp, change, cred, CHOWN)) == 0) {
803 if ((error = hfs_chkiq(cp, 1, cred, CHOWN)) == 0)
804 goto good;
805 else
806 (void) hfs_chkdq(cp, -change, cred, CHOWN|FORCE);
807 }
808 for (i = 0; i < MAXQUOTAS; i++) {
809 dqrele(cp->c_dquot[i]);
810 cp->c_dquot[i] = NODQUOT;
811 }
812 }
813 cp->c_gid = ogid;
814 cp->c_uid = ouid;
815 if (hfs_getinoquota(cp) == 0) {
816 if (ouid == uid) {
817 dqrele(cp->c_dquot[USRQUOTA]);
818 cp->c_dquot[USRQUOTA] = NODQUOT;
819 }
820 if (ogid == gid) {
821 dqrele(cp->c_dquot[GRPQUOTA]);
822 cp->c_dquot[GRPQUOTA] = NODQUOT;
823 }
824 (void) hfs_chkdq(cp, change, cred, FORCE|CHOWN);
825 (void) hfs_chkiq(cp, 1, cred, FORCE|CHOWN);
826 (void) hfs_getinoquota(cp);
827 }
828 return (error);
829 good:
830 if (hfs_getinoquota(cp))
831 panic("hfs_chown: lost quota");
832 #endif /* QUOTA */
833
834 if (ouid != uid || ogid != gid)
835 cp->c_touch_chgtime = TRUE;
836 return (0);
837 }
838
839
840 /*
841 * The hfs_exchange routine swaps the fork data in two files by
842 * exchanging some of the information in the cnode. It is used
843 * to preserve the file ID when updating an existing file, in
844 * case the file is being tracked through its file ID. Typically
845 * its used after creating a new file during a safe-save.
846 */
847 static int
848 hfs_vnop_exchange(ap)
849 struct vnop_exchange_args /* {
850 struct vnode *a_fvp;
851 struct vnode *a_tvp;
852 int a_options;
853 vfs_context_t a_context;
854 } */ *ap;
855 {
856 struct vnode *from_vp = ap->a_fvp;
857 struct vnode *to_vp = ap->a_tvp;
858 struct cnode *from_cp;
859 struct cnode *to_cp;
860 struct hfsmount *hfsmp;
861 struct cat_desc tempdesc;
862 struct cat_attr tempattr;
863 int lockflags;
864 int error = 0, started_tr = 0, got_cookie = 0;
865 cat_cookie_t cookie;
866
867 /* The files must be on the same volume. */
868 if (vnode_mount(from_vp) != vnode_mount(to_vp))
869 return (EXDEV);
870
871 if (from_vp == to_vp)
872 return (EINVAL);
873
874 if ((error = hfs_lockpair(VTOC(from_vp), VTOC(to_vp), HFS_EXCLUSIVE_LOCK)))
875 return (error);
876
877 from_cp = VTOC(from_vp);
878 to_cp = VTOC(to_vp);
879 hfsmp = VTOHFS(from_vp);
880
881 /* Only normal files can be exchanged. */
882 if (!vnode_isreg(from_vp) || !vnode_isreg(to_vp) ||
883 (from_cp->c_flag & C_HARDLINK) || (to_cp->c_flag & C_HARDLINK) ||
884 VNODE_IS_RSRC(from_vp) || VNODE_IS_RSRC(to_vp)) {
885 error = EINVAL;
886 goto exit;
887 }
888
889 // XXXdbg - don't allow modification of the journal or journal_info_block
890 if (hfsmp->jnl) {
891 struct HFSPlusExtentDescriptor *extd;
892
893 if (from_cp->c_datafork) {
894 extd = &from_cp->c_datafork->ff_extents[0];
895 if (extd->startBlock == VTOVCB(from_vp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
896 error = EPERM;
897 goto exit;
898 }
899 }
900
901 if (to_cp->c_datafork) {
902 extd = &to_cp->c_datafork->ff_extents[0];
903 if (extd->startBlock == VTOVCB(to_vp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
904 error = EPERM;
905 goto exit;
906 }
907 }
908 }
909
910 if ((error = hfs_start_transaction(hfsmp)) != 0) {
911 goto exit;
912 }
913 started_tr = 1;
914
915 /*
916 * Reserve some space in the Catalog file.
917 */
918 bzero(&cookie, sizeof(cookie));
919 if ((error = cat_preflight(hfsmp, CAT_EXCHANGE, &cookie, vfs_context_proc(ap->a_context)))) {
920 goto exit;
921 }
922 got_cookie = 1;
923
924 /* The backend code always tries to delete the virtual
925 * extent id for exchanging files so we neeed to lock
926 * the extents b-tree.
927 */
928 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
929
930 /* Do the exchange */
931 error = ExchangeFileIDs(hfsmp,
932 from_cp->c_desc.cd_nameptr,
933 to_cp->c_desc.cd_nameptr,
934 from_cp->c_parentcnid,
935 to_cp->c_parentcnid,
936 from_cp->c_hint,
937 to_cp->c_hint);
938 hfs_systemfile_unlock(hfsmp, lockflags);
939
940 /*
941 * Note that we don't need to exchange any extended attributes
942 * since the attributes are keyed by file ID.
943 */
944
945 if (error != E_NONE) {
946 error = MacToVFSError(error);
947 goto exit;
948 }
949
950 /* Purge the vnodes from the name cache */
951 if (from_vp)
952 cache_purge(from_vp);
953 if (to_vp)
954 cache_purge(to_vp);
955
956 /* Save a copy of from attributes before swapping. */
957 bcopy(&from_cp->c_desc, &tempdesc, sizeof(struct cat_desc));
958 bcopy(&from_cp->c_attr, &tempattr, sizeof(struct cat_attr));
959
960 /*
961 * Swap the descriptors and all non-fork related attributes.
962 * (except the modify date)
963 */
964 bcopy(&to_cp->c_desc, &from_cp->c_desc, sizeof(struct cat_desc));
965
966 from_cp->c_hint = 0;
967 from_cp->c_fileid = from_cp->c_cnid;
968 from_cp->c_itime = to_cp->c_itime;
969 from_cp->c_btime = to_cp->c_btime;
970 from_cp->c_atime = to_cp->c_atime;
971 from_cp->c_ctime = to_cp->c_ctime;
972 from_cp->c_gid = to_cp->c_gid;
973 from_cp->c_uid = to_cp->c_uid;
974 from_cp->c_flags = to_cp->c_flags;
975 from_cp->c_mode = to_cp->c_mode;
976 from_cp->c_attr.ca_recflags = to_cp->c_attr.ca_recflags;
977 bcopy(to_cp->c_finderinfo, from_cp->c_finderinfo, 32);
978
979 bcopy(&tempdesc, &to_cp->c_desc, sizeof(struct cat_desc));
980 to_cp->c_hint = 0;
981 to_cp->c_fileid = to_cp->c_cnid;
982 to_cp->c_itime = tempattr.ca_itime;
983 to_cp->c_btime = tempattr.ca_btime;
984 to_cp->c_atime = tempattr.ca_atime;
985 to_cp->c_ctime = tempattr.ca_ctime;
986 to_cp->c_gid = tempattr.ca_gid;
987 to_cp->c_uid = tempattr.ca_uid;
988 to_cp->c_flags = tempattr.ca_flags;
989 to_cp->c_mode = tempattr.ca_mode;
990 to_cp->c_attr.ca_recflags = tempattr.ca_recflags;
991 bcopy(tempattr.ca_finderinfo, to_cp->c_finderinfo, 32);
992
993 /* Rehash the cnodes using their new file IDs */
994 hfs_chash_rehash(from_cp, to_cp);
995
996 /*
997 * When a file moves out of "Cleanup At Startup"
998 * we can drop its NODUMP status.
999 */
1000 if ((from_cp->c_flags & UF_NODUMP) &&
1001 (from_cp->c_parentcnid != to_cp->c_parentcnid)) {
1002 from_cp->c_flags &= ~UF_NODUMP;
1003 from_cp->c_touch_chgtime = TRUE;
1004 }
1005 if ((to_cp->c_flags & UF_NODUMP) &&
1006 (to_cp->c_parentcnid != from_cp->c_parentcnid)) {
1007 to_cp->c_flags &= ~UF_NODUMP;
1008 to_cp->c_touch_chgtime = TRUE;
1009 }
1010
1011 HFS_KNOTE(from_vp, NOTE_ATTRIB);
1012 HFS_KNOTE(to_vp, NOTE_ATTRIB);
1013
1014 exit:
1015 if (got_cookie) {
1016 cat_postflight(hfsmp, &cookie, vfs_context_proc(ap->a_context));
1017 }
1018 if (started_tr) {
1019 hfs_end_transaction(hfsmp);
1020 }
1021
1022 hfs_unlockpair(from_cp, to_cp);
1023 return (error);
1024 }
1025
1026
1027 /*
1028 * cnode must be locked
1029 */
1030 __private_extern__
1031 int
1032 hfs_fsync(struct vnode *vp, int waitfor, int fullsync, struct proc *p)
1033 {
1034 struct cnode *cp = VTOC(vp);
1035 struct filefork *fp = NULL;
1036 int retval = 0;
1037 struct hfsmount *hfsmp = VTOHFS(vp);
1038 struct timeval tv;
1039 int wait;
1040 int lockflag;
1041 int took_trunc_lock = 0;
1042
1043 wait = (waitfor == MNT_WAIT);
1044
1045 /* HFS directories don't have any data blocks. */
1046 if (vnode_isdir(vp))
1047 goto metasync;
1048
1049 /*
1050 * For system files flush the B-tree header and
1051 * for regular files write out any clusters
1052 */
1053 if (vnode_issystem(vp)) {
1054 if (VTOF(vp)->fcbBTCBPtr != NULL) {
1055 // XXXdbg
1056 if (hfsmp->jnl == NULL) {
1057 BTFlushPath(VTOF(vp));
1058 }
1059 }
1060 } else if (UBCINFOEXISTS(vp)) {
1061 hfs_unlock(cp);
1062 hfs_lock_truncate(cp, TRUE);
1063 took_trunc_lock = 1;
1064
1065 /* Don't hold cnode lock when calling into cluster layer. */
1066 (void) cluster_push(vp, 0);
1067
1068 hfs_lock(cp, HFS_FORCE_LOCK);
1069 }
1070 /*
1071 * When MNT_WAIT is requested and the zero fill timeout
1072 * has expired then we must explicitly zero out any areas
1073 * that are currently marked invalid (holes).
1074 *
1075 * Files with NODUMP can bypass zero filling here.
1076 */
1077 if ((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
1078 ((cp->c_flags & UF_NODUMP) == 0) &&
1079 UBCINFOEXISTS(vp) && (fp = VTOF(vp)) &&
1080 cp->c_zftimeout != 0) {
1081 microuptime(&tv);
1082 if (tv.tv_sec < cp->c_zftimeout) {
1083 /* Remember that a force sync was requested. */
1084 cp->c_flag |= C_ZFWANTSYNC;
1085 goto datasync;
1086 }
1087 if (!took_trunc_lock) {
1088 hfs_unlock(cp);
1089 hfs_lock_truncate(cp, TRUE);
1090 hfs_lock(cp, HFS_FORCE_LOCK);
1091 took_trunc_lock = 1;
1092 }
1093
1094 while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) {
1095 struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges);
1096 off_t start = invalid_range->rl_start;
1097 off_t end = invalid_range->rl_end;
1098
1099 /* The range about to be written must be validated
1100 * first, so that VNOP_BLOCKMAP() will return the
1101 * appropriate mapping for the cluster code:
1102 */
1103 rl_remove(start, end, &fp->ff_invalidranges);
1104
1105 /* Don't hold cnode lock when calling into cluster layer. */
1106 hfs_unlock(cp);
1107 (void) cluster_write(vp, (struct uio *) 0,
1108 fp->ff_size, end + 1, start, (off_t)0,
1109 IO_HEADZEROFILL | IO_NOZERODIRTY | IO_NOCACHE);
1110 hfs_lock(cp, HFS_FORCE_LOCK);
1111 cp->c_flag |= C_MODIFIED;
1112 }
1113 hfs_unlock(cp);
1114 (void) cluster_push(vp, 0);
1115 hfs_lock(cp, HFS_FORCE_LOCK);
1116
1117 cp->c_flag &= ~C_ZFWANTSYNC;
1118 cp->c_zftimeout = 0;
1119 }
1120 datasync:
1121 if (took_trunc_lock)
1122 hfs_unlock_truncate(cp);
1123
1124 /*
1125 * if we have a journal and if journal_active() returns != 0 then the
1126 * we shouldn't do anything to a locked block (because it is part
1127 * of a transaction). otherwise we'll just go through the normal
1128 * code path and flush the buffer. note journal_active() can return
1129 * -1 if the journal is invalid -- however we still need to skip any
1130 * locked blocks as they get cleaned up when we finish the transaction
1131 * or close the journal.
1132 */
1133 // if (hfsmp->jnl && journal_active(hfsmp->jnl) >= 0)
1134 if (hfsmp->jnl)
1135 lockflag = BUF_SKIP_LOCKED;
1136 else
1137 lockflag = 0;
1138
1139 /*
1140 * Flush all dirty buffers associated with a vnode.
1141 */
1142 buf_flushdirtyblks(vp, wait, lockflag, "hfs_fsync");
1143
1144 metasync:
1145 if (vnode_isreg(vp) && vnode_issystem(vp)) {
1146 if (VTOF(vp)->fcbBTCBPtr != NULL) {
1147 microuptime(&tv);
1148 BTSetLastSync(VTOF(vp), tv.tv_sec);
1149 }
1150 cp->c_touch_acctime = FALSE;
1151 cp->c_touch_chgtime = FALSE;
1152 cp->c_touch_modtime = FALSE;
1153 } else if ( !(vp->v_flag & VSWAP) ) /* User file */ {
1154 retval = hfs_update(vp, wait);
1155
1156 /* When MNT_WAIT is requested push out any delayed meta data */
1157 if ((retval == 0) && wait && cp->c_hint &&
1158 !ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
1159 hfs_metasync(VTOHFS(vp), (daddr64_t)cp->c_hint, p);
1160 }
1161
1162 // make sure that we've really been called from the user
1163 // fsync() and if so push out any pending transactions
1164 // that this file might is a part of (and get them on
1165 // stable storage).
1166 if (fullsync || always_do_fullfsync) {
1167 if (hfsmp->jnl) {
1168 journal_flush(hfsmp->jnl);
1169 } else {
1170 /* XXX need to pass context! */
1171 VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, NULL);
1172 }
1173 }
1174 }
1175
1176 return (retval);
1177 }
1178
1179
1180 /* Sync an hfs catalog b-tree node */
1181 static int
1182 hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, struct proc *p)
1183 {
1184 vnode_t vp;
1185 buf_t bp;
1186 int lockflags;
1187
1188 vp = HFSTOVCB(hfsmp)->catalogRefNum;
1189
1190 // XXXdbg - don't need to do this on a journaled volume
1191 if (hfsmp->jnl) {
1192 return 0;
1193 }
1194
1195 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1196 /*
1197 * Look for a matching node that has been delayed
1198 * but is not part of a set (B_LOCKED).
1199 *
1200 * BLK_ONLYVALID causes buf_getblk to return a
1201 * buf_t for the daddr64_t specified only if it's
1202 * currently resident in the cache... the size
1203 * parameter to buf_getblk is ignored when this flag
1204 * is set
1205 */
1206 bp = buf_getblk(vp, node, 0, 0, 0, BLK_META | BLK_ONLYVALID);
1207
1208 if (bp) {
1209 if ((buf_flags(bp) & (B_LOCKED | B_DELWRI)) == B_DELWRI)
1210 (void) VNOP_BWRITE(bp);
1211 else
1212 buf_brelse(bp);
1213 }
1214
1215 hfs_systemfile_unlock(hfsmp, lockflags);
1216
1217 return (0);
1218 }
1219
1220
1221 /*ARGSUSED 1*/
1222 static int
1223 hfs_btsync_callback(struct buf *bp, void *dummy)
1224 {
1225 buf_clearflags(bp, B_LOCKED);
1226 (void) buf_bawrite(bp);
1227
1228 return(BUF_CLAIMED);
1229 }
1230
1231
1232 __private_extern__
1233 int
1234 hfs_btsync(struct vnode *vp, int sync_transaction)
1235 {
1236 struct cnode *cp = VTOC(vp);
1237 struct timeval tv;
1238 int flags = 0;
1239
1240 if (sync_transaction)
1241 flags |= BUF_SKIP_NONLOCKED;
1242 /*
1243 * Flush all dirty buffers associated with b-tree.
1244 */
1245 buf_iterate(vp, hfs_btsync_callback, flags, 0);
1246
1247 microuptime(&tv);
1248 if (vnode_issystem(vp) && (VTOF(vp)->fcbBTCBPtr != NULL))
1249 (void) BTSetLastSync(VTOF(vp), tv.tv_sec);
1250 cp->c_touch_acctime = FALSE;
1251 cp->c_touch_chgtime = FALSE;
1252 cp->c_touch_modtime = FALSE;
1253
1254 return 0;
1255 }
1256
1257 /*
1258 * Remove a directory.
1259 */
1260 static int
1261 hfs_vnop_rmdir(ap)
1262 struct vnop_rmdir_args /* {
1263 struct vnode *a_dvp;
1264 struct vnode *a_vp;
1265 struct componentname *a_cnp;
1266 vfs_context_t a_context;
1267 } */ *ap;
1268 {
1269 struct vnode *dvp = ap->a_dvp;
1270 struct vnode *vp = ap->a_vp;
1271 int error;
1272
1273 if (!vnode_isdir(vp)) {
1274 return (ENOTDIR);
1275 }
1276 if (dvp == vp) {
1277 return (EINVAL);
1278 }
1279 if ((error = hfs_lockpair(VTOC(dvp), VTOC(vp), HFS_EXCLUSIVE_LOCK)))
1280 return (error);
1281
1282 error = hfs_removedir(dvp, vp, ap->a_cnp, 0);
1283
1284 hfs_unlockpair(VTOC(dvp), VTOC(vp));
1285
1286 return (error);
1287 }
1288
1289 /*
1290 * Remove a directory
1291 *
1292 * Both dvp and vp cnodes are locked
1293 */
1294 static int
1295 hfs_removedir(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
1296 int skip_reserve)
1297 {
1298 vfs_context_t ctx = cnp->cn_context;
1299 struct proc *p = vfs_context_proc(ctx);
1300 struct cnode *cp;
1301 struct cnode *dcp;
1302 struct hfsmount * hfsmp;
1303 struct cat_desc desc;
1304 cat_cookie_t cookie;
1305 int lockflags;
1306 int error = 0, started_tr = 0, got_cookie = 0;
1307
1308 cp = VTOC(vp);
1309 dcp = VTOC(dvp);
1310 hfsmp = VTOHFS(vp);
1311
1312 if (dcp == cp)
1313 return (EINVAL); /* cannot remove "." */
1314
1315 #if QUOTA
1316 (void)hfs_getinoquota(cp);
1317 #endif
1318 if ((error = hfs_start_transaction(hfsmp)) != 0) {
1319 goto out;
1320 }
1321 started_tr = 1;
1322
1323 if (!skip_reserve) {
1324 /*
1325 * Reserve some space in the Catalog file.
1326 */
1327 bzero(&cookie, sizeof(cookie));
1328 if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
1329 goto out;
1330 }
1331 got_cookie = 1;
1332 }
1333
1334 /*
1335 * Verify the directory is empty (and valid).
1336 * (Rmdir ".." won't be valid since
1337 * ".." will contain a reference to
1338 * the current directory and thus be
1339 * non-empty.)
1340 */
1341 if (cp->c_entries != 0) {
1342 error = ENOTEMPTY;
1343 goto out;
1344 }
1345 if ((dcp->c_flags & APPEND) || (cp->c_flags & (IMMUTABLE | APPEND))) {
1346 error = EPERM;
1347 goto out;
1348 }
1349
1350 if (cp->c_entries > 0)
1351 panic("hfs_rmdir: attempting to delete a non-empty directory!");
1352
1353 /* Remove the entry from the namei cache: */
1354 cache_purge(vp);
1355
1356 /*
1357 * Protect against a race with rename by using the component
1358 * name passed in and parent id from dvp (instead of using
1359 * the cp->c_desc which may have changed).
1360 */
1361 bzero(&desc, sizeof(desc));
1362 desc.cd_nameptr = cnp->cn_nameptr;
1363 desc.cd_namelen = cnp->cn_namelen;
1364 desc.cd_parentcnid = dcp->c_cnid;
1365 desc.cd_cnid = cp->c_cnid;
1366
1367 /* Remove entry from catalog */
1368 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1369 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1370 if (error == 0) {
1371 /* Delete any attributes, ignore errors */
1372 (void) hfs_removeallattr(hfsmp, cp->c_fileid);
1373 }
1374 hfs_systemfile_unlock(hfsmp, lockflags);
1375
1376 if (error)
1377 goto out;
1378
1379 #if QUOTA
1380 (void)hfs_chkiq(cp, -1, NOCRED, 0);
1381 #endif /* QUOTA */
1382
1383 /* The parent lost a child */
1384 if (dcp->c_entries > 0)
1385 dcp->c_entries--;
1386 if (dcp->c_nlink > 0)
1387 dcp->c_nlink--;
1388 dcp->c_touch_chgtime = TRUE;
1389 dcp->c_touch_modtime = TRUE;
1390
1391 dcp->c_flag |= C_FORCEUPDATE; // XXXdbg - don't screw around, force this guy out
1392
1393 (void) hfs_update(dvp, 0);
1394 HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
1395
1396 hfs_volupdate(hfsmp, VOL_RMDIR, (dcp->c_cnid == kHFSRootFolderID));
1397
1398 cp->c_mode = 0; /* Makes the vnode go away...see inactive */
1399 cp->c_flag |= C_NOEXISTS;
1400 out:
1401 HFS_KNOTE(vp, NOTE_DELETE);
1402
1403 if (got_cookie) {
1404 cat_postflight(hfsmp, &cookie, p);
1405 }
1406 if (started_tr) {
1407 hfs_end_transaction(hfsmp);
1408 }
1409
1410 return (error);
1411 }
1412
1413
1414 /*
1415 * Remove a file or link.
1416 */
1417 static int
1418 hfs_vnop_remove(ap)
1419 struct vnop_remove_args /* {
1420 struct vnode *a_dvp;
1421 struct vnode *a_vp;
1422 struct componentname *a_cnp;
1423 int a_flags;
1424 vfs_context_t a_context;
1425 } */ *ap;
1426 {
1427 struct vnode *dvp = ap->a_dvp;
1428 struct vnode *vp = ap->a_vp;
1429 int error;
1430
1431 if (dvp == vp) {
1432 return (EINVAL);
1433 }
1434
1435 hfs_lock_truncate(VTOC(vp), TRUE);
1436
1437 if ((error = hfs_lockpair(VTOC(dvp), VTOC(vp), HFS_EXCLUSIVE_LOCK)))
1438 goto out;
1439
1440 error = hfs_removefile(dvp, vp, ap->a_cnp, ap->a_flags, 0);
1441
1442 hfs_unlockpair(VTOC(dvp), VTOC(vp));
1443 out:
1444 hfs_unlock_truncate(VTOC(vp));
1445 return (error);
1446 }
1447
1448
1449 static int
1450 hfs_removefile_callback(struct buf *bp, void *hfsmp) {
1451
1452 if ( !(buf_flags(bp) & B_META))
1453 panic("hfs: symlink bp @ 0x%x is not marked meta-data!\n", bp);
1454 /*
1455 * it's part of the current transaction, kill it.
1456 */
1457 journal_kill_block(((struct hfsmount *)hfsmp)->jnl, bp);
1458
1459 return (BUF_CLAIMED);
1460 }
1461
1462 /*
1463 * hfs_removefile
1464 *
1465 * Similar to hfs_vnop_remove except there are additional options.
1466 *
1467 * Requires cnode and truncate locks to be held.
1468 */
1469 static int
1470 hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
1471 int flags, int skip_reserve)
1472 {
1473 struct vnode *rvp = NULL;
1474 struct cnode *cp;
1475 struct cnode *dcp;
1476 struct hfsmount *hfsmp;
1477 struct cat_desc desc;
1478 struct timeval tv;
1479 vfs_context_t ctx = cnp->cn_context;
1480 int dataforkbusy = 0;
1481 int rsrcforkbusy = 0;
1482 int truncated = 0;
1483 cat_cookie_t cookie;
1484 int lockflags;
1485 int error = 0;
1486 int started_tr = 0, got_cookie = 0;
1487 int isbigfile = 0;
1488 cnid_t real_cnid = 0;
1489
1490 /* Directories should call hfs_rmdir! */
1491 if (vnode_isdir(vp)) {
1492 return (EISDIR);
1493 }
1494
1495 cp = VTOC(vp);
1496 dcp = VTOC(dvp);
1497 hfsmp = VTOHFS(vp);
1498
1499 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
1500 return 0;
1501 }
1502
1503 // if it's not a hardlink, check that the parent
1504 // cnid is the same as the directory cnid
1505 if ( (cp->c_flag & C_HARDLINK) == 0
1506 && (cp->c_parentcnid != hfsmp->hfs_privdir_desc.cd_cnid)
1507 && (cp->c_parentcnid != dcp->c_cnid)) {
1508 error = EINVAL;
1509 goto out;
1510 }
1511
1512 /* Make sure a remove is permitted */
1513 if (VNODE_IS_RSRC(vp)) {
1514 error = EPERM;
1515 goto out;
1516 }
1517
1518 /*
1519 * Aquire a vnode for a non-empty resource fork.
1520 * (needed for hfs_truncate)
1521 */
1522 if (cp->c_blocks - VTOF(vp)->ff_blocks) {
1523 error = hfs_vgetrsrc(hfsmp, vp, &rvp, 0);
1524 if (error)
1525 goto out;
1526 }
1527
1528 // XXXdbg - don't allow deleting the journal or journal_info_block
1529 if (hfsmp->jnl && cp->c_datafork) {
1530 struct HFSPlusExtentDescriptor *extd;
1531
1532 extd = &cp->c_datafork->ff_extents[0];
1533 if (extd->startBlock == HFSTOVCB(hfsmp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
1534 error = EPERM;
1535 goto out;
1536 }
1537 }
1538
1539 /*
1540 * Check if this file is being used.
1541 */
1542 if (vnode_isinuse(vp, 0))
1543 dataforkbusy = 1;
1544 if (rvp && vnode_isinuse(rvp, 0))
1545 rsrcforkbusy = 1;
1546
1547 // need this to check if we have to break the deletion
1548 // into multiple pieces
1549 isbigfile = (VTOC(vp)->c_datafork->ff_size >= HFS_BIGFILE_SIZE);
1550
1551 /*
1552 * Carbon semantics prohibit deleting busy files.
1553 * (enforced when VNODE_REMOVE_NODELETEBUSY is requested)
1554 */
1555 if (dataforkbusy || rsrcforkbusy) {
1556 if ((flags & VNODE_REMOVE_NODELETEBUSY) ||
1557 (hfsmp->hfs_privdir_desc.cd_cnid == 0)) {
1558 error = EBUSY;
1559 goto out;
1560 }
1561 }
1562
1563 #if QUOTA
1564 (void)hfs_getinoquota(cp);
1565 #endif /* QUOTA */
1566
1567 /*
1568 * We do the ubc_setsize before the hfs_truncate
1569 * since we'll be inside a transaction.
1570 */
1571 if ((cp->c_flag & C_HARDLINK) == 0 &&
1572 (!dataforkbusy || !rsrcforkbusy)) {
1573 /*
1574 * A ubc_setsize can cause a pagein here
1575 * so we need to the drop cnode lock. Note
1576 * that we still hold the truncate lock.
1577 */
1578 hfs_unlock(cp);
1579 if (!dataforkbusy && cp->c_datafork->ff_blocks && !isbigfile) {
1580 ubc_setsize(vp, 0);
1581 }
1582 if (!rsrcforkbusy && rvp) {
1583 ubc_setsize(rvp, 0);
1584 }
1585 hfs_lock(cp, HFS_FORCE_LOCK);
1586 } else {
1587 struct cat_desc cndesc;
1588
1589 // for hard links, re-lookup the name that was passed
1590 // in so we get the correct cnid for the name (as
1591 // opposed to the c_cnid in the cnode which could have
1592 // been changed before this node got locked).
1593 bzero(&cndesc, sizeof(cndesc));
1594 cndesc.cd_nameptr = cnp->cn_nameptr;
1595 cndesc.cd_namelen = cnp->cn_namelen;
1596 cndesc.cd_parentcnid = VTOC(dvp)->c_cnid;
1597 cndesc.cd_hint = VTOC(dvp)->c_childhint;
1598
1599 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1600
1601 if (cat_lookup(hfsmp, &cndesc, 0, NULL, NULL, NULL, &real_cnid) != 0) {
1602 hfs_systemfile_unlock(hfsmp, lockflags);
1603 error = ENOENT;
1604 goto out;
1605 }
1606
1607 hfs_systemfile_unlock(hfsmp, lockflags);
1608 }
1609
1610 if ((error = hfs_start_transaction(hfsmp)) != 0) {
1611 goto out;
1612 }
1613 started_tr = 1;
1614
1615 if (!skip_reserve) {
1616 /*
1617 * Reserve some space in the Catalog file.
1618 */
1619 if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, 0))) {
1620 goto out;
1621 }
1622 got_cookie = 1;
1623 }
1624
1625 /* Remove our entry from the namei cache. */
1626 cache_purge(vp);
1627
1628 // XXXdbg - if we're journaled, kill any dirty symlink buffers
1629 if (hfsmp->jnl && vnode_islnk(vp))
1630 buf_iterate(vp, hfs_removefile_callback, BUF_SKIP_NONLOCKED, (void *)hfsmp);
1631
1632 /*
1633 * Truncate any non-busy forks. Busy forks will
1634 * get trucated when their vnode goes inactive.
1635 *
1636 * Since we're already inside a transaction,
1637 * tell hfs_truncate to skip the ubc_setsize.
1638 *
1639 * (Note: hard links are truncated in VOP_INACTIVE)
1640 */
1641 if ((cp->c_flag & C_HARDLINK) == 0) {
1642 int mode = cp->c_mode;
1643
1644 if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0) {
1645 cp->c_mode = 0; /* Suppress hfs_update */
1646 error = hfs_truncate(vp, (off_t)0, IO_NDELAY, 1, ctx);
1647 cp->c_mode = mode;
1648 if (error)
1649 goto out;
1650 truncated = 1;
1651 }
1652 if (!rsrcforkbusy && rvp) {
1653 cp->c_mode = 0; /* Suppress hfs_update */
1654 error = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 1, ctx);
1655 cp->c_mode = mode;
1656 if (error)
1657 goto out;
1658 truncated = 1;
1659 }
1660 }
1661
1662 /*
1663 * Protect against a race with rename by using the component
1664 * name passed in and parent id from dvp (instead of using
1665 * the cp->c_desc which may have changed).
1666 */
1667 desc.cd_flags = 0;
1668 desc.cd_encoding = cp->c_desc.cd_encoding;
1669 desc.cd_nameptr = cnp->cn_nameptr;
1670 desc.cd_namelen = cnp->cn_namelen;
1671 desc.cd_parentcnid = dcp->c_cnid;
1672 desc.cd_hint = cp->c_desc.cd_hint;
1673 if (real_cnid) {
1674 // if it was a hardlink we had to re-lookup the cnid
1675 desc.cd_cnid = real_cnid;
1676 } else {
1677 desc.cd_cnid = cp->c_cnid;
1678 }
1679 microtime(&tv);
1680
1681 /*
1682 * There are 3 remove cases to consider:
1683 * 1. File is a hardlink ==> remove the link
1684 * 2. File is busy (in use) ==> move/rename the file
1685 * 3. File is not in use ==> remove the file
1686 */
1687
1688 if (cp->c_flag & C_HARDLINK) {
1689 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1690
1691 /* Delete the link record */
1692 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1693 if (error == 0) {
1694 /* Update the parent directory */
1695 if (dcp->c_entries > 0)
1696 dcp->c_entries--;
1697 if (dcp->c_nlink > 0)
1698 dcp->c_nlink--;
1699 dcp->c_ctime = tv.tv_sec;
1700 dcp->c_mtime = tv.tv_sec;
1701 (void ) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1702
1703 if (--cp->c_nlink < 1) {
1704 char inodename[32];
1705 char delname[32];
1706 struct cat_desc to_desc;
1707 struct cat_desc from_desc;
1708
1709 /*
1710 * This is now esentially an open deleted file.
1711 * Rename it to reflect this state which makes
1712 * orphan file cleanup easier (see hfs_remove_orphans).
1713 * Note: a rename failure here is not fatal.
1714 */
1715 MAKE_INODE_NAME(inodename, cp->c_rdev);
1716 bzero(&from_desc, sizeof(from_desc));
1717 from_desc.cd_nameptr = inodename;
1718 from_desc.cd_namelen = strlen(inodename);
1719 from_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
1720 from_desc.cd_flags = 0;
1721 from_desc.cd_cnid = cp->c_fileid;
1722
1723 MAKE_DELETED_NAME(delname, cp->c_fileid);
1724 bzero(&to_desc, sizeof(to_desc));
1725 to_desc.cd_nameptr = delname;
1726 to_desc.cd_namelen = strlen(delname);
1727 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
1728 to_desc.cd_flags = 0;
1729 to_desc.cd_cnid = cp->c_fileid;
1730
1731 error = cat_rename(hfsmp, &from_desc, &hfsmp->hfs_privdir_desc,
1732 &to_desc, (struct cat_desc *)NULL);
1733 if (error != 0) {
1734 panic("hfs_removefile: error %d from cat_rename(%s %s) cp 0x%x\n",
1735 inodename, delname, cp);
1736 }
1737 if (error == 0) {
1738 /* Update the file's state */
1739 cp->c_flag |= C_DELETED;
1740 cp->c_ctime = tv.tv_sec;
1741 (void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
1742 }
1743 } else {
1744 /* Update the file's state */
1745 cp->c_ctime = tv.tv_sec;
1746 (void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
1747 }
1748 }
1749 hfs_systemfile_unlock(hfsmp, lockflags);
1750 if (error != 0)
1751 goto out;
1752
1753 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
1754
1755 } else if (dataforkbusy || rsrcforkbusy || isbigfile) {
1756 char delname[32];
1757 struct cat_desc to_desc;
1758 struct cat_desc todir_desc;
1759
1760 /*
1761 * Orphan this file (move to hidden directory).
1762 */
1763 bzero(&todir_desc, sizeof(todir_desc));
1764 todir_desc.cd_parentcnid = 2;
1765
1766 MAKE_DELETED_NAME(delname, cp->c_fileid);
1767 bzero(&to_desc, sizeof(to_desc));
1768 to_desc.cd_nameptr = delname;
1769 to_desc.cd_namelen = strlen(delname);
1770 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
1771 to_desc.cd_flags = 0;
1772 to_desc.cd_cnid = cp->c_cnid;
1773
1774 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1775
1776 error = cat_rename(hfsmp, &desc, &todir_desc,
1777 &to_desc, (struct cat_desc *)NULL);
1778
1779 if (error == 0) {
1780 hfsmp->hfs_privdir_attr.ca_entries++;
1781 (void) cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
1782 &hfsmp->hfs_privdir_attr, NULL, NULL);
1783
1784 /* Update the parent directory */
1785 if (dcp->c_entries > 0)
1786 dcp->c_entries--;
1787 if (dcp->c_nlink > 0)
1788 dcp->c_nlink--;
1789 dcp->c_ctime = tv.tv_sec;
1790 dcp->c_mtime = tv.tv_sec;
1791 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1792
1793 /* Update the file's state */
1794 cp->c_flag |= C_DELETED;
1795 cp->c_ctime = tv.tv_sec;
1796 --cp->c_nlink;
1797 (void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
1798 }
1799 hfs_systemfile_unlock(hfsmp, lockflags);
1800 if (error)
1801 goto out;
1802
1803 } else /* Not busy */ {
1804
1805 if (cp->c_blocks > 0) {
1806 printf("hfs_remove: attempting to delete a non-empty file %s\n",
1807 cp->c_desc.cd_nameptr);
1808 error = EBUSY;
1809 goto out;
1810 }
1811
1812 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1813
1814 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1815
1816 if (error && error != ENXIO && error != ENOENT && truncated) {
1817 if ((cp->c_datafork && cp->c_datafork->ff_size != 0) ||
1818 (cp->c_rsrcfork && cp->c_rsrcfork->ff_size != 0)) {
1819 panic("hfs: remove: couldn't delete a truncated file! (%d, data sz %lld; rsrc sz %lld)",
1820 error, cp->c_datafork->ff_size, cp->c_rsrcfork->ff_size);
1821 } else {
1822 printf("hfs: remove: strangely enough, deleting truncated file %s (%d) got err %d\n",
1823 cp->c_desc.cd_nameptr, cp->c_attr.ca_fileid, error);
1824 }
1825 }
1826 if (error == 0) {
1827 /* Delete any attributes, ignore errors */
1828 (void) hfs_removeallattr(hfsmp, cp->c_fileid);
1829
1830 /* Update the parent directory */
1831 if (dcp->c_entries > 0)
1832 dcp->c_entries--;
1833 if (dcp->c_nlink > 0)
1834 dcp->c_nlink--;
1835 dcp->c_ctime = tv.tv_sec;
1836 dcp->c_mtime = tv.tv_sec;
1837 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1838 }
1839 hfs_systemfile_unlock(hfsmp, lockflags);
1840 if (error)
1841 goto out;
1842
1843 #if QUOTA
1844 (void)hfs_chkiq(cp, -1, NOCRED, 0);
1845 #endif /* QUOTA */
1846
1847 cp->c_mode = 0;
1848 truncated = 0; // because the catalog entry is gone
1849 cp->c_flag |= C_NOEXISTS;
1850 cp->c_touch_chgtime = TRUE; /* XXX needed ? */
1851 --cp->c_nlink;
1852
1853 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
1854 }
1855
1856 /*
1857 * All done with this cnode's descriptor...
1858 *
1859 * Note: all future catalog calls for this cnode must be
1860 * by fileid only. This is OK for HFS (which doesn't have
1861 * file thread records) since HFS doesn't support hard
1862 * links or the removal of busy files.
1863 */
1864 cat_releasedesc(&cp->c_desc);
1865
1866 HFS_KNOTE(dvp, NOTE_WRITE);
1867
1868 out:
1869 if (got_cookie) {
1870 cat_postflight(hfsmp, &cookie, 0);
1871 }
1872
1873 /* Commit the truncation to the catalog record */
1874 if (truncated) {
1875 cp->c_flag |= C_FORCEUPDATE;
1876 cp->c_touch_chgtime = TRUE;
1877 cp->c_touch_modtime = TRUE;
1878 (void) hfs_update(vp, 0);
1879 }
1880
1881 if (started_tr) {
1882 hfs_end_transaction(hfsmp);
1883 }
1884
1885 HFS_KNOTE(vp, NOTE_DELETE);
1886 if (rvp) {
1887 HFS_KNOTE(rvp, NOTE_DELETE);
1888 /* Defer the vnode_put on rvp until the hfs_unlock(). */
1889 cp->c_flag |= C_NEED_RVNODE_PUT;
1890 };
1891
1892 return (error);
1893 }
1894
1895
1896 __private_extern__ void
1897 replace_desc(struct cnode *cp, struct cat_desc *cdp)
1898 {
1899 if (&cp->c_desc == cdp) {
1900 return;
1901 }
1902
1903 /* First release allocated name buffer */
1904 if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) {
1905 char *name = cp->c_desc.cd_nameptr;
1906
1907 cp->c_desc.cd_nameptr = 0;
1908 cp->c_desc.cd_namelen = 0;
1909 cp->c_desc.cd_flags &= ~CD_HASBUF;
1910 vfs_removename(name);
1911 }
1912 bcopy(cdp, &cp->c_desc, sizeof(cp->c_desc));
1913
1914 /* Cnode now owns the name buffer */
1915 cdp->cd_nameptr = 0;
1916 cdp->cd_namelen = 0;
1917 cdp->cd_flags &= ~CD_HASBUF;
1918 }
1919
1920
1921 /*
1922 * Rename a cnode.
1923 *
1924 * The VFS layer guarantees that:
1925 * - source and destination will either both be directories, or
1926 * both not be directories.
1927 * - all the vnodes are from the same file system
1928 *
1929 * When the target is a directory, HFS must ensure that its empty.
1930 */
1931 static int
1932 hfs_vnop_rename(ap)
1933 struct vnop_rename_args /* {
1934 struct vnode *a_fdvp;
1935 struct vnode *a_fvp;
1936 struct componentname *a_fcnp;
1937 struct vnode *a_tdvp;
1938 struct vnode *a_tvp;
1939 struct componentname *a_tcnp;
1940 vfs_context_t a_context;
1941 } */ *ap;
1942 {
1943 struct vnode *tvp = ap->a_tvp;
1944 struct vnode *tdvp = ap->a_tdvp;
1945 struct vnode *fvp = ap->a_fvp;
1946 struct vnode *fdvp = ap->a_fdvp;
1947 struct componentname *tcnp = ap->a_tcnp;
1948 struct componentname *fcnp = ap->a_fcnp;
1949 struct proc *p = vfs_context_proc(ap->a_context);
1950 struct cnode *fcp;
1951 struct cnode *fdcp;
1952 struct cnode *tdcp;
1953 struct cnode *tcp;
1954 struct cat_desc from_desc;
1955 struct cat_desc to_desc;
1956 struct cat_desc out_desc;
1957 struct hfsmount *hfsmp;
1958 cat_cookie_t cookie;
1959 int tvp_deleted = 0;
1960 int started_tr = 0, got_cookie = 0;
1961 int took_trunc_lock = 0;
1962 int lockflags;
1963 int error;
1964
1965 /* When tvp exist, take the truncate lock for the hfs_removefile(). */
1966 if (tvp && vnode_isreg(tvp)) {
1967 hfs_lock_truncate(VTOC(tvp), TRUE);
1968 took_trunc_lock = 1;
1969 }
1970
1971 error = hfs_lockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL,
1972 HFS_EXCLUSIVE_LOCK);
1973 if (error) {
1974 if (took_trunc_lock)
1975 hfs_unlock_truncate(VTOC(tvp));
1976 return (error);
1977 }
1978
1979 fdcp = VTOC(fdvp);
1980 fcp = VTOC(fvp);
1981 tdcp = VTOC(tdvp);
1982 tcp = tvp ? VTOC(tvp) : NULL;
1983 hfsmp = VTOHFS(tdvp);
1984
1985 /* Check for a race against unlink. */
1986 if (fcp->c_flag & C_NOEXISTS) {
1987 error = ENOENT;
1988 goto out;
1989 }
1990
1991 /*
1992 * The following edge case is caught here:
1993 * (to cannot be a descendent of from)
1994 *
1995 * o fdvp
1996 * /
1997 * /
1998 * o fvp
1999 * \
2000 * \
2001 * o tdvp
2002 * /
2003 * /
2004 * o tvp
2005 */
2006 if (tdcp->c_parentcnid == fcp->c_cnid) {
2007 error = EINVAL;
2008 goto out;
2009 }
2010
2011 /*
2012 * The following two edge cases are caught here:
2013 * (note tvp is not empty)
2014 *
2015 * o tdvp o tdvp
2016 * / /
2017 * / /
2018 * o tvp tvp o fdvp
2019 * \ \
2020 * \ \
2021 * o fdvp o fvp
2022 * /
2023 * /
2024 * o fvp
2025 */
2026 if (tvp && vnode_isdir(tvp) && (tcp->c_entries != 0) && fvp != tvp) {
2027 error = ENOTEMPTY;
2028 goto out;
2029 }
2030
2031 /*
2032 * The following edge case is caught here:
2033 * (the from child and parent are the same)
2034 *
2035 * o tdvp
2036 * /
2037 * /
2038 * fdvp o fvp
2039 */
2040 if (fdvp == fvp) {
2041 error = EINVAL;
2042 goto out;
2043 }
2044
2045 /*
2046 * Make sure "from" vnode and its parent are changeable.
2047 */
2048 if ((fcp->c_flags & (IMMUTABLE | APPEND)) || (fdcp->c_flags & APPEND)) {
2049 error = EPERM;
2050 goto out;
2051 }
2052
2053 /*
2054 * If the destination parent directory is "sticky", then the
2055 * user must own the parent directory, or the destination of
2056 * the rename, otherwise the destination may not be changed
2057 * (except by root). This implements append-only directories.
2058 *
2059 * Note that checks for immutable and write access are done
2060 * by the call to hfs_removefile.
2061 */
2062 if (tvp && (tdcp->c_mode & S_ISTXT) &&
2063 (suser(vfs_context_ucred(tcnp->cn_context), NULL)) &&
2064 (kauth_cred_getuid(vfs_context_ucred(tcnp->cn_context)) != tdcp->c_uid) &&
2065 (hfs_owner_rights(hfsmp, tcp->c_uid, vfs_context_ucred(tcnp->cn_context), p, false)) ) {
2066 error = EPERM;
2067 goto out;
2068 }
2069
2070 #if QUOTA
2071 if (tvp)
2072 (void)hfs_getinoquota(tcp);
2073 #endif
2074 /* Preflighting done, take fvp out of the name space. */
2075 cache_purge(fvp);
2076
2077 /*
2078 * When a file moves out of "Cleanup At Startup"
2079 * we can drop its NODUMP status.
2080 */
2081 if ((fcp->c_flags & UF_NODUMP) &&
2082 vnode_isreg(fvp) &&
2083 (fdvp != tdvp) &&
2084 (fdcp->c_desc.cd_nameptr != NULL) &&
2085 (strcmp(fdcp->c_desc.cd_nameptr, CARBON_TEMP_DIR_NAME) == 0)) {
2086 fcp->c_flags &= ~UF_NODUMP;
2087 fcp->c_touch_chgtime = TRUE;
2088 (void) hfs_update(fvp, 0);
2089 }
2090
2091 bzero(&from_desc, sizeof(from_desc));
2092 from_desc.cd_nameptr = fcnp->cn_nameptr;
2093 from_desc.cd_namelen = fcnp->cn_namelen;
2094 from_desc.cd_parentcnid = fdcp->c_cnid;
2095 from_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
2096 from_desc.cd_cnid = fcp->c_cnid;
2097
2098 bzero(&to_desc, sizeof(to_desc));
2099 to_desc.cd_nameptr = tcnp->cn_nameptr;
2100 to_desc.cd_namelen = tcnp->cn_namelen;
2101 to_desc.cd_parentcnid = tdcp->c_cnid;
2102 to_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
2103 to_desc.cd_cnid = fcp->c_cnid;
2104
2105 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2106 goto out;
2107 }
2108 started_tr = 1;
2109
2110 // if it's a hardlink then re-lookup the name so
2111 // that we get the correct cnid in from_desc (see
2112 // the comment in hfs_removefile for more details)
2113 //
2114 if (fcp->c_flag & C_HARDLINK) {
2115 struct cat_desc tmpdesc;
2116 cnid_t real_cnid;
2117
2118 bzero(&tmpdesc, sizeof(tmpdesc));
2119 tmpdesc.cd_nameptr = fcnp->cn_nameptr;
2120 tmpdesc.cd_namelen = fcnp->cn_namelen;
2121 tmpdesc.cd_parentcnid = fdcp->c_cnid;
2122 tmpdesc.cd_hint = fdcp->c_childhint;
2123
2124 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2125
2126 if (cat_lookup(hfsmp, &tmpdesc, 0, NULL, NULL, NULL, &real_cnid) != 0) {
2127 hfs_systemfile_unlock(hfsmp, lockflags);
2128 goto out;
2129 }
2130
2131 // use the real cnid instead of whatever happened to be there
2132 from_desc.cd_cnid = real_cnid;
2133 hfs_systemfile_unlock(hfsmp, lockflags);
2134 }
2135
2136 /*
2137 * Reserve some space in the Catalog file.
2138 */
2139 bzero(&cookie, sizeof(cookie));
2140 if ((error = cat_preflight(hfsmp, CAT_RENAME + CAT_DELETE, &cookie, p))) {
2141 goto out;
2142 }
2143 got_cookie = 1;
2144
2145 /*
2146 * If the destination exists then it may need to be removed.
2147 */
2148 if (tvp) {
2149 /*
2150 * When fvp matches tvp they must be case variants
2151 * or hard links.
2152 */
2153 if (fvp == tvp) {
2154 /*
2155 * If this a hard link with different parents
2156 * and its not a case variant then tvp should
2157 * be removed.
2158 */
2159 if (!((fcp->c_flag & C_HARDLINK) &&
2160 ((fdvp != tdvp) ||
2161 (hfs_namecmp(fcnp->cn_nameptr, fcnp->cn_namelen,
2162 tcnp->cn_nameptr, tcnp->cn_namelen) != 0)))) {
2163 goto skip;
2164 }
2165 } else {
2166 cache_purge(tvp);
2167 }
2168
2169 if (vnode_isdir(tvp))
2170 error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_SKIP_RESERVE);
2171 else {
2172 error = hfs_removefile(tdvp, tvp, tcnp, 0, HFSRM_SKIP_RESERVE);
2173 }
2174
2175 if (error)
2176 goto out;
2177 tvp_deleted = 1;
2178 }
2179 skip:
2180 /*
2181 * All done with tvp and fvp
2182 */
2183
2184 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
2185 error = cat_rename(hfsmp, &from_desc, &tdcp->c_desc, &to_desc, &out_desc);
2186 hfs_systemfile_unlock(hfsmp, lockflags);
2187
2188 if (error) {
2189 goto out;
2190 }
2191
2192 /* Invalidate negative cache entries in the destination directory */
2193 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
2194 cache_purge_negatives(tdvp);
2195
2196 /* Update cnode's catalog descriptor */
2197 replace_desc(fcp, &out_desc);
2198 fcp->c_parentcnid = tdcp->c_cnid;
2199 fcp->c_hint = 0;
2200
2201 hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_RMDIR : VOL_RMFILE,
2202 (fdcp->c_cnid == kHFSRootFolderID));
2203 hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_MKDIR : VOL_MKFILE,
2204 (tdcp->c_cnid == kHFSRootFolderID));
2205
2206 /* Update both parent directories. */
2207 if (fdvp != tdvp) {
2208 tdcp->c_nlink++;
2209 tdcp->c_entries++;
2210 if (fdcp->c_nlink > 0)
2211 fdcp->c_nlink--;
2212 if (fdcp->c_entries > 0)
2213 fdcp->c_entries--;
2214 fdcp->c_touch_chgtime = TRUE;
2215 fdcp->c_touch_modtime = TRUE;
2216
2217 fdcp->c_flag |= C_FORCEUPDATE; // XXXdbg - force it out!
2218 (void) hfs_update(fdvp, 0);
2219 }
2220 tdcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
2221 tdcp->c_touch_chgtime = TRUE;
2222 tdcp->c_touch_modtime = TRUE;
2223
2224 tdcp->c_flag |= C_FORCEUPDATE; // XXXdbg - force it out!
2225 (void) hfs_update(tdvp, 0);
2226 out:
2227 if (got_cookie) {
2228 cat_postflight(hfsmp, &cookie, p);
2229 }
2230 if (started_tr) {
2231 hfs_end_transaction(hfsmp);
2232 }
2233
2234 /* Note that if hfs_removedir or hfs_removefile was invoked above they will already have
2235 generated a NOTE_WRITE for tdvp and a NOTE_DELETE for tvp.
2236 */
2237 if (error == 0) {
2238 HFS_KNOTE(fvp, NOTE_RENAME);
2239 HFS_KNOTE(fdvp, NOTE_WRITE);
2240 if (tdvp != fdvp) HFS_KNOTE(tdvp, NOTE_WRITE);
2241 };
2242
2243 if (took_trunc_lock)
2244 hfs_unlock_truncate(VTOC(tvp));
2245
2246 hfs_unlockfour(fdcp, fcp, tdcp, tcp);
2247
2248 /* After tvp is removed the only acceptable error is EIO */
2249 if (error && tvp_deleted)
2250 error = EIO;
2251
2252 return (error);
2253 }
2254
2255
2256 /*
2257 * Make a directory.
2258 */
2259 static int
2260 hfs_vnop_mkdir(struct vnop_mkdir_args *ap)
2261 {
2262 /***** HACK ALERT ********/
2263 ap->a_cnp->cn_flags |= MAKEENTRY;
2264 return hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context);
2265 }
2266
2267
2268 /*
2269 * Create a symbolic link.
2270 */
2271 static int
2272 hfs_vnop_symlink(struct vnop_symlink_args *ap)
2273 {
2274 struct vnode **vpp = ap->a_vpp;
2275 struct vnode *dvp = ap->a_dvp;
2276 struct vnode *vp = NULL;
2277 struct hfsmount *hfsmp;
2278 struct filefork *fp;
2279 struct buf *bp = NULL;
2280 char *datap;
2281 int started_tr = 0;
2282 int len, error;
2283
2284 /* HFS standard disks don't support symbolic links */
2285 if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord)
2286 return (ENOTSUP);
2287
2288 /* Check for empty target name */
2289 if (ap->a_target[0] == 0)
2290 return (EINVAL);
2291
2292 /* Create the vnode */
2293 ap->a_vap->va_mode |= S_IFLNK;
2294 if ((error = hfs_makenode(dvp, vpp, ap->a_cnp, ap->a_vap, ap->a_context))) {
2295 goto out;
2296 }
2297 vp = *vpp;
2298 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
2299 return (error);
2300 fp = VTOF(vp);
2301 hfsmp = VTOHFS(dvp);
2302 len = strlen(ap->a_target);
2303
2304 #if QUOTA
2305 (void)hfs_getinoquota(VTOC(vp));
2306 #endif /* QUOTA */
2307
2308 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2309 goto out;
2310 }
2311 started_tr = 1;
2312
2313 /*
2314 * Allocate space for the link.
2315 *
2316 * Since we're already inside a transaction,
2317 * tell hfs_truncate to skip the ubc_setsize.
2318 *
2319 * Don't need truncate lock since a symlink is treated as a system file.
2320 */
2321 error = hfs_truncate(vp, len, IO_NOZEROFILL, 1, ap->a_context);
2322 if (error)
2323 goto out; /* XXX need to remove link */
2324
2325 /* Write the link to disk */
2326 bp = buf_getblk(vp, (daddr64_t)0, roundup((int)fp->ff_size, VTOHFS(vp)->hfs_phys_block_size),
2327 0, 0, BLK_META);
2328 if (hfsmp->jnl) {
2329 journal_modify_block_start(hfsmp->jnl, bp);
2330 }
2331 datap = (char *)buf_dataptr(bp);
2332 bzero(datap, buf_size(bp));
2333 bcopy(ap->a_target, datap, len);
2334
2335 if (hfsmp->jnl) {
2336 journal_modify_block_end(hfsmp->jnl, bp);
2337 } else {
2338 buf_bawrite(bp);
2339 }
2340 /*
2341 * We defered the ubc_setsize for hfs_truncate
2342 * since we were inside a transaction.
2343 *
2344 * We don't need to drop the cnode lock here
2345 * since this is a symlink.
2346 */
2347 ubc_setsize(vp, len);
2348 out:
2349 if (started_tr)
2350 hfs_end_transaction(hfsmp);
2351 if (vp) {
2352 hfs_unlock(VTOC(vp));
2353 }
2354 return (error);
2355 }
2356
2357
2358 /* structures to hold a "." or ".." directory entry */
2359 struct hfs_stddotentry {
2360 u_int32_t d_fileno; /* unique file number */
2361 u_int16_t d_reclen; /* length of this structure */
2362 u_int8_t d_type; /* dirent file type */
2363 u_int8_t d_namlen; /* len of filename */
2364 char d_name[4]; /* "." or ".." */
2365 };
2366
2367 struct hfs_extdotentry {
2368 u_int64_t d_fileno; /* unique file number */
2369 u_int64_t d_seekoff; /* seek offset (optional, used by servers) */
2370 u_int16_t d_reclen; /* length of this structure */
2371 u_int16_t d_namlen; /* len of filename */
2372 u_int8_t d_type; /* dirent file type */
2373 u_char d_name[3]; /* "." or ".." */
2374 };
2375
2376 typedef union {
2377 struct hfs_stddotentry std;
2378 struct hfs_extdotentry ext;
2379 } hfs_dotentry_t;
2380
2381 /*
2382 * hfs_vnop_readdir reads directory entries into the buffer pointed
2383 * to by uio, in a filesystem independent format. Up to uio_resid
2384 * bytes of data can be transferred. The data in the buffer is a
2385 * series of packed dirent structures where each one contains the
2386 * following entries:
2387 *
2388 * u_int32_t d_fileno; // file number of entry
2389 * u_int16_t d_reclen; // length of this record
2390 * u_int8_t d_type; // file type
2391 * u_int8_t d_namlen; // length of string in d_name
2392 * char d_name[MAXNAMELEN+1]; // null terminated file name
2393 *
2394 * The current position (uio_offset) refers to the next block of
2395 * entries. The offset can only be set to a value previously
2396 * returned by hfs_vnop_readdir or zero. This offset does not have
2397 * to match the number of bytes returned (in uio_resid).
2398 *
2399 * In fact, the offset used by HFS is essentially an index (26 bits)
2400 * with a tag (6 bits). The tag is for associating the next request
2401 * with the current request. This enables us to have multiple threads
2402 * reading the directory while the directory is also being modified.
2403 *
2404 * Each tag/index pair is tied to a unique directory hint. The hint
2405 * contains information (filename) needed to build the catalog b-tree
2406 * key for finding the next set of entries.
2407 */
2408 static int
2409 hfs_vnop_readdir(ap)
2410 struct vnop_readdir_args /* {
2411 vnode_t a_vp;
2412 uio_t a_uio;
2413 int a_flags;
2414 int *a_eofflag;
2415 int *a_numdirent;
2416 vfs_context_t a_context;
2417 } */ *ap;
2418 {
2419 struct vnode *vp = ap->a_vp;
2420 uio_t uio = ap->a_uio;
2421 struct cnode *cp;
2422 struct hfsmount *hfsmp;
2423 directoryhint_t *dirhint = NULL;
2424 directoryhint_t localhint;
2425 off_t offset;
2426 off_t startoffset;
2427 int error = 0;
2428 int eofflag = 0;
2429 user_addr_t user_start = 0;
2430 user_size_t user_len = 0;
2431 int index;
2432 unsigned int tag;
2433 int items;
2434 int lockflags;
2435 int extended;
2436 int nfs_cookies;
2437 caddr_t bufstart;
2438 cnid_t cnid_hint = 0;
2439
2440 items = 0;
2441 startoffset = offset = uio_offset(uio);
2442 bufstart = CAST_DOWN(caddr_t, uio_iov_base(uio));
2443 extended = (ap->a_flags & VNODE_READDIR_EXTENDED);
2444 nfs_cookies = extended && (ap->a_flags & VNODE_READDIR_REQSEEKOFF);
2445
2446 /* Sanity check the uio data. */
2447 if ((uio_iovcnt(uio) > 1) ||
2448 (uio_resid(uio) < (int)sizeof(struct dirent))) {
2449 return (EINVAL);
2450 }
2451 /* Note that the dirhint calls require an exclusive lock. */
2452 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
2453 return (error);
2454 cp = VTOC(vp);
2455 hfsmp = VTOHFS(vp);
2456
2457 /* Pick up cnid hint (if any). */
2458 if (nfs_cookies) {
2459 cnid_hint = (cnid_t)(uio_offset(uio) >> 32);
2460 uio_setoffset(uio, uio_offset(uio) & 0x00000000ffffffffLL);
2461 if (cnid_hint == INT_MAX) { /* searching pass the last item */
2462 eofflag = 1;
2463 goto out;
2464 }
2465 }
2466 /*
2467 * Synthesize entries for "." and ".."
2468 */
2469 if (offset == 0) {
2470 hfs_dotentry_t dotentry[2];
2471 size_t uiosize;
2472
2473 if (extended) {
2474 struct hfs_extdotentry *entry = &dotentry[0].ext;
2475
2476 entry->d_fileno = cp->c_cnid;
2477 entry->d_reclen = sizeof(struct hfs_extdotentry);
2478 entry->d_type = DT_DIR;
2479 entry->d_namlen = 1;
2480 entry->d_name[0] = '.';
2481 entry->d_name[1] = '\0';
2482 entry->d_name[2] = '\0';
2483 entry->d_seekoff = 1;
2484
2485 ++entry;
2486 entry->d_fileno = cp->c_parentcnid;
2487 entry->d_reclen = sizeof(struct hfs_extdotentry);
2488 entry->d_type = DT_DIR;
2489 entry->d_namlen = 2;
2490 entry->d_name[0] = '.';
2491 entry->d_name[1] = '.';
2492 entry->d_name[2] = '\0';
2493 entry->d_seekoff = 2;
2494 uiosize = 2 * sizeof(struct hfs_extdotentry);
2495 } else {
2496 struct hfs_stddotentry *entry = &dotentry[0].std;
2497
2498 entry->d_fileno = cp->c_cnid;
2499 entry->d_reclen = sizeof(struct hfs_stddotentry);
2500 entry->d_type = DT_DIR;
2501 entry->d_namlen = 1;
2502 *(int *)&entry->d_name[0] = 0;
2503 entry->d_name[0] = '.';
2504
2505 ++entry;
2506 entry->d_fileno = cp->c_parentcnid;
2507 entry->d_reclen = sizeof(struct hfs_stddotentry);
2508 entry->d_type = DT_DIR;
2509 entry->d_namlen = 2;
2510 *(int *)&entry->d_name[0] = 0;
2511 entry->d_name[0] = '.';
2512 entry->d_name[1] = '.';
2513 uiosize = 2 * sizeof(struct hfs_stddotentry);
2514 }
2515 if ((error = uiomove((caddr_t)&dotentry, uiosize, uio))) {
2516 goto out;
2517 }
2518 offset += 2;
2519 }
2520
2521 /* If there are no real entries then we're done. */
2522 if (cp->c_entries == 0) {
2523 error = 0;
2524 eofflag = 1;
2525 uio_setoffset(uio, offset);
2526 goto seekoffcalc;
2527 }
2528
2529 //
2530 // We have to lock the user's buffer here so that we won't
2531 // fault on it after we've acquired a shared lock on the
2532 // catalog file. The issue is that you can get a 3-way
2533 // deadlock if someone else starts a transaction and then
2534 // tries to lock the catalog file but can't because we're
2535 // here and we can't service our page fault because VM is
2536 // blocked trying to start a transaction as a result of
2537 // trying to free up pages for our page fault. It's messy
2538 // but it does happen on dual-procesors that are paging
2539 // heavily (see radar 3082639 for more info). By locking
2540 // the buffer up-front we prevent ourselves from faulting
2541 // while holding the shared catalog file lock.
2542 //
2543 // Fortunately this and hfs_search() are the only two places
2544 // currently (10/30/02) that can fault on user data with a
2545 // shared lock on the catalog file.
2546 //
2547 if (hfsmp->jnl && uio_isuserspace(uio)) {
2548 user_start = uio_curriovbase(uio);
2549 user_len = uio_curriovlen(uio);
2550
2551 if ((error = vslock(user_start, user_len)) != 0) {
2552 user_start = 0;
2553 goto out;
2554 }
2555 }
2556 /* Convert offset into a catalog directory index. */
2557 index = (offset & HFS_INDEX_MASK) - 2;
2558 tag = offset & ~HFS_INDEX_MASK;
2559
2560 /* Lock catalog during cat_findname and cat_getdirentries. */
2561 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2562
2563 /* When called from NFS, try and resolve a cnid hint. */
2564 if (nfs_cookies && cnid_hint != 0) {
2565 if (cat_findname(hfsmp, cnid_hint, &localhint.dh_desc) == 0) {
2566 if ( localhint.dh_desc.cd_parentcnid == cp->c_cnid) {
2567 localhint.dh_index = index - 1;
2568 localhint.dh_time = 0;
2569 bzero(&localhint.dh_link, sizeof(localhint.dh_link));
2570 dirhint = &localhint; /* don't forget to release the descriptor */
2571 } else {
2572 cat_releasedesc(&localhint.dh_desc);
2573 }
2574 }
2575 }
2576
2577 /* Get a directory hint (cnode must be locked exclusive) */
2578 if (dirhint == NULL) {
2579 dirhint = hfs_getdirhint(cp, ((index - 1) & HFS_INDEX_MASK) | tag);
2580
2581 /* Hide tag from catalog layer. */
2582 dirhint->dh_index &= HFS_INDEX_MASK;
2583 if (dirhint->dh_index == HFS_INDEX_MASK) {
2584 dirhint->dh_index = -1;
2585 }
2586 }
2587
2588 /* Pack the buffer with dirent entries. */
2589 error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, uio, extended, &items, &eofflag);
2590
2591 hfs_systemfile_unlock(hfsmp, lockflags);
2592
2593 if (error != 0) {
2594 goto out;
2595 }
2596
2597 /* Get index to the next item */
2598 index += items;
2599
2600 if (items >= (int)cp->c_entries) {
2601 eofflag = 1;
2602 }
2603
2604 /* Convert catalog directory index back into an offset. */
2605 while (tag == 0)
2606 tag = (++cp->c_dirhinttag) << HFS_INDEX_BITS;
2607 uio_setoffset(uio, (index + 2) | tag);
2608 dirhint->dh_index |= tag;
2609
2610 seekoffcalc:
2611 cp->c_touch_acctime = TRUE;
2612
2613 if (ap->a_numdirent) {
2614 if (startoffset == 0)
2615 items += 2;
2616 *ap->a_numdirent = items;
2617 }
2618
2619 out:
2620 if (hfsmp->jnl && user_start) {
2621 vsunlock(user_start, user_len, TRUE);
2622 }
2623 /* If we didn't do anything then go ahead and dump the hint. */
2624 if ((dirhint != NULL) &&
2625 (dirhint != &localhint) &&
2626 (uio_offset(uio) == startoffset)) {
2627 hfs_reldirhint(cp, dirhint);
2628 eofflag = 1;
2629 }
2630 if (ap->a_eofflag) {
2631 *ap->a_eofflag = eofflag;
2632 }
2633 if (dirhint == &localhint) {
2634 cat_releasedesc(&localhint.dh_desc);
2635 }
2636 hfs_unlock(cp);
2637 return (error);
2638 }
2639
2640
2641 /*
2642 * Read contents of a symbolic link.
2643 */
2644 static int
2645 hfs_vnop_readlink(ap)
2646 struct vnop_readlink_args /* {
2647 struct vnode *a_vp;
2648 struct uio *a_uio;
2649 vfs_context_t a_context;
2650 } */ *ap;
2651 {
2652 struct vnode *vp = ap->a_vp;
2653 struct cnode *cp;
2654 struct filefork *fp;
2655 int error;
2656
2657 if (!vnode_islnk(vp))
2658 return (EINVAL);
2659
2660 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
2661 return (error);
2662 cp = VTOC(vp);
2663 fp = VTOF(vp);
2664
2665 /* Zero length sym links are not allowed */
2666 if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
2667 VTOVCB(vp)->vcbFlags |= kHFS_DamagedVolume;
2668 error = EINVAL;
2669 goto exit;
2670 }
2671
2672 /* Cache the path so we don't waste buffer cache resources */
2673 if (fp->ff_symlinkptr == NULL) {
2674 struct buf *bp = NULL;
2675
2676 MALLOC(fp->ff_symlinkptr, char *, fp->ff_size, M_TEMP, M_WAITOK);
2677 error = (int)buf_meta_bread(vp, (daddr64_t)0,
2678 roundup((int)fp->ff_size,
2679 VTOHFS(vp)->hfs_phys_block_size),
2680 vfs_context_ucred(ap->a_context), &bp);
2681 if (error) {
2682 if (bp)
2683 buf_brelse(bp);
2684 if (fp->ff_symlinkptr) {
2685 FREE(fp->ff_symlinkptr, M_TEMP);
2686 fp->ff_symlinkptr = NULL;
2687 }
2688 goto exit;
2689 }
2690 bcopy((char *)buf_dataptr(bp), fp->ff_symlinkptr, (size_t)fp->ff_size);
2691
2692 if (VTOHFS(vp)->jnl && (buf_flags(bp) & B_LOCKED) == 0) {
2693 buf_markinvalid(bp); /* data no longer needed */
2694 }
2695 buf_brelse(bp);
2696 }
2697 error = uiomove((caddr_t)fp->ff_symlinkptr, (int)fp->ff_size, ap->a_uio);
2698
2699 /*
2700 * Keep track blocks read
2701 */
2702 if ((VTOHFS(vp)->hfc_stage == HFC_RECORDING) && (error == 0)) {
2703
2704 /*
2705 * If this file hasn't been seen since the start of
2706 * the current sampling period then start over.
2707 */
2708 if (cp->c_atime < VTOHFS(vp)->hfc_timebase)
2709 VTOF(vp)->ff_bytesread = fp->ff_size;
2710 else
2711 VTOF(vp)->ff_bytesread += fp->ff_size;
2712
2713 // if (VTOF(vp)->ff_bytesread > fp->ff_size)
2714 // cp->c_touch_acctime = TRUE;
2715 }
2716
2717 exit:
2718 hfs_unlock(cp);
2719 return (error);
2720 }
2721
2722
2723 /*
2724 * Get configurable pathname variables.
2725 */
2726 static int
2727 hfs_vnop_pathconf(ap)
2728 struct vnop_pathconf_args /* {
2729 struct vnode *a_vp;
2730 int a_name;
2731 int *a_retval;
2732 vfs_context_t a_context;
2733 } */ *ap;
2734 {
2735 switch (ap->a_name) {
2736 case _PC_LINK_MAX:
2737 if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD)
2738 *ap->a_retval = 1;
2739 else
2740 *ap->a_retval = HFS_LINK_MAX;
2741 break;
2742 case _PC_NAME_MAX:
2743 if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD)
2744 *ap->a_retval = kHFSMaxFileNameChars; /* 255 */
2745 else
2746 *ap->a_retval = kHFSPlusMaxFileNameChars; /* 31 */
2747 break;
2748 case _PC_PATH_MAX:
2749 *ap->a_retval = PATH_MAX; /* 1024 */
2750 break;
2751 case _PC_PIPE_BUF:
2752 *ap->a_retval = PIPE_BUF;
2753 break;
2754 case _PC_CHOWN_RESTRICTED:
2755 *ap->a_retval = 1;
2756 break;
2757 case _PC_NO_TRUNC:
2758 *ap->a_retval = 0;
2759 break;
2760 case _PC_NAME_CHARS_MAX:
2761 *ap->a_retval = kHFSPlusMaxFileNameChars;
2762 break;
2763 case _PC_CASE_SENSITIVE:
2764 if (VTOHFS(ap->a_vp)->hfs_flags & HFS_CASE_SENSITIVE)
2765 *ap->a_retval = 1;
2766 else
2767 *ap->a_retval = 0;
2768 break;
2769 case _PC_CASE_PRESERVING:
2770 *ap->a_retval = 1;
2771 break;
2772 default:
2773 return (EINVAL);
2774 }
2775
2776 return (0);
2777 }
2778
2779
2780 /*
2781 * Update a cnode's on-disk metadata.
2782 *
2783 * If waitfor is set, then wait for the disk write of
2784 * the node to complete.
2785 *
2786 * The cnode must be locked exclusive
2787 */
2788 __private_extern__
2789 int
2790 hfs_update(struct vnode *vp, __unused int waitfor)
2791 {
2792 struct cnode *cp = VTOC(vp);
2793 struct proc *p;
2794 struct cat_fork *dataforkp = NULL;
2795 struct cat_fork *rsrcforkp = NULL;
2796 struct cat_fork datafork;
2797 struct hfsmount *hfsmp;
2798 int lockflags;
2799 int error;
2800
2801 p = current_proc();
2802 hfsmp = VTOHFS(vp);
2803
2804 if (vnode_issystem(vp) && (cp->c_cnid < kHFSFirstUserCatalogNodeID)) {
2805 return (0);
2806 }
2807 if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (cp->c_mode == 0)) {
2808 cp->c_flag &= ~C_MODIFIED;
2809 cp->c_touch_acctime = 0;
2810 cp->c_touch_chgtime = 0;
2811 cp->c_touch_modtime = 0;
2812 return (0);
2813 }
2814
2815 hfs_touchtimes(hfsmp, cp);
2816
2817 /* Nothing to update. */
2818 if ((cp->c_flag & (C_MODIFIED | C_FORCEUPDATE)) == 0) {
2819 return (0);
2820 }
2821
2822 if (cp->c_datafork)
2823 dataforkp = &cp->c_datafork->ff_data;
2824 if (cp->c_rsrcfork)
2825 rsrcforkp = &cp->c_rsrcfork->ff_data;
2826
2827 /*
2828 * For delayed allocations updates are
2829 * postponed until an fsync or the file
2830 * gets written to disk.
2831 *
2832 * Deleted files can defer meta data updates until inactive.
2833 *
2834 * If we're ever called with the C_FORCEUPDATE flag though
2835 * we have to do the update.
2836 */
2837 if (ISSET(cp->c_flag, C_FORCEUPDATE) == 0 &&
2838 (ISSET(cp->c_flag, C_DELETED) ||
2839 (dataforkp && cp->c_datafork->ff_unallocblocks) ||
2840 (rsrcforkp && cp->c_rsrcfork->ff_unallocblocks))) {
2841 // cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE);
2842 cp->c_flag |= C_MODIFIED;
2843
2844 HFS_KNOTE(vp, NOTE_ATTRIB);
2845
2846 return (0);
2847 }
2848
2849 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2850 return error;
2851 }
2852
2853 /*
2854 * For files with invalid ranges (holes) the on-disk
2855 * field representing the size of the file (cf_size)
2856 * must be no larger than the start of the first hole.
2857 */
2858 if (dataforkp && !CIRCLEQ_EMPTY(&cp->c_datafork->ff_invalidranges)) {
2859 bcopy(dataforkp, &datafork, sizeof(datafork));
2860 datafork.cf_size = CIRCLEQ_FIRST(&cp->c_datafork->ff_invalidranges)->rl_start;
2861 dataforkp = &datafork;
2862 } else if (dataforkp && (cp->c_datafork->ff_unallocblocks != 0)) {
2863 // always make sure the block count and the size
2864 // of the file match the number of blocks actually
2865 // allocated to the file on disk
2866 bcopy(dataforkp, &datafork, sizeof(datafork));
2867 // make sure that we don't assign a negative block count
2868 if (cp->c_datafork->ff_blocks < cp->c_datafork->ff_unallocblocks) {
2869 panic("hfs: ff_blocks %d is less than unalloc blocks %d\n",
2870 cp->c_datafork->ff_blocks, cp->c_datafork->ff_unallocblocks);
2871 }
2872 datafork.cf_blocks = (cp->c_datafork->ff_blocks - cp->c_datafork->ff_unallocblocks);
2873 datafork.cf_size = datafork.cf_blocks * HFSTOVCB(hfsmp)->blockSize;
2874 dataforkp = &datafork;
2875 }
2876
2877 /*
2878 * Lock the Catalog b-tree file.
2879 * A shared lock is sufficient since an update doesn't change
2880 * the tree and the lock on vp protects the cnode.
2881 */
2882 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2883
2884 /* XXX - waitfor is not enforced */
2885 error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
2886
2887 hfs_systemfile_unlock(hfsmp, lockflags);
2888
2889 /* After the updates are finished, clear the flags */
2890 cp->c_flag &= ~(C_MODIFIED | C_FORCEUPDATE);
2891
2892 hfs_end_transaction(hfsmp);
2893
2894 HFS_KNOTE(vp, NOTE_ATTRIB);
2895
2896 return (error);
2897 }
2898
2899 /*
2900 * Allocate a new node
2901 */
2902 static int
2903 hfs_makenode(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
2904 struct vnode_attr *vap, vfs_context_t ctx)
2905 {
2906 struct cnode *cp = NULL;
2907 struct cnode *dcp;
2908 struct vnode *tvp;
2909 struct hfsmount *hfsmp;
2910 struct cat_desc in_desc, out_desc;
2911 struct cat_attr attr;
2912 struct timeval tv;
2913 cat_cookie_t cookie;
2914 int lockflags;
2915 int error, started_tr = 0, got_cookie = 0;
2916 enum vtype vnodetype;
2917 int mode;
2918
2919 if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK)))
2920 return (error);
2921 dcp = VTOC(dvp);
2922 hfsmp = VTOHFS(dvp);
2923 *vpp = NULL;
2924 tvp = NULL;
2925 out_desc.cd_flags = 0;
2926 out_desc.cd_nameptr = NULL;
2927
2928 mode = MAKEIMODE(vap->va_type, vap->va_mode);
2929
2930 if ((mode & S_IFMT) == 0)
2931 mode |= S_IFREG;
2932 vnodetype = IFTOVT(mode);
2933
2934 /* Check if were out of usable disk space. */
2935 if ((hfs_freeblks(hfsmp, 1) <= 0) && (suser(vfs_context_ucred(ctx), NULL) != 0)) {
2936 error = ENOSPC;
2937 goto exit;
2938 }
2939
2940 microtime(&tv);
2941
2942 /* Setup the default attributes */
2943 bzero(&attr, sizeof(attr));
2944 attr.ca_mode = mode;
2945 attr.ca_nlink = vnodetype == VDIR ? 2 : 1;
2946 attr.ca_mtime = tv.tv_sec;
2947 if ((VTOVCB(dvp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
2948 attr.ca_mtime += 3600; /* Same as what hfs_update does */
2949 }
2950 attr.ca_atime = attr.ca_ctime = attr.ca_itime = attr.ca_mtime;
2951 attr.ca_atimeondisk = attr.ca_atime;
2952 /* On HFS+ the ThreadExists flag must always be set for files. */
2953 if (vnodetype != VDIR && (hfsmp->hfs_flags & HFS_STANDARD) == 0)
2954 attr.ca_recflags = kHFSThreadExistsMask;
2955
2956 attr.ca_uid = vap->va_uid;
2957 attr.ca_gid = vap->va_gid;
2958 VATTR_SET_SUPPORTED(vap, va_mode);
2959 VATTR_SET_SUPPORTED(vap, va_uid);
2960 VATTR_SET_SUPPORTED(vap, va_gid);
2961
2962 /* Tag symlinks with a type and creator. */
2963 if (vnodetype == VLNK) {
2964 struct FndrFileInfo *fip;
2965
2966 fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
2967 fip->fdType = SWAP_BE32(kSymLinkFileType);
2968 fip->fdCreator = SWAP_BE32(kSymLinkCreator);
2969 }
2970 if (cnp->cn_flags & ISWHITEOUT)
2971 attr.ca_flags |= UF_OPAQUE;
2972
2973 /* Setup the descriptor */
2974 in_desc.cd_nameptr = cnp->cn_nameptr;
2975 in_desc.cd_namelen = cnp->cn_namelen;
2976 in_desc.cd_parentcnid = dcp->c_cnid;
2977 in_desc.cd_flags = S_ISDIR(mode) ? CD_ISDIR : 0;
2978 in_desc.cd_hint = dcp->c_childhint;
2979 in_desc.cd_encoding = 0;
2980
2981 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2982 goto exit;
2983 }
2984 started_tr = 1;
2985
2986 /*
2987 * Reserve some space in the Catalog file.
2988 *
2989 * (we also add CAT_DELETE since our getnewvnode
2990 * request can cause an hfs_inactive call to
2991 * delete an unlinked file)
2992 */
2993 if ((error = cat_preflight(hfsmp, CAT_CREATE | CAT_DELETE, &cookie, 0))) {
2994 goto exit;
2995 }
2996 got_cookie = 1;
2997
2998 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
2999 error = cat_create(hfsmp, &in_desc, &attr, &out_desc);
3000 if (error == 0) {
3001 /* Update the parent directory */
3002 dcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
3003 dcp->c_nlink++;
3004 dcp->c_entries++;
3005 dcp->c_ctime = tv.tv_sec;
3006 dcp->c_mtime = tv.tv_sec;
3007 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
3008 HFS_KNOTE(dvp, NOTE_ATTRIB);
3009 }
3010 hfs_systemfile_unlock(hfsmp, lockflags);
3011 if (error)
3012 goto exit;
3013
3014 /* Invalidate negative cache entries in the directory */
3015 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
3016 cache_purge_negatives(dvp);
3017
3018 if (vnodetype == VDIR) {
3019 HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
3020 } else {
3021 HFS_KNOTE(dvp, NOTE_WRITE);
3022 };
3023
3024 hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE,
3025 (dcp->c_cnid == kHFSRootFolderID));
3026
3027 // XXXdbg
3028 // have to end the transaction here before we call hfs_getnewvnode()
3029 // because that can cause us to try and reclaim a vnode on a different
3030 // file system which could cause us to start a transaction which can
3031 // deadlock with someone on that other file system (since we could be
3032 // holding two transaction locks as well as various vnodes and we did
3033 // not obtain the locks on them in the proper order).
3034 //
3035 // NOTE: this means that if the quota check fails or we have to update
3036 // the change time on a block-special device that those changes
3037 // will happen as part of independent transactions.
3038 //
3039 if (started_tr) {
3040 hfs_end_transaction(hfsmp);
3041 started_tr = 0;
3042 }
3043
3044 /*
3045 * Create a vnode for the object just created.
3046 *
3047 * The cnode is locked on successful return.
3048 */
3049 error = hfs_getnewvnode(hfsmp, dvp, cnp, &out_desc, 0, &attr, NULL, &tvp);
3050 if (error)
3051 goto exit;
3052
3053 // XXXdbg
3054 //cache_enter(dvp, tvp, cnp);
3055
3056 cp = VTOC(tvp);
3057 #if QUOTA
3058 /*
3059 * We call hfs_chkiq with FORCE flag so that if we
3060 * fall through to the rmdir we actually have
3061 * accounted for the inode
3062 */
3063 if (vfs_flags(HFSTOVFS(hfsmp)) & MNT_QUOTA) {
3064 if ((error = hfs_getinoquota(cp)) ||
3065 (error = hfs_chkiq(cp, 1, vfs_context_ucred(ctx), FORCE))) {
3066
3067 if (vnode_isdir(tvp))
3068 (void) hfs_removedir(dvp, tvp, cnp, 0);
3069 else {
3070 hfs_unlock(cp);
3071 hfs_lock_truncate(cp, TRUE);
3072 hfs_lock(cp, HFS_FORCE_LOCK);
3073 (void) hfs_removefile(dvp, tvp, cnp, 0, 0);
3074 hfs_unlock_truncate(cp);
3075 }
3076 /*
3077 * we successfully allocated a new vnode, but
3078 * the quota check is telling us we're beyond
3079 * our limit, so we need to dump our lock + reference
3080 */
3081 hfs_unlock(cp);
3082 vnode_put(tvp);
3083
3084 goto exit;
3085 }
3086 }
3087 #endif /* QUOTA */
3088
3089 /* Remember if any ACL data was set. */
3090 if (VATTR_IS_ACTIVE(vap, va_acl) &&
3091 (vap->va_acl != NULL)) {
3092 cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
3093 cp->c_touch_chgtime = TRUE;
3094 (void) hfs_update(tvp, TRUE);
3095 }
3096 *vpp = tvp;
3097 exit:
3098 cat_releasedesc(&out_desc);
3099
3100 if (got_cookie) {
3101 cat_postflight(hfsmp, &cookie, 0);
3102 }
3103 /*
3104 * Check if a file is located in the "Cleanup At Startup"
3105 * directory. If it is then tag it as NODUMP so that we
3106 * can be lazy about zero filling data holes.
3107 */
3108 if ((error == 0) && dvp && (vnodetype == VREG) &&
3109 (dcp->c_desc.cd_nameptr != NULL) &&
3110 (strcmp(dcp->c_desc.cd_nameptr, CARBON_TEMP_DIR_NAME) == 0)) {
3111 struct vnode *ddvp;
3112
3113 hfs_unlock(dcp);
3114 dvp = NULL;
3115
3116 /*
3117 * The parent of "Cleanup At Startup" should
3118 * have the ASCII name of the userid.
3119 */
3120 if (hfs_vget(hfsmp, dcp->c_parentcnid, &ddvp, 0) == 0) {
3121 if (VTOC(ddvp)->c_desc.cd_nameptr) {
3122 uid_t uid;
3123
3124 uid = strtoul(VTOC(ddvp)->c_desc.cd_nameptr, 0, 0);
3125 if ((uid == cp->c_uid) ||
3126 (uid == vfs_context_ucred(ctx)->cr_uid)) {
3127 cp->c_flags |= UF_NODUMP;
3128 cp->c_touch_chgtime = TRUE;
3129 }
3130 }
3131 hfs_unlock(VTOC(ddvp));
3132 vnode_put(ddvp);
3133 }
3134 }
3135 if (dvp) {
3136 hfs_unlock(dcp);
3137 }
3138 if (error == 0 && cp != NULL) {
3139 hfs_unlock(cp);
3140 }
3141 if (started_tr) {
3142 hfs_end_transaction(hfsmp);
3143 started_tr = 0;
3144 }
3145
3146 return (error);
3147 }
3148
3149
3150 /*
3151 * WARNING - assumes caller has cnode lock.
3152 */
3153 __private_extern__
3154 int
3155 hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp, struct vnode **rvpp, __unused struct proc *p)
3156 {
3157 struct vnode *rvp;
3158 struct cnode *cp = VTOC(vp);
3159 int error;
3160 int vid;
3161
3162 if ((rvp = cp->c_rsrc_vp)) {
3163 vid = vnode_vid(rvp);
3164
3165 /* Use exising vnode */
3166 error = vnode_getwithvid(rvp, vid);
3167 if (error) {
3168 char * name = VTOC(vp)->c_desc.cd_nameptr;
3169
3170 if (name)
3171 printf("hfs_vgetrsrc: couldn't get"
3172 " resource fork for %s\n", name);
3173 return (error);
3174 }
3175 } else {
3176 struct cat_fork rsrcfork;
3177 struct componentname cn;
3178 int lockflags;
3179
3180 /*
3181 * Make sure cnode lock is exclusive, if not upgrade it.
3182 *
3183 * We assume that we were called from a read-only VNOP (getattr)
3184 * and that its safe to have the cnode lock dropped and reacquired.
3185 */
3186 if (cp->c_lockowner != current_thread()) {
3187 /*
3188 * If the upgrade fails we loose the lock and
3189 * have to take the exclusive lock on our own.
3190 */
3191 if (lck_rw_lock_shared_to_exclusive(&cp->c_rwlock) != 0)
3192 lck_rw_lock_exclusive(&cp->c_rwlock);
3193 cp->c_lockowner = current_thread();
3194 }
3195
3196 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
3197
3198 /* Get resource fork data */
3199 error = cat_lookup(hfsmp, &cp->c_desc, 1, (struct cat_desc *)0,
3200 (struct cat_attr *)0, &rsrcfork, NULL);
3201
3202 hfs_systemfile_unlock(hfsmp, lockflags);
3203 if (error)
3204 return (error);
3205
3206 /*
3207 * Supply hfs_getnewvnode with a component name.
3208 */
3209 cn.cn_pnbuf = NULL;
3210 if (cp->c_desc.cd_nameptr) {
3211 MALLOC_ZONE(cn.cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
3212 cn.cn_nameiop = LOOKUP;
3213 cn.cn_flags = ISLASTCN | HASBUF;
3214 cn.cn_context = NULL;
3215 cn.cn_pnlen = MAXPATHLEN;
3216 cn.cn_nameptr = cn.cn_pnbuf;
3217 cn.cn_hash = 0;
3218 cn.cn_consume = 0;
3219 cn.cn_namelen = sprintf(cn.cn_nameptr, "%s%s", cp->c_desc.cd_nameptr, _PATH_RSRCFORKSPEC);
3220 }
3221 error = hfs_getnewvnode(hfsmp, vnode_parent(vp), cn.cn_pnbuf ? &cn : NULL,
3222 &cp->c_desc, 2, &cp->c_attr, &rsrcfork, &rvp);
3223 if (cn.cn_pnbuf)
3224 FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
3225 if (error)
3226 return (error);
3227 }
3228
3229 *rvpp = rvp;
3230 return (0);
3231 }
3232
3233
3234 static void
3235 filt_hfsdetach(struct knote *kn)
3236 {
3237 struct vnode *vp;
3238
3239 vp = (struct vnode *)kn->kn_hook;
3240 if (vnode_getwithvid(vp, kn->kn_hookid))
3241 return;
3242
3243 if (1) { /* ! KNDETACH_VNLOCKED */
3244 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
3245 (void) KNOTE_DETACH(&VTOC(vp)->c_knotes, kn);
3246 hfs_unlock(VTOC(vp));
3247 }
3248 }
3249
3250 vnode_put(vp);
3251 }
3252
3253 /*ARGSUSED*/
3254 static int
3255 filt_hfsread(struct knote *kn, long hint)
3256 {
3257 struct vnode *vp = (struct vnode *)kn->kn_hook;
3258 int dropvp = 0;
3259
3260 if (hint == 0) {
3261 if ((vnode_getwithvid(vp, kn->kn_hookid) != 0)) {
3262 hint = NOTE_REVOKE;
3263 } else
3264 dropvp = 1;
3265 }
3266 if (hint == NOTE_REVOKE) {
3267 /*
3268 * filesystem is gone, so set the EOF flag and schedule
3269 * the knote for deletion.
3270 */
3271 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3272 return (1);
3273 }
3274
3275 /* poll(2) semantics dictate always saying there is data */
3276 kn->kn_data = (!(kn->kn_flags & EV_POLL)) ?
3277 VTOF(vp)->ff_size - kn->kn_fp->f_fglob->fg_offset : 1;
3278
3279 if (dropvp)
3280 vnode_put(vp);
3281
3282 return (kn->kn_data != 0);
3283 }
3284
3285 /*ARGSUSED*/
3286 static int
3287 filt_hfswrite(struct knote *kn, long hint)
3288 {
3289 int dropvp = 0;
3290
3291 if (hint == 0) {
3292 if ((vnode_getwithvid(kn->kn_hook, kn->kn_hookid) != 0)) {
3293 hint = NOTE_REVOKE;
3294 } else
3295 vnode_put(kn->kn_hook);
3296 }
3297 if (hint == NOTE_REVOKE) {
3298 /*
3299 * filesystem is gone, so set the EOF flag and schedule
3300 * the knote for deletion.
3301 */
3302 kn->kn_data = 0;
3303 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3304 return (1);
3305 }
3306 kn->kn_data = 0;
3307 return (1);
3308 }
3309
3310 static int
3311 filt_hfsvnode(struct knote *kn, long hint)
3312 {
3313
3314 if (hint == 0) {
3315 if ((vnode_getwithvid(kn->kn_hook, kn->kn_hookid) != 0)) {
3316 hint = NOTE_REVOKE;
3317 } else
3318 vnode_put(kn->kn_hook);
3319 }
3320 if (kn->kn_sfflags & hint)
3321 kn->kn_fflags |= hint;
3322 if ((hint == NOTE_REVOKE)) {
3323 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3324 return (1);
3325 }
3326
3327 return (kn->kn_fflags != 0);
3328 }
3329
3330 static struct filterops hfsread_filtops =
3331 { 1, NULL, filt_hfsdetach, filt_hfsread };
3332 static struct filterops hfswrite_filtops =
3333 { 1, NULL, filt_hfsdetach, filt_hfswrite };
3334 static struct filterops hfsvnode_filtops =
3335 { 1, NULL, filt_hfsdetach, filt_hfsvnode };
3336
3337 /*
3338 * Add a kqueue filter.
3339 */
3340 static int
3341 hfs_vnop_kqfiltadd(
3342 struct vnop_kqfilt_add_args /* {
3343 struct vnode *a_vp;
3344 struct knote *a_kn;
3345 struct proc *p;
3346 vfs_context_t a_context;
3347 } */ *ap)
3348 {
3349 struct vnode *vp = ap->a_vp;
3350 struct knote *kn = ap->a_kn;
3351 int error;
3352
3353 switch (kn->kn_filter) {
3354 case EVFILT_READ:
3355 if (vnode_isreg(vp)) {
3356 kn->kn_fop = &hfsread_filtops;
3357 } else {
3358 return EINVAL;
3359 };
3360 break;
3361 case EVFILT_WRITE:
3362 if (vnode_isreg(vp)) {
3363 kn->kn_fop = &hfswrite_filtops;
3364 } else {
3365 return EINVAL;
3366 };
3367 break;
3368 case EVFILT_VNODE:
3369 kn->kn_fop = &hfsvnode_filtops;
3370 break;
3371 default:
3372 return (1);
3373 }
3374
3375 kn->kn_hook = (caddr_t)vp;
3376 kn->kn_hookid = vnode_vid(vp);
3377
3378 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
3379 return (error);
3380 KNOTE_ATTACH(&VTOC(vp)->c_knotes, kn);
3381 hfs_unlock(VTOC(vp));
3382
3383 return (0);
3384 }
3385
3386 /*
3387 * Remove a kqueue filter
3388 */
3389 static int
3390 hfs_vnop_kqfiltremove(ap)
3391 struct vnop_kqfilt_remove_args /* {
3392 struct vnode *a_vp;
3393 uintptr_t ident;
3394 vfs_context_t a_context;
3395 } */ *ap;
3396 {
3397 int result;
3398
3399 result = ENOTSUP; /* XXX */
3400
3401 return (result);
3402 }
3403
3404 /*
3405 * Wrapper for special device reads
3406 */
3407 static int
3408 hfsspec_read(ap)
3409 struct vnop_read_args /* {
3410 struct vnode *a_vp;
3411 struct uio *a_uio;
3412 int a_ioflag;
3413 vfs_context_t a_context;
3414 } */ *ap;
3415 {
3416 /*
3417 * Set access flag.
3418 */
3419 VTOC(ap->a_vp)->c_touch_acctime = TRUE;
3420 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap));
3421 }
3422
3423 /*
3424 * Wrapper for special device writes
3425 */
3426 static int
3427 hfsspec_write(ap)
3428 struct vnop_write_args /* {
3429 struct vnode *a_vp;
3430 struct uio *a_uio;
3431 int a_ioflag;
3432 vfs_context_t a_context;
3433 } */ *ap;
3434 {
3435 /*
3436 * Set update and change flags.
3437 */
3438 VTOC(ap->a_vp)->c_touch_chgtime = TRUE;
3439 VTOC(ap->a_vp)->c_touch_modtime = TRUE;
3440 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap));
3441 }
3442
3443 /*
3444 * Wrapper for special device close
3445 *
3446 * Update the times on the cnode then do device close.
3447 */
3448 static int
3449 hfsspec_close(ap)
3450 struct vnop_close_args /* {
3451 struct vnode *a_vp;
3452 int a_fflag;
3453 vfs_context_t a_context;
3454 } */ *ap;
3455 {
3456 struct vnode *vp = ap->a_vp;
3457 struct cnode *cp;
3458
3459 if (vnode_isinuse(ap->a_vp, 1)) {
3460 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
3461 cp = VTOC(vp);
3462 hfs_touchtimes(VTOHFS(vp), cp);
3463 hfs_unlock(cp);
3464 }
3465 }
3466 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap));
3467 }
3468
3469 #if FIFO
3470 /*
3471 * Wrapper for fifo reads
3472 */
3473 static int
3474 hfsfifo_read(ap)
3475 struct vnop_read_args /* {
3476 struct vnode *a_vp;
3477 struct uio *a_uio;
3478 int a_ioflag;
3479 vfs_context_t a_context;
3480 } */ *ap;
3481 {
3482 extern int (**fifo_vnodeop_p)(void *);
3483
3484 /*
3485 * Set access flag.
3486 */
3487 VTOC(ap->a_vp)->c_touch_acctime = TRUE;
3488 return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_read), ap));
3489 }
3490
3491 /*
3492 * Wrapper for fifo writes
3493 */
3494 static int
3495 hfsfifo_write(ap)
3496 struct vnop_write_args /* {
3497 struct vnode *a_vp;
3498 struct uio *a_uio;
3499 int a_ioflag;
3500 vfs_context_t a_context;
3501 } */ *ap;
3502 {
3503 extern int (**fifo_vnodeop_p)(void *);
3504
3505 /*
3506 * Set update and change flags.
3507 */
3508 VTOC(ap->a_vp)->c_touch_chgtime = TRUE;
3509 VTOC(ap->a_vp)->c_touch_modtime = TRUE;
3510 return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_write), ap));
3511 }
3512
3513 /*
3514 * Wrapper for fifo close
3515 *
3516 * Update the times on the cnode then do device close.
3517 */
3518 static int
3519 hfsfifo_close(ap)
3520 struct vnop_close_args /* {
3521 struct vnode *a_vp;
3522 int a_fflag;
3523 vfs_context_t a_context;
3524 } */ *ap;
3525 {
3526 extern int (**fifo_vnodeop_p)(void *);
3527 struct vnode *vp = ap->a_vp;
3528 struct cnode *cp;
3529
3530 if (vnode_isinuse(ap->a_vp, 1)) {
3531 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
3532 cp = VTOC(vp);
3533 hfs_touchtimes(VTOHFS(vp), cp);
3534 hfs_unlock(cp);
3535 }
3536 }
3537 return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_close), ap));
3538 }
3539
3540 /*
3541 * kqfilt_add wrapper for fifos.
3542 *
3543 * Fall through to hfs kqfilt_add routines if needed
3544 */
3545 int
3546 hfsfifo_kqfilt_add(ap)
3547 struct vnop_kqfilt_add_args *ap;
3548 {
3549 extern int (**fifo_vnodeop_p)(void *);
3550 int error;
3551
3552 error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_add), ap);
3553 if (error)
3554 error = hfs_vnop_kqfiltadd(ap);
3555 return (error);
3556 }
3557
3558 /*
3559 * kqfilt_remove wrapper for fifos.
3560 *
3561 * Fall through to hfs kqfilt_remove routines if needed
3562 */
3563 int
3564 hfsfifo_kqfilt_remove(ap)
3565 struct vnop_kqfilt_remove_args *ap;
3566 {
3567 extern int (**fifo_vnodeop_p)(void *);
3568 int error;
3569
3570 error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_remove), ap);
3571 if (error)
3572 error = hfs_vnop_kqfiltremove(ap);
3573 return (error);
3574 }
3575
3576 #endif /* FIFO */
3577
3578 /*
3579 * Synchronize a file's in-core state with that on disk.
3580 */
3581 static int
3582 hfs_vnop_fsync(ap)
3583 struct vnop_fsync_args /* {
3584 struct vnode *a_vp;
3585 int a_waitfor;
3586 vfs_context_t a_context;
3587 } */ *ap;
3588 {
3589 struct vnode* vp = ap->a_vp;
3590 int error;
3591
3592 /*
3593 * We need to allow ENOENT lock errors since unlink
3594 * systenm call can call VNOP_FSYNC during vclean.
3595 */
3596 error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
3597 if (error)
3598 return (0);
3599
3600 error = hfs_fsync(vp, ap->a_waitfor, 0, vfs_context_proc(ap->a_context));
3601
3602 hfs_unlock(VTOC(vp));
3603 return (error);
3604 }
3605
3606 /*****************************************************************************
3607 *
3608 * VOP Tables
3609 *
3610 *****************************************************************************/
3611 int hfs_vnop_readdirattr(struct vnop_readdirattr_args *); /* in hfs_attrlist.c */
3612 int hfs_vnop_inactive(struct vnop_inactive_args *); /* in hfs_cnode.c */
3613 int hfs_vnop_reclaim(struct vnop_reclaim_args *); /* in hfs_cnode.c */
3614 int hfs_vnop_link(struct vnop_link_args *); /* in hfs_link.c */
3615 int hfs_vnop_lookup(struct vnop_lookup_args *); /* in hfs_lookup.c */
3616 int hfs_vnop_search(struct vnop_searchfs_args *); /* in hfs_search.c */
3617
3618 int hfs_vnop_read(struct vnop_read_args *); /* in hfs_readwrite.c */
3619 int hfs_vnop_write(struct vnop_write_args *); /* in hfs_readwrite.c */
3620 int hfs_vnop_ioctl(struct vnop_ioctl_args *); /* in hfs_readwrite.c */
3621 int hfs_vnop_select(struct vnop_select_args *); /* in hfs_readwrite.c */
3622 int hfs_vnop_strategy(struct vnop_strategy_args *); /* in hfs_readwrite.c */
3623 int hfs_vnop_allocate(struct vnop_allocate_args *); /* in hfs_readwrite.c */
3624 int hfs_vnop_pagein(struct vnop_pagein_args *); /* in hfs_readwrite.c */
3625 int hfs_vnop_pageout(struct vnop_pageout_args *); /* in hfs_readwrite.c */
3626 int hfs_vnop_bwrite(struct vnop_bwrite_args *); /* in hfs_readwrite.c */
3627 int hfs_vnop_blktooff(struct vnop_blktooff_args *); /* in hfs_readwrite.c */
3628 int hfs_vnop_offtoblk(struct vnop_offtoblk_args *); /* in hfs_readwrite.c */
3629 int hfs_vnop_blockmap(struct vnop_blockmap_args *); /* in hfs_readwrite.c */
3630 int hfs_vnop_getxattr(struct vnop_getxattr_args *); /* in hfs_xattr.c */
3631 int hfs_vnop_setxattr(struct vnop_setxattr_args *); /* in hfs_xattr.c */
3632 int hfs_vnop_removexattr(struct vnop_removexattr_args *); /* in hfs_xattr.c */
3633 int hfs_vnop_listxattr(struct vnop_listxattr_args *); /* in hfs_xattr.c */
3634
3635 int (**hfs_vnodeop_p)(void *);
3636
3637 #define VOPFUNC int (*)(void *)
3638
3639 struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
3640 { &vnop_default_desc, (VOPFUNC)vn_default_error },
3641 { &vnop_lookup_desc, (VOPFUNC)hfs_vnop_lookup }, /* lookup */
3642 { &vnop_create_desc, (VOPFUNC)hfs_vnop_create }, /* create */
3643 { &vnop_mknod_desc, (VOPFUNC)hfs_vnop_mknod }, /* mknod */
3644 { &vnop_open_desc, (VOPFUNC)hfs_vnop_open }, /* open */
3645 { &vnop_close_desc, (VOPFUNC)hfs_vnop_close }, /* close */
3646 { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */
3647 { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */
3648 { &vnop_read_desc, (VOPFUNC)hfs_vnop_read }, /* read */
3649 { &vnop_write_desc, (VOPFUNC)hfs_vnop_write }, /* write */
3650 { &vnop_ioctl_desc, (VOPFUNC)hfs_vnop_ioctl }, /* ioctl */
3651 { &vnop_select_desc, (VOPFUNC)hfs_vnop_select }, /* select */
3652 { &vnop_revoke_desc, (VOPFUNC)nop_revoke }, /* revoke */
3653 { &vnop_exchange_desc, (VOPFUNC)hfs_vnop_exchange }, /* exchange */
3654 { &vnop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */
3655 { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */
3656 { &vnop_remove_desc, (VOPFUNC)hfs_vnop_remove }, /* remove */
3657 { &vnop_link_desc, (VOPFUNC)hfs_vnop_link }, /* link */
3658 { &vnop_rename_desc, (VOPFUNC)hfs_vnop_rename }, /* rename */
3659 { &vnop_mkdir_desc, (VOPFUNC)hfs_vnop_mkdir }, /* mkdir */
3660 { &vnop_rmdir_desc, (VOPFUNC)hfs_vnop_rmdir }, /* rmdir */
3661 { &vnop_symlink_desc, (VOPFUNC)hfs_vnop_symlink }, /* symlink */
3662 { &vnop_readdir_desc, (VOPFUNC)hfs_vnop_readdir }, /* readdir */
3663 { &vnop_readdirattr_desc, (VOPFUNC)hfs_vnop_readdirattr }, /* readdirattr */
3664 { &vnop_readlink_desc, (VOPFUNC)hfs_vnop_readlink }, /* readlink */
3665 { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */
3666 { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */
3667 { &vnop_strategy_desc, (VOPFUNC)hfs_vnop_strategy }, /* strategy */
3668 { &vnop_pathconf_desc, (VOPFUNC)hfs_vnop_pathconf }, /* pathconf */
3669 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
3670 { &vnop_allocate_desc, (VOPFUNC)hfs_vnop_allocate }, /* allocate */
3671 { &vnop_searchfs_desc, (VOPFUNC)hfs_vnop_search }, /* search fs */
3672 { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite }, /* bwrite */
3673 { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* pagein */
3674 { &vnop_pageout_desc,(VOPFUNC) hfs_vnop_pageout }, /* pageout */
3675 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3676 { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */
3677 { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */
3678 { &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap }, /* blockmap */
3679 { &vnop_kqfilt_add_desc, (VOPFUNC)hfs_vnop_kqfiltadd }, /* kqfilt_add */
3680 { &vnop_kqfilt_remove_desc, (VOPFUNC)hfs_vnop_kqfiltremove }, /* kqfilt_remove */
3681 { &vnop_getxattr_desc, (VOPFUNC)hfs_vnop_getxattr},
3682 { &vnop_setxattr_desc, (VOPFUNC)hfs_vnop_setxattr},
3683 { &vnop_removexattr_desc, (VOPFUNC)hfs_vnop_removexattr},
3684 { &vnop_listxattr_desc, (VOPFUNC)hfs_vnop_listxattr},
3685 { NULL, (VOPFUNC)NULL }
3686 };
3687
3688 struct vnodeopv_desc hfs_vnodeop_opv_desc =
3689 { &hfs_vnodeop_p, hfs_vnodeop_entries };
3690
3691 int (**hfs_specop_p)(void *);
3692 struct vnodeopv_entry_desc hfs_specop_entries[] = {
3693 { &vnop_default_desc, (VOPFUNC)vn_default_error },
3694 { &vnop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */
3695 { &vnop_create_desc, (VOPFUNC)spec_create }, /* create */
3696 { &vnop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */
3697 { &vnop_open_desc, (VOPFUNC)spec_open }, /* open */
3698 { &vnop_close_desc, (VOPFUNC)hfsspec_close }, /* close */
3699 { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */
3700 { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */
3701 { &vnop_read_desc, (VOPFUNC)hfsspec_read }, /* read */
3702 { &vnop_write_desc, (VOPFUNC)hfsspec_write }, /* write */
3703 { &vnop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */
3704 { &vnop_select_desc, (VOPFUNC)spec_select }, /* select */
3705 { &vnop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */
3706 { &vnop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */
3707 { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */
3708 { &vnop_remove_desc, (VOPFUNC)spec_remove }, /* remove */
3709 { &vnop_link_desc, (VOPFUNC)spec_link }, /* link */
3710 { &vnop_rename_desc, (VOPFUNC)spec_rename }, /* rename */
3711 { &vnop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */
3712 { &vnop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */
3713 { &vnop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */
3714 { &vnop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */
3715 { &vnop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */
3716 { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */
3717 { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */
3718 { &vnop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */
3719 { &vnop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */
3720 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
3721 { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
3722 { &vnop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */
3723 { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* Pagein */
3724 { &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout }, /* Pageout */
3725 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3726 { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */
3727 { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */
3728 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
3729 };
3730 struct vnodeopv_desc hfs_specop_opv_desc =
3731 { &hfs_specop_p, hfs_specop_entries };
3732
3733 #if FIFO
3734 int (**hfs_fifoop_p)(void *);
3735 struct vnodeopv_entry_desc hfs_fifoop_entries[] = {
3736 { &vnop_default_desc, (VOPFUNC)vn_default_error },
3737 { &vnop_lookup_desc, (VOPFUNC)fifo_lookup }, /* lookup */
3738 { &vnop_create_desc, (VOPFUNC)fifo_create }, /* create */
3739 { &vnop_mknod_desc, (VOPFUNC)fifo_mknod }, /* mknod */
3740 { &vnop_open_desc, (VOPFUNC)fifo_open }, /* open */
3741 { &vnop_close_desc, (VOPFUNC)hfsfifo_close }, /* close */
3742 { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */
3743 { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */
3744 { &vnop_read_desc, (VOPFUNC)hfsfifo_read }, /* read */
3745 { &vnop_write_desc, (VOPFUNC)hfsfifo_write }, /* write */
3746 { &vnop_ioctl_desc, (VOPFUNC)fifo_ioctl }, /* ioctl */
3747 { &vnop_select_desc, (VOPFUNC)fifo_select }, /* select */
3748 { &vnop_revoke_desc, (VOPFUNC)fifo_revoke }, /* revoke */
3749 { &vnop_mmap_desc, (VOPFUNC)fifo_mmap }, /* mmap */
3750 { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */
3751 { &vnop_remove_desc, (VOPFUNC)fifo_remove }, /* remove */
3752 { &vnop_link_desc, (VOPFUNC)fifo_link }, /* link */
3753 { &vnop_rename_desc, (VOPFUNC)fifo_rename }, /* rename */
3754 { &vnop_mkdir_desc, (VOPFUNC)fifo_mkdir }, /* mkdir */
3755 { &vnop_rmdir_desc, (VOPFUNC)fifo_rmdir }, /* rmdir */
3756 { &vnop_symlink_desc, (VOPFUNC)fifo_symlink }, /* symlink */
3757 { &vnop_readdir_desc, (VOPFUNC)fifo_readdir }, /* readdir */
3758 { &vnop_readlink_desc, (VOPFUNC)fifo_readlink }, /* readlink */
3759 { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */
3760 { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */
3761 { &vnop_strategy_desc, (VOPFUNC)fifo_strategy }, /* strategy */
3762 { &vnop_pathconf_desc, (VOPFUNC)fifo_pathconf }, /* pathconf */
3763 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
3764 { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
3765 { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* Pagein */
3766 { &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout }, /* Pageout */
3767 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3768 { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */
3769 { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */
3770 { &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap }, /* blockmap */
3771 { &vnop_kqfilt_add_desc, (VOPFUNC)hfsfifo_kqfilt_add }, /* kqfilt_add */
3772 { &vnop_kqfilt_remove_desc, (VOPFUNC)hfsfifo_kqfilt_remove }, /* kqfilt_remove */
3773 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
3774 };
3775 struct vnodeopv_desc hfs_fifoop_opv_desc =
3776 { &hfs_fifoop_p, hfs_fifoop_entries };
3777 #endif /* FIFO */
3778
3779
3780