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