]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vnops.c
29288a58ec5b54e42a8cef49da6f3b017ef6b087
[apple/xnu.git] / bsd / hfs / hfs_vnops.c
1 /*
2 * Copyright (c) 2000-2005 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 so defer it
1577 * until after the cnode lock is dropped. The
1578 * cnode lock cannot be dropped/reacquired here
1579 * since we might already hold the journal lock.
1580 */
1581 if (!dataforkbusy && cp->c_datafork->ff_blocks && !isbigfile) {
1582 cp->c_flag |= C_NEED_DATA_SETSIZE;
1583 }
1584 if (!rsrcforkbusy && rvp) {
1585 cp->c_flag |= C_NEED_RSRC_SETSIZE;
1586 }
1587 } else {
1588 struct cat_desc cndesc;
1589
1590 // for hard links, re-lookup the name that was passed
1591 // in so we get the correct cnid for the name (as
1592 // opposed to the c_cnid in the cnode which could have
1593 // been changed before this node got locked).
1594 bzero(&cndesc, sizeof(cndesc));
1595 cndesc.cd_nameptr = cnp->cn_nameptr;
1596 cndesc.cd_namelen = cnp->cn_namelen;
1597 cndesc.cd_parentcnid = VTOC(dvp)->c_cnid;
1598 cndesc.cd_hint = VTOC(dvp)->c_childhint;
1599
1600 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1601
1602 if (cat_lookup(hfsmp, &cndesc, 0, NULL, NULL, NULL, &real_cnid) != 0) {
1603 hfs_systemfile_unlock(hfsmp, lockflags);
1604 error = ENOENT;
1605 goto out;
1606 }
1607
1608 hfs_systemfile_unlock(hfsmp, lockflags);
1609 }
1610
1611 if ((error = hfs_start_transaction(hfsmp)) != 0) {
1612 goto out;
1613 }
1614 started_tr = 1;
1615
1616 if (!skip_reserve) {
1617 /*
1618 * Reserve some space in the Catalog file.
1619 */
1620 if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, 0))) {
1621 goto out;
1622 }
1623 got_cookie = 1;
1624 }
1625
1626 /* Remove our entry from the namei cache. */
1627 cache_purge(vp);
1628
1629 // XXXdbg - if we're journaled, kill any dirty symlink buffers
1630 if (hfsmp->jnl && vnode_islnk(vp))
1631 buf_iterate(vp, hfs_removefile_callback, BUF_SKIP_NONLOCKED, (void *)hfsmp);
1632
1633 /*
1634 * Truncate any non-busy forks. Busy forks will
1635 * get trucated when their vnode goes inactive.
1636 *
1637 * Since we're already inside a transaction,
1638 * tell hfs_truncate to skip the ubc_setsize.
1639 *
1640 * (Note: hard links are truncated in VOP_INACTIVE)
1641 */
1642 if ((cp->c_flag & C_HARDLINK) == 0) {
1643 int mode = cp->c_mode;
1644
1645 if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0) {
1646 cp->c_mode = 0; /* Suppress hfs_update */
1647 error = hfs_truncate(vp, (off_t)0, IO_NDELAY, 1, ctx);
1648 cp->c_mode = mode;
1649 if (error)
1650 goto out;
1651 truncated = 1;
1652 }
1653 if (!rsrcforkbusy && rvp) {
1654 cp->c_mode = 0; /* Suppress hfs_update */
1655 error = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 1, ctx);
1656 cp->c_mode = mode;
1657 if (error)
1658 goto out;
1659 truncated = 1;
1660 }
1661 }
1662
1663 /*
1664 * Protect against a race with rename by using the component
1665 * name passed in and parent id from dvp (instead of using
1666 * the cp->c_desc which may have changed).
1667 */
1668 desc.cd_flags = 0;
1669 desc.cd_encoding = cp->c_desc.cd_encoding;
1670 desc.cd_nameptr = cnp->cn_nameptr;
1671 desc.cd_namelen = cnp->cn_namelen;
1672 desc.cd_parentcnid = dcp->c_cnid;
1673 desc.cd_hint = cp->c_desc.cd_hint;
1674 if (real_cnid) {
1675 // if it was a hardlink we had to re-lookup the cnid
1676 desc.cd_cnid = real_cnid;
1677 } else {
1678 desc.cd_cnid = cp->c_cnid;
1679 }
1680 microtime(&tv);
1681
1682 /*
1683 * There are 3 remove cases to consider:
1684 * 1. File is a hardlink ==> remove the link
1685 * 2. File is busy (in use) ==> move/rename the file
1686 * 3. File is not in use ==> remove the file
1687 */
1688
1689 if (cp->c_flag & C_HARDLINK) {
1690 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1691
1692 /* Delete the link record */
1693 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1694 if (error == 0) {
1695 /* Update the parent directory */
1696 if (dcp->c_entries > 0)
1697 dcp->c_entries--;
1698 if (dcp->c_nlink > 0)
1699 dcp->c_nlink--;
1700 dcp->c_ctime = tv.tv_sec;
1701 dcp->c_mtime = tv.tv_sec;
1702 (void ) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1703
1704 if (--cp->c_nlink < 1) {
1705 char inodename[32];
1706 char delname[32];
1707 struct cat_desc to_desc;
1708 struct cat_desc from_desc;
1709
1710 /*
1711 * This is now esentially an open deleted file.
1712 * Rename it to reflect this state which makes
1713 * orphan file cleanup easier (see hfs_remove_orphans).
1714 * Note: a rename failure here is not fatal.
1715 */
1716 MAKE_INODE_NAME(inodename, cp->c_rdev);
1717 bzero(&from_desc, sizeof(from_desc));
1718 from_desc.cd_nameptr = inodename;
1719 from_desc.cd_namelen = strlen(inodename);
1720 from_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
1721 from_desc.cd_flags = 0;
1722 from_desc.cd_cnid = cp->c_fileid;
1723
1724 MAKE_DELETED_NAME(delname, cp->c_fileid);
1725 bzero(&to_desc, sizeof(to_desc));
1726 to_desc.cd_nameptr = delname;
1727 to_desc.cd_namelen = strlen(delname);
1728 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
1729 to_desc.cd_flags = 0;
1730 to_desc.cd_cnid = cp->c_fileid;
1731
1732 error = cat_rename(hfsmp, &from_desc, &hfsmp->hfs_privdir_desc,
1733 &to_desc, (struct cat_desc *)NULL);
1734 if (error != 0) {
1735 panic("hfs_removefile: error %d from cat_rename(%s %s) cp 0x%x\n",
1736 inodename, delname, cp);
1737 }
1738 if (error == 0) {
1739 /* Update the file's state */
1740 cp->c_flag |= C_DELETED;
1741 cp->c_ctime = tv.tv_sec;
1742 (void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
1743 }
1744 } else {
1745 /* Update the file's state */
1746 cp->c_ctime = tv.tv_sec;
1747 (void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
1748 }
1749 }
1750 hfs_systemfile_unlock(hfsmp, lockflags);
1751 if (error != 0)
1752 goto out;
1753
1754 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
1755
1756 } else if (dataforkbusy || rsrcforkbusy || isbigfile) {
1757 char delname[32];
1758 struct cat_desc to_desc;
1759 struct cat_desc todir_desc;
1760
1761 /*
1762 * Orphan this file (move to hidden directory).
1763 */
1764 bzero(&todir_desc, sizeof(todir_desc));
1765 todir_desc.cd_parentcnid = 2;
1766
1767 MAKE_DELETED_NAME(delname, cp->c_fileid);
1768 bzero(&to_desc, sizeof(to_desc));
1769 to_desc.cd_nameptr = delname;
1770 to_desc.cd_namelen = strlen(delname);
1771 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
1772 to_desc.cd_flags = 0;
1773 to_desc.cd_cnid = cp->c_cnid;
1774
1775 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1776
1777 error = cat_rename(hfsmp, &desc, &todir_desc,
1778 &to_desc, (struct cat_desc *)NULL);
1779
1780 if (error == 0) {
1781 hfsmp->hfs_privdir_attr.ca_entries++;
1782 (void) cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
1783 &hfsmp->hfs_privdir_attr, NULL, NULL);
1784
1785 /* Update the parent directory */
1786 if (dcp->c_entries > 0)
1787 dcp->c_entries--;
1788 if (dcp->c_nlink > 0)
1789 dcp->c_nlink--;
1790 dcp->c_ctime = tv.tv_sec;
1791 dcp->c_mtime = tv.tv_sec;
1792 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1793
1794 /* Update the file's state */
1795 cp->c_flag |= C_DELETED;
1796 cp->c_ctime = tv.tv_sec;
1797 --cp->c_nlink;
1798 (void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
1799 }
1800 hfs_systemfile_unlock(hfsmp, lockflags);
1801 if (error)
1802 goto out;
1803
1804 } else /* Not busy */ {
1805
1806 if (cp->c_blocks > 0) {
1807 printf("hfs_remove: attempting to delete a non-empty file %s\n",
1808 cp->c_desc.cd_nameptr);
1809 error = EBUSY;
1810 goto out;
1811 }
1812
1813 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1814
1815 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1816
1817 if (error && error != ENXIO && error != ENOENT && truncated) {
1818 if ((cp->c_datafork && cp->c_datafork->ff_size != 0) ||
1819 (cp->c_rsrcfork && cp->c_rsrcfork->ff_size != 0)) {
1820 panic("hfs: remove: couldn't delete a truncated file! (%d, data sz %lld; rsrc sz %lld)",
1821 error, cp->c_datafork->ff_size, cp->c_rsrcfork->ff_size);
1822 } else {
1823 printf("hfs: remove: strangely enough, deleting truncated file %s (%d) got err %d\n",
1824 cp->c_desc.cd_nameptr, cp->c_attr.ca_fileid, error);
1825 }
1826 }
1827 if (error == 0) {
1828 /* Delete any attributes, ignore errors */
1829 (void) hfs_removeallattr(hfsmp, cp->c_fileid);
1830
1831 /* Update the parent directory */
1832 if (dcp->c_entries > 0)
1833 dcp->c_entries--;
1834 if (dcp->c_nlink > 0)
1835 dcp->c_nlink--;
1836 dcp->c_ctime = tv.tv_sec;
1837 dcp->c_mtime = tv.tv_sec;
1838 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1839 }
1840 hfs_systemfile_unlock(hfsmp, lockflags);
1841 if (error)
1842 goto out;
1843
1844 #if QUOTA
1845 (void)hfs_chkiq(cp, -1, NOCRED, 0);
1846 #endif /* QUOTA */
1847
1848 cp->c_mode = 0;
1849 truncated = 0; // because the catalog entry is gone
1850 cp->c_flag |= C_NOEXISTS;
1851 cp->c_touch_chgtime = TRUE; /* XXX needed ? */
1852 --cp->c_nlink;
1853
1854 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
1855 }
1856
1857 /*
1858 * All done with this cnode's descriptor...
1859 *
1860 * Note: all future catalog calls for this cnode must be
1861 * by fileid only. This is OK for HFS (which doesn't have
1862 * file thread records) since HFS doesn't support hard
1863 * links or the removal of busy files.
1864 */
1865 cat_releasedesc(&cp->c_desc);
1866
1867 HFS_KNOTE(dvp, NOTE_WRITE);
1868
1869 out:
1870 if (got_cookie) {
1871 cat_postflight(hfsmp, &cookie, 0);
1872 }
1873
1874 /* Commit the truncation to the catalog record */
1875 if (truncated) {
1876 cp->c_flag |= C_FORCEUPDATE;
1877 cp->c_touch_chgtime = TRUE;
1878 cp->c_touch_modtime = TRUE;
1879 (void) hfs_update(vp, 0);
1880 }
1881
1882 if (started_tr) {
1883 hfs_end_transaction(hfsmp);
1884 }
1885
1886 HFS_KNOTE(vp, NOTE_DELETE);
1887 if (rvp) {
1888 HFS_KNOTE(rvp, NOTE_DELETE);
1889 /* Defer the vnode_put on rvp until the hfs_unlock(). */
1890 cp->c_flag |= C_NEED_RVNODE_PUT;
1891 };
1892
1893 return (error);
1894 }
1895
1896
1897 __private_extern__ void
1898 replace_desc(struct cnode *cp, struct cat_desc *cdp)
1899 {
1900 if (&cp->c_desc == cdp) {
1901 return;
1902 }
1903
1904 /* First release allocated name buffer */
1905 if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) {
1906 char *name = cp->c_desc.cd_nameptr;
1907
1908 cp->c_desc.cd_nameptr = 0;
1909 cp->c_desc.cd_namelen = 0;
1910 cp->c_desc.cd_flags &= ~CD_HASBUF;
1911 vfs_removename(name);
1912 }
1913 bcopy(cdp, &cp->c_desc, sizeof(cp->c_desc));
1914
1915 /* Cnode now owns the name buffer */
1916 cdp->cd_nameptr = 0;
1917 cdp->cd_namelen = 0;
1918 cdp->cd_flags &= ~CD_HASBUF;
1919 }
1920
1921
1922 /*
1923 * Rename a cnode.
1924 *
1925 * The VFS layer guarantees that:
1926 * - source and destination will either both be directories, or
1927 * both not be directories.
1928 * - all the vnodes are from the same file system
1929 *
1930 * When the target is a directory, HFS must ensure that its empty.
1931 */
1932 static int
1933 hfs_vnop_rename(ap)
1934 struct vnop_rename_args /* {
1935 struct vnode *a_fdvp;
1936 struct vnode *a_fvp;
1937 struct componentname *a_fcnp;
1938 struct vnode *a_tdvp;
1939 struct vnode *a_tvp;
1940 struct componentname *a_tcnp;
1941 vfs_context_t a_context;
1942 } */ *ap;
1943 {
1944 struct vnode *tvp = ap->a_tvp;
1945 struct vnode *tdvp = ap->a_tdvp;
1946 struct vnode *fvp = ap->a_fvp;
1947 struct vnode *fdvp = ap->a_fdvp;
1948 struct componentname *tcnp = ap->a_tcnp;
1949 struct componentname *fcnp = ap->a_fcnp;
1950 struct proc *p = vfs_context_proc(ap->a_context);
1951 struct cnode *fcp;
1952 struct cnode *fdcp;
1953 struct cnode *tdcp;
1954 struct cnode *tcp;
1955 struct cat_desc from_desc;
1956 struct cat_desc to_desc;
1957 struct cat_desc out_desc;
1958 struct hfsmount *hfsmp;
1959 cat_cookie_t cookie;
1960 int tvp_deleted = 0;
1961 int started_tr = 0, got_cookie = 0;
1962 int took_trunc_lock = 0;
1963 int lockflags;
1964 int error;
1965
1966 /* When tvp exist, take the truncate lock for the hfs_removefile(). */
1967 if (tvp && vnode_isreg(tvp)) {
1968 hfs_lock_truncate(VTOC(tvp), TRUE);
1969 took_trunc_lock = 1;
1970 }
1971
1972 error = hfs_lockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL,
1973 HFS_EXCLUSIVE_LOCK);
1974 if (error) {
1975 if (took_trunc_lock)
1976 hfs_unlock_truncate(VTOC(tvp));
1977 return (error);
1978 }
1979
1980 fdcp = VTOC(fdvp);
1981 fcp = VTOC(fvp);
1982 tdcp = VTOC(tdvp);
1983 tcp = tvp ? VTOC(tvp) : NULL;
1984 hfsmp = VTOHFS(tdvp);
1985
1986 /* Check for a race against unlink. */
1987 if (fcp->c_flag & C_NOEXISTS) {
1988 error = ENOENT;
1989 goto out;
1990 }
1991
1992 /*
1993 * The following edge case is caught here:
1994 * (to cannot be a descendent of from)
1995 *
1996 * o fdvp
1997 * /
1998 * /
1999 * o fvp
2000 * \
2001 * \
2002 * o tdvp
2003 * /
2004 * /
2005 * o tvp
2006 */
2007 if (tdcp->c_parentcnid == fcp->c_cnid) {
2008 error = EINVAL;
2009 goto out;
2010 }
2011
2012 /*
2013 * The following two edge cases are caught here:
2014 * (note tvp is not empty)
2015 *
2016 * o tdvp o tdvp
2017 * / /
2018 * / /
2019 * o tvp tvp o fdvp
2020 * \ \
2021 * \ \
2022 * o fdvp o fvp
2023 * /
2024 * /
2025 * o fvp
2026 */
2027 if (tvp && vnode_isdir(tvp) && (tcp->c_entries != 0) && fvp != tvp) {
2028 error = ENOTEMPTY;
2029 goto out;
2030 }
2031
2032 /*
2033 * The following edge case is caught here:
2034 * (the from child and parent are the same)
2035 *
2036 * o tdvp
2037 * /
2038 * /
2039 * fdvp o fvp
2040 */
2041 if (fdvp == fvp) {
2042 error = EINVAL;
2043 goto out;
2044 }
2045
2046 /*
2047 * Make sure "from" vnode and its parent are changeable.
2048 */
2049 if ((fcp->c_flags & (IMMUTABLE | APPEND)) || (fdcp->c_flags & APPEND)) {
2050 error = EPERM;
2051 goto out;
2052 }
2053
2054 /*
2055 * If the destination parent directory is "sticky", then the
2056 * user must own the parent directory, or the destination of
2057 * the rename, otherwise the destination may not be changed
2058 * (except by root). This implements append-only directories.
2059 *
2060 * Note that checks for immutable and write access are done
2061 * by the call to hfs_removefile.
2062 */
2063 if (tvp && (tdcp->c_mode & S_ISTXT) &&
2064 (suser(vfs_context_ucred(tcnp->cn_context), NULL)) &&
2065 (kauth_cred_getuid(vfs_context_ucred(tcnp->cn_context)) != tdcp->c_uid) &&
2066 (hfs_owner_rights(hfsmp, tcp->c_uid, vfs_context_ucred(tcnp->cn_context), p, false)) ) {
2067 error = EPERM;
2068 goto out;
2069 }
2070
2071 #if QUOTA
2072 if (tvp)
2073 (void)hfs_getinoquota(tcp);
2074 #endif
2075 /* Preflighting done, take fvp out of the name space. */
2076 cache_purge(fvp);
2077
2078 /*
2079 * When a file moves out of "Cleanup At Startup"
2080 * we can drop its NODUMP status.
2081 */
2082 if ((fcp->c_flags & UF_NODUMP) &&
2083 vnode_isreg(fvp) &&
2084 (fdvp != tdvp) &&
2085 (fdcp->c_desc.cd_nameptr != NULL) &&
2086 (strcmp(fdcp->c_desc.cd_nameptr, CARBON_TEMP_DIR_NAME) == 0)) {
2087 fcp->c_flags &= ~UF_NODUMP;
2088 fcp->c_touch_chgtime = TRUE;
2089 (void) hfs_update(fvp, 0);
2090 }
2091
2092 bzero(&from_desc, sizeof(from_desc));
2093 from_desc.cd_nameptr = fcnp->cn_nameptr;
2094 from_desc.cd_namelen = fcnp->cn_namelen;
2095 from_desc.cd_parentcnid = fdcp->c_cnid;
2096 from_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
2097 from_desc.cd_cnid = fcp->c_cnid;
2098
2099 bzero(&to_desc, sizeof(to_desc));
2100 to_desc.cd_nameptr = tcnp->cn_nameptr;
2101 to_desc.cd_namelen = tcnp->cn_namelen;
2102 to_desc.cd_parentcnid = tdcp->c_cnid;
2103 to_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
2104 to_desc.cd_cnid = fcp->c_cnid;
2105
2106 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2107 goto out;
2108 }
2109 started_tr = 1;
2110
2111 // if it's a hardlink then re-lookup the name so
2112 // that we get the correct cnid in from_desc (see
2113 // the comment in hfs_removefile for more details)
2114 //
2115 if (fcp->c_flag & C_HARDLINK) {
2116 struct cat_desc tmpdesc;
2117 cnid_t real_cnid;
2118
2119 bzero(&tmpdesc, sizeof(tmpdesc));
2120 tmpdesc.cd_nameptr = fcnp->cn_nameptr;
2121 tmpdesc.cd_namelen = fcnp->cn_namelen;
2122 tmpdesc.cd_parentcnid = fdcp->c_cnid;
2123 tmpdesc.cd_hint = fdcp->c_childhint;
2124
2125 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2126
2127 if (cat_lookup(hfsmp, &tmpdesc, 0, NULL, NULL, NULL, &real_cnid) != 0) {
2128 hfs_systemfile_unlock(hfsmp, lockflags);
2129 goto out;
2130 }
2131
2132 // use the real cnid instead of whatever happened to be there
2133 from_desc.cd_cnid = real_cnid;
2134 hfs_systemfile_unlock(hfsmp, lockflags);
2135 }
2136
2137 /*
2138 * Reserve some space in the Catalog file.
2139 */
2140 bzero(&cookie, sizeof(cookie));
2141 if ((error = cat_preflight(hfsmp, CAT_RENAME + CAT_DELETE, &cookie, p))) {
2142 goto out;
2143 }
2144 got_cookie = 1;
2145
2146 /*
2147 * If the destination exists then it may need to be removed.
2148 */
2149 if (tvp) {
2150 /*
2151 * When fvp matches tvp they must be case variants
2152 * or hard links.
2153 */
2154 if (fvp == tvp) {
2155 /*
2156 * If this a hard link with different parents
2157 * and its not a case variant then tvp should
2158 * be removed.
2159 */
2160 if (!((fcp->c_flag & C_HARDLINK) &&
2161 ((fdvp != tdvp) ||
2162 (hfs_namecmp(fcnp->cn_nameptr, fcnp->cn_namelen,
2163 tcnp->cn_nameptr, tcnp->cn_namelen) != 0)))) {
2164 goto skip;
2165 }
2166 } else {
2167 cache_purge(tvp);
2168 }
2169
2170 if (vnode_isdir(tvp))
2171 error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_SKIP_RESERVE);
2172 else {
2173 error = hfs_removefile(tdvp, tvp, tcnp, 0, HFSRM_SKIP_RESERVE);
2174 }
2175
2176 if (error)
2177 goto out;
2178 tvp_deleted = 1;
2179 }
2180 skip:
2181 /*
2182 * All done with tvp and fvp
2183 */
2184
2185 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
2186 error = cat_rename(hfsmp, &from_desc, &tdcp->c_desc, &to_desc, &out_desc);
2187 hfs_systemfile_unlock(hfsmp, lockflags);
2188
2189 if (error) {
2190 goto out;
2191 }
2192
2193 /* Invalidate negative cache entries in the destination directory */
2194 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
2195 cache_purge_negatives(tdvp);
2196
2197 /* Update cnode's catalog descriptor */
2198 replace_desc(fcp, &out_desc);
2199 fcp->c_parentcnid = tdcp->c_cnid;
2200 fcp->c_hint = 0;
2201
2202 hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_RMDIR : VOL_RMFILE,
2203 (fdcp->c_cnid == kHFSRootFolderID));
2204 hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_MKDIR : VOL_MKFILE,
2205 (tdcp->c_cnid == kHFSRootFolderID));
2206
2207 /* Update both parent directories. */
2208 if (fdvp != tdvp) {
2209 tdcp->c_nlink++;
2210 tdcp->c_entries++;
2211 if (fdcp->c_nlink > 0)
2212 fdcp->c_nlink--;
2213 if (fdcp->c_entries > 0)
2214 fdcp->c_entries--;
2215 fdcp->c_touch_chgtime = TRUE;
2216 fdcp->c_touch_modtime = TRUE;
2217
2218 fdcp->c_flag |= C_FORCEUPDATE; // XXXdbg - force it out!
2219 (void) hfs_update(fdvp, 0);
2220 }
2221 tdcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
2222 tdcp->c_touch_chgtime = TRUE;
2223 tdcp->c_touch_modtime = TRUE;
2224
2225 tdcp->c_flag |= C_FORCEUPDATE; // XXXdbg - force it out!
2226 (void) hfs_update(tdvp, 0);
2227 out:
2228 if (got_cookie) {
2229 cat_postflight(hfsmp, &cookie, p);
2230 }
2231 if (started_tr) {
2232 hfs_end_transaction(hfsmp);
2233 }
2234
2235 /* Note that if hfs_removedir or hfs_removefile was invoked above they will already have
2236 generated a NOTE_WRITE for tdvp and a NOTE_DELETE for tvp.
2237 */
2238 if (error == 0) {
2239 HFS_KNOTE(fvp, NOTE_RENAME);
2240 HFS_KNOTE(fdvp, NOTE_WRITE);
2241 if (tdvp != fdvp) HFS_KNOTE(tdvp, NOTE_WRITE);
2242 };
2243
2244 if (took_trunc_lock)
2245 hfs_unlock_truncate(VTOC(tvp));
2246
2247 hfs_unlockfour(fdcp, fcp, tdcp, tcp);
2248
2249 /* After tvp is removed the only acceptable error is EIO */
2250 if (error && tvp_deleted)
2251 error = EIO;
2252
2253 return (error);
2254 }
2255
2256
2257 /*
2258 * Make a directory.
2259 */
2260 static int
2261 hfs_vnop_mkdir(struct vnop_mkdir_args *ap)
2262 {
2263 /***** HACK ALERT ********/
2264 ap->a_cnp->cn_flags |= MAKEENTRY;
2265 return hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context);
2266 }
2267
2268
2269 /*
2270 * Create a symbolic link.
2271 */
2272 static int
2273 hfs_vnop_symlink(struct vnop_symlink_args *ap)
2274 {
2275 struct vnode **vpp = ap->a_vpp;
2276 struct vnode *dvp = ap->a_dvp;
2277 struct vnode *vp = NULL;
2278 struct hfsmount *hfsmp;
2279 struct filefork *fp;
2280 struct buf *bp = NULL;
2281 char *datap;
2282 int started_tr = 0;
2283 int len, error;
2284
2285 /* HFS standard disks don't support symbolic links */
2286 if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord)
2287 return (ENOTSUP);
2288
2289 /* Check for empty target name */
2290 if (ap->a_target[0] == 0)
2291 return (EINVAL);
2292
2293 /* Create the vnode */
2294 ap->a_vap->va_mode |= S_IFLNK;
2295 if ((error = hfs_makenode(dvp, vpp, ap->a_cnp, ap->a_vap, ap->a_context))) {
2296 goto out;
2297 }
2298 vp = *vpp;
2299 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
2300 return (error);
2301 fp = VTOF(vp);
2302 hfsmp = VTOHFS(dvp);
2303 len = strlen(ap->a_target);
2304
2305 #if QUOTA
2306 (void)hfs_getinoquota(VTOC(vp));
2307 #endif /* QUOTA */
2308
2309 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2310 goto out;
2311 }
2312 started_tr = 1;
2313
2314 /*
2315 * Allocate space for the link.
2316 *
2317 * Since we're already inside a transaction,
2318 * tell hfs_truncate to skip the ubc_setsize.
2319 *
2320 * Don't need truncate lock since a symlink is treated as a system file.
2321 */
2322 error = hfs_truncate(vp, len, IO_NOZEROFILL, 1, ap->a_context);
2323 if (error)
2324 goto out; /* XXX need to remove link */
2325
2326 /* Write the link to disk */
2327 bp = buf_getblk(vp, (daddr64_t)0, roundup((int)fp->ff_size, VTOHFS(vp)->hfs_phys_block_size),
2328 0, 0, BLK_META);
2329 if (hfsmp->jnl) {
2330 journal_modify_block_start(hfsmp->jnl, bp);
2331 }
2332 datap = (char *)buf_dataptr(bp);
2333 bzero(datap, buf_size(bp));
2334 bcopy(ap->a_target, datap, len);
2335
2336 if (hfsmp->jnl) {
2337 journal_modify_block_end(hfsmp->jnl, bp);
2338 } else {
2339 buf_bawrite(bp);
2340 }
2341 /*
2342 * We defered the ubc_setsize for hfs_truncate
2343 * since we were inside a transaction.
2344 *
2345 * We don't need to drop the cnode lock here
2346 * since this is a symlink.
2347 */
2348 ubc_setsize(vp, len);
2349 out:
2350 if (started_tr)
2351 hfs_end_transaction(hfsmp);
2352 if (vp) {
2353 hfs_unlock(VTOC(vp));
2354 }
2355 return (error);
2356 }
2357
2358
2359 /* structures to hold a "." or ".." directory entry */
2360 struct hfs_stddotentry {
2361 u_int32_t d_fileno; /* unique file number */
2362 u_int16_t d_reclen; /* length of this structure */
2363 u_int8_t d_type; /* dirent file type */
2364 u_int8_t d_namlen; /* len of filename */
2365 char d_name[4]; /* "." or ".." */
2366 };
2367
2368 struct hfs_extdotentry {
2369 u_int64_t d_fileno; /* unique file number */
2370 u_int64_t d_seekoff; /* seek offset (optional, used by servers) */
2371 u_int16_t d_reclen; /* length of this structure */
2372 u_int16_t d_namlen; /* len of filename */
2373 u_int8_t d_type; /* dirent file type */
2374 u_char d_name[3]; /* "." or ".." */
2375 };
2376
2377 typedef union {
2378 struct hfs_stddotentry std;
2379 struct hfs_extdotentry ext;
2380 } hfs_dotentry_t;
2381
2382 /*
2383 * hfs_vnop_readdir reads directory entries into the buffer pointed
2384 * to by uio, in a filesystem independent format. Up to uio_resid
2385 * bytes of data can be transferred. The data in the buffer is a
2386 * series of packed dirent structures where each one contains the
2387 * following entries:
2388 *
2389 * u_int32_t d_fileno; // file number of entry
2390 * u_int16_t d_reclen; // length of this record
2391 * u_int8_t d_type; // file type
2392 * u_int8_t d_namlen; // length of string in d_name
2393 * char d_name[MAXNAMELEN+1]; // null terminated file name
2394 *
2395 * The current position (uio_offset) refers to the next block of
2396 * entries. The offset can only be set to a value previously
2397 * returned by hfs_vnop_readdir or zero. This offset does not have
2398 * to match the number of bytes returned (in uio_resid).
2399 *
2400 * In fact, the offset used by HFS is essentially an index (26 bits)
2401 * with a tag (6 bits). The tag is for associating the next request
2402 * with the current request. This enables us to have multiple threads
2403 * reading the directory while the directory is also being modified.
2404 *
2405 * Each tag/index pair is tied to a unique directory hint. The hint
2406 * contains information (filename) needed to build the catalog b-tree
2407 * key for finding the next set of entries.
2408 */
2409 static int
2410 hfs_vnop_readdir(ap)
2411 struct vnop_readdir_args /* {
2412 vnode_t a_vp;
2413 uio_t a_uio;
2414 int a_flags;
2415 int *a_eofflag;
2416 int *a_numdirent;
2417 vfs_context_t a_context;
2418 } */ *ap;
2419 {
2420 struct vnode *vp = ap->a_vp;
2421 uio_t uio = ap->a_uio;
2422 struct cnode *cp;
2423 struct hfsmount *hfsmp;
2424 directoryhint_t *dirhint = NULL;
2425 directoryhint_t localhint;
2426 off_t offset;
2427 off_t startoffset;
2428 int error = 0;
2429 int eofflag = 0;
2430 user_addr_t user_start = 0;
2431 user_size_t user_len = 0;
2432 int index;
2433 unsigned int tag;
2434 int items;
2435 int lockflags;
2436 int extended;
2437 int nfs_cookies;
2438 caddr_t bufstart;
2439 cnid_t cnid_hint = 0;
2440
2441 items = 0;
2442 startoffset = offset = uio_offset(uio);
2443 bufstart = CAST_DOWN(caddr_t, uio_iov_base(uio));
2444 extended = (ap->a_flags & VNODE_READDIR_EXTENDED);
2445 nfs_cookies = extended && (ap->a_flags & VNODE_READDIR_REQSEEKOFF);
2446
2447 /* Sanity check the uio data. */
2448 if ((uio_iovcnt(uio) > 1) ||
2449 (uio_resid(uio) < (int)sizeof(struct dirent))) {
2450 return (EINVAL);
2451 }
2452 /* Note that the dirhint calls require an exclusive lock. */
2453 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
2454 return (error);
2455 cp = VTOC(vp);
2456 hfsmp = VTOHFS(vp);
2457
2458 /* Pick up cnid hint (if any). */
2459 if (nfs_cookies) {
2460 cnid_hint = (cnid_t)(uio_offset(uio) >> 32);
2461 uio_setoffset(uio, uio_offset(uio) & 0x00000000ffffffffLL);
2462 if (cnid_hint == INT_MAX) { /* searching pass the last item */
2463 eofflag = 1;
2464 goto out;
2465 }
2466 }
2467 /*
2468 * Synthesize entries for "." and ".."
2469 */
2470 if (offset == 0) {
2471 hfs_dotentry_t dotentry[2];
2472 size_t uiosize;
2473
2474 if (extended) {
2475 struct hfs_extdotentry *entry = &dotentry[0].ext;
2476
2477 entry->d_fileno = cp->c_cnid;
2478 entry->d_reclen = sizeof(struct hfs_extdotentry);
2479 entry->d_type = DT_DIR;
2480 entry->d_namlen = 1;
2481 entry->d_name[0] = '.';
2482 entry->d_name[1] = '\0';
2483 entry->d_name[2] = '\0';
2484 entry->d_seekoff = 1;
2485
2486 ++entry;
2487 entry->d_fileno = cp->c_parentcnid;
2488 entry->d_reclen = sizeof(struct hfs_extdotentry);
2489 entry->d_type = DT_DIR;
2490 entry->d_namlen = 2;
2491 entry->d_name[0] = '.';
2492 entry->d_name[1] = '.';
2493 entry->d_name[2] = '\0';
2494 entry->d_seekoff = 2;
2495 uiosize = 2 * sizeof(struct hfs_extdotentry);
2496 } else {
2497 struct hfs_stddotentry *entry = &dotentry[0].std;
2498
2499 entry->d_fileno = cp->c_cnid;
2500 entry->d_reclen = sizeof(struct hfs_stddotentry);
2501 entry->d_type = DT_DIR;
2502 entry->d_namlen = 1;
2503 *(int *)&entry->d_name[0] = 0;
2504 entry->d_name[0] = '.';
2505
2506 ++entry;
2507 entry->d_fileno = cp->c_parentcnid;
2508 entry->d_reclen = sizeof(struct hfs_stddotentry);
2509 entry->d_type = DT_DIR;
2510 entry->d_namlen = 2;
2511 *(int *)&entry->d_name[0] = 0;
2512 entry->d_name[0] = '.';
2513 entry->d_name[1] = '.';
2514 uiosize = 2 * sizeof(struct hfs_stddotentry);
2515 }
2516 if ((error = uiomove((caddr_t)&dotentry, uiosize, uio))) {
2517 goto out;
2518 }
2519 offset += 2;
2520 }
2521
2522 /* If there are no real entries then we're done. */
2523 if (cp->c_entries == 0) {
2524 error = 0;
2525 eofflag = 1;
2526 uio_setoffset(uio, offset);
2527 goto seekoffcalc;
2528 }
2529
2530 //
2531 // We have to lock the user's buffer here so that we won't
2532 // fault on it after we've acquired a shared lock on the
2533 // catalog file. The issue is that you can get a 3-way
2534 // deadlock if someone else starts a transaction and then
2535 // tries to lock the catalog file but can't because we're
2536 // here and we can't service our page fault because VM is
2537 // blocked trying to start a transaction as a result of
2538 // trying to free up pages for our page fault. It's messy
2539 // but it does happen on dual-procesors that are paging
2540 // heavily (see radar 3082639 for more info). By locking
2541 // the buffer up-front we prevent ourselves from faulting
2542 // while holding the shared catalog file lock.
2543 //
2544 // Fortunately this and hfs_search() are the only two places
2545 // currently (10/30/02) that can fault on user data with a
2546 // shared lock on the catalog file.
2547 //
2548 if (hfsmp->jnl && uio_isuserspace(uio)) {
2549 user_start = uio_curriovbase(uio);
2550 user_len = uio_curriovlen(uio);
2551
2552 if ((error = vslock(user_start, user_len)) != 0) {
2553 user_start = 0;
2554 goto out;
2555 }
2556 }
2557 /* Convert offset into a catalog directory index. */
2558 index = (offset & HFS_INDEX_MASK) - 2;
2559 tag = offset & ~HFS_INDEX_MASK;
2560
2561 /* Lock catalog during cat_findname and cat_getdirentries. */
2562 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2563
2564 /* When called from NFS, try and resolve a cnid hint. */
2565 if (nfs_cookies && cnid_hint != 0) {
2566 if (cat_findname(hfsmp, cnid_hint, &localhint.dh_desc) == 0) {
2567 if ( localhint.dh_desc.cd_parentcnid == cp->c_cnid) {
2568 localhint.dh_index = index - 1;
2569 localhint.dh_time = 0;
2570 bzero(&localhint.dh_link, sizeof(localhint.dh_link));
2571 dirhint = &localhint; /* don't forget to release the descriptor */
2572 } else {
2573 cat_releasedesc(&localhint.dh_desc);
2574 }
2575 }
2576 }
2577
2578 /* Get a directory hint (cnode must be locked exclusive) */
2579 if (dirhint == NULL) {
2580 dirhint = hfs_getdirhint(cp, ((index - 1) & HFS_INDEX_MASK) | tag);
2581
2582 /* Hide tag from catalog layer. */
2583 dirhint->dh_index &= HFS_INDEX_MASK;
2584 if (dirhint->dh_index == HFS_INDEX_MASK) {
2585 dirhint->dh_index = -1;
2586 }
2587 }
2588
2589 /* Pack the buffer with dirent entries. */
2590 error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, uio, extended, &items, &eofflag);
2591
2592 hfs_systemfile_unlock(hfsmp, lockflags);
2593
2594 if (error != 0) {
2595 goto out;
2596 }
2597
2598 /* Get index to the next item */
2599 index += items;
2600
2601 if (items >= (int)cp->c_entries) {
2602 eofflag = 1;
2603 }
2604
2605 /* Convert catalog directory index back into an offset. */
2606 while (tag == 0)
2607 tag = (++cp->c_dirhinttag) << HFS_INDEX_BITS;
2608 uio_setoffset(uio, (index + 2) | tag);
2609 dirhint->dh_index |= tag;
2610
2611 seekoffcalc:
2612 cp->c_touch_acctime = TRUE;
2613
2614 if (ap->a_numdirent) {
2615 if (startoffset == 0)
2616 items += 2;
2617 *ap->a_numdirent = items;
2618 }
2619
2620 out:
2621 if (hfsmp->jnl && user_start) {
2622 vsunlock(user_start, user_len, TRUE);
2623 }
2624 /* If we didn't do anything then go ahead and dump the hint. */
2625 if ((dirhint != NULL) &&
2626 (dirhint != &localhint) &&
2627 (uio_offset(uio) == startoffset)) {
2628 hfs_reldirhint(cp, dirhint);
2629 eofflag = 1;
2630 }
2631 if (ap->a_eofflag) {
2632 *ap->a_eofflag = eofflag;
2633 }
2634 if (dirhint == &localhint) {
2635 cat_releasedesc(&localhint.dh_desc);
2636 }
2637 hfs_unlock(cp);
2638 return (error);
2639 }
2640
2641
2642 /*
2643 * Read contents of a symbolic link.
2644 */
2645 static int
2646 hfs_vnop_readlink(ap)
2647 struct vnop_readlink_args /* {
2648 struct vnode *a_vp;
2649 struct uio *a_uio;
2650 vfs_context_t a_context;
2651 } */ *ap;
2652 {
2653 struct vnode *vp = ap->a_vp;
2654 struct cnode *cp;
2655 struct filefork *fp;
2656 int error;
2657
2658 if (!vnode_islnk(vp))
2659 return (EINVAL);
2660
2661 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
2662 return (error);
2663 cp = VTOC(vp);
2664 fp = VTOF(vp);
2665
2666 /* Zero length sym links are not allowed */
2667 if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
2668 VTOVCB(vp)->vcbFlags |= kHFS_DamagedVolume;
2669 error = EINVAL;
2670 goto exit;
2671 }
2672
2673 /* Cache the path so we don't waste buffer cache resources */
2674 if (fp->ff_symlinkptr == NULL) {
2675 struct buf *bp = NULL;
2676
2677 MALLOC(fp->ff_symlinkptr, char *, fp->ff_size, M_TEMP, M_WAITOK);
2678 error = (int)buf_meta_bread(vp, (daddr64_t)0,
2679 roundup((int)fp->ff_size,
2680 VTOHFS(vp)->hfs_phys_block_size),
2681 vfs_context_ucred(ap->a_context), &bp);
2682 if (error) {
2683 if (bp)
2684 buf_brelse(bp);
2685 if (fp->ff_symlinkptr) {
2686 FREE(fp->ff_symlinkptr, M_TEMP);
2687 fp->ff_symlinkptr = NULL;
2688 }
2689 goto exit;
2690 }
2691 bcopy((char *)buf_dataptr(bp), fp->ff_symlinkptr, (size_t)fp->ff_size);
2692
2693 if (VTOHFS(vp)->jnl && (buf_flags(bp) & B_LOCKED) == 0) {
2694 buf_markinvalid(bp); /* data no longer needed */
2695 }
2696 buf_brelse(bp);
2697 }
2698 error = uiomove((caddr_t)fp->ff_symlinkptr, (int)fp->ff_size, ap->a_uio);
2699
2700 /*
2701 * Keep track blocks read
2702 */
2703 if ((VTOHFS(vp)->hfc_stage == HFC_RECORDING) && (error == 0)) {
2704
2705 /*
2706 * If this file hasn't been seen since the start of
2707 * the current sampling period then start over.
2708 */
2709 if (cp->c_atime < VTOHFS(vp)->hfc_timebase)
2710 VTOF(vp)->ff_bytesread = fp->ff_size;
2711 else
2712 VTOF(vp)->ff_bytesread += fp->ff_size;
2713
2714 // if (VTOF(vp)->ff_bytesread > fp->ff_size)
2715 // cp->c_touch_acctime = TRUE;
2716 }
2717
2718 exit:
2719 hfs_unlock(cp);
2720 return (error);
2721 }
2722
2723
2724 /*
2725 * Get configurable pathname variables.
2726 */
2727 static int
2728 hfs_vnop_pathconf(ap)
2729 struct vnop_pathconf_args /* {
2730 struct vnode *a_vp;
2731 int a_name;
2732 int *a_retval;
2733 vfs_context_t a_context;
2734 } */ *ap;
2735 {
2736 switch (ap->a_name) {
2737 case _PC_LINK_MAX:
2738 if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD)
2739 *ap->a_retval = 1;
2740 else
2741 *ap->a_retval = HFS_LINK_MAX;
2742 break;
2743 case _PC_NAME_MAX:
2744 if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD)
2745 *ap->a_retval = kHFSMaxFileNameChars; /* 255 */
2746 else
2747 *ap->a_retval = kHFSPlusMaxFileNameChars; /* 31 */
2748 break;
2749 case _PC_PATH_MAX:
2750 *ap->a_retval = PATH_MAX; /* 1024 */
2751 break;
2752 case _PC_PIPE_BUF:
2753 *ap->a_retval = PIPE_BUF;
2754 break;
2755 case _PC_CHOWN_RESTRICTED:
2756 *ap->a_retval = 1;
2757 break;
2758 case _PC_NO_TRUNC:
2759 *ap->a_retval = 0;
2760 break;
2761 case _PC_NAME_CHARS_MAX:
2762 *ap->a_retval = kHFSPlusMaxFileNameChars;
2763 break;
2764 case _PC_CASE_SENSITIVE:
2765 if (VTOHFS(ap->a_vp)->hfs_flags & HFS_CASE_SENSITIVE)
2766 *ap->a_retval = 1;
2767 else
2768 *ap->a_retval = 0;
2769 break;
2770 case _PC_CASE_PRESERVING:
2771 *ap->a_retval = 1;
2772 break;
2773 default:
2774 return (EINVAL);
2775 }
2776
2777 return (0);
2778 }
2779
2780
2781 /*
2782 * Update a cnode's on-disk metadata.
2783 *
2784 * If waitfor is set, then wait for the disk write of
2785 * the node to complete.
2786 *
2787 * The cnode must be locked exclusive
2788 */
2789 __private_extern__
2790 int
2791 hfs_update(struct vnode *vp, __unused int waitfor)
2792 {
2793 struct cnode *cp = VTOC(vp);
2794 struct proc *p;
2795 struct cat_fork *dataforkp = NULL;
2796 struct cat_fork *rsrcforkp = NULL;
2797 struct cat_fork datafork;
2798 struct hfsmount *hfsmp;
2799 int lockflags;
2800 int error;
2801
2802 p = current_proc();
2803 hfsmp = VTOHFS(vp);
2804
2805 if (vnode_issystem(vp) && (cp->c_cnid < kHFSFirstUserCatalogNodeID)) {
2806 return (0);
2807 }
2808 if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (cp->c_mode == 0)) {
2809 cp->c_flag &= ~C_MODIFIED;
2810 cp->c_touch_acctime = 0;
2811 cp->c_touch_chgtime = 0;
2812 cp->c_touch_modtime = 0;
2813 return (0);
2814 }
2815
2816 hfs_touchtimes(hfsmp, cp);
2817
2818 /* Nothing to update. */
2819 if ((cp->c_flag & (C_MODIFIED | C_FORCEUPDATE)) == 0) {
2820 return (0);
2821 }
2822
2823 if (cp->c_datafork)
2824 dataforkp = &cp->c_datafork->ff_data;
2825 if (cp->c_rsrcfork)
2826 rsrcforkp = &cp->c_rsrcfork->ff_data;
2827
2828 /*
2829 * For delayed allocations updates are
2830 * postponed until an fsync or the file
2831 * gets written to disk.
2832 *
2833 * Deleted files can defer meta data updates until inactive.
2834 *
2835 * If we're ever called with the C_FORCEUPDATE flag though
2836 * we have to do the update.
2837 */
2838 if (ISSET(cp->c_flag, C_FORCEUPDATE) == 0 &&
2839 (ISSET(cp->c_flag, C_DELETED) ||
2840 (dataforkp && cp->c_datafork->ff_unallocblocks) ||
2841 (rsrcforkp && cp->c_rsrcfork->ff_unallocblocks))) {
2842 // cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE);
2843 cp->c_flag |= C_MODIFIED;
2844
2845 HFS_KNOTE(vp, NOTE_ATTRIB);
2846
2847 return (0);
2848 }
2849
2850 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2851 return error;
2852 }
2853
2854 /*
2855 * For files with invalid ranges (holes) the on-disk
2856 * field representing the size of the file (cf_size)
2857 * must be no larger than the start of the first hole.
2858 */
2859 if (dataforkp && !CIRCLEQ_EMPTY(&cp->c_datafork->ff_invalidranges)) {
2860 bcopy(dataforkp, &datafork, sizeof(datafork));
2861 datafork.cf_size = CIRCLEQ_FIRST(&cp->c_datafork->ff_invalidranges)->rl_start;
2862 dataforkp = &datafork;
2863 } else if (dataforkp && (cp->c_datafork->ff_unallocblocks != 0)) {
2864 // always make sure the block count and the size
2865 // of the file match the number of blocks actually
2866 // allocated to the file on disk
2867 bcopy(dataforkp, &datafork, sizeof(datafork));
2868 // make sure that we don't assign a negative block count
2869 if (cp->c_datafork->ff_blocks < cp->c_datafork->ff_unallocblocks) {
2870 panic("hfs: ff_blocks %d is less than unalloc blocks %d\n",
2871 cp->c_datafork->ff_blocks, cp->c_datafork->ff_unallocblocks);
2872 }
2873 datafork.cf_blocks = (cp->c_datafork->ff_blocks - cp->c_datafork->ff_unallocblocks);
2874 datafork.cf_size = datafork.cf_blocks * HFSTOVCB(hfsmp)->blockSize;
2875 dataforkp = &datafork;
2876 }
2877
2878 /*
2879 * Lock the Catalog b-tree file.
2880 * A shared lock is sufficient since an update doesn't change
2881 * the tree and the lock on vp protects the cnode.
2882 */
2883 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2884
2885 /* XXX - waitfor is not enforced */
2886 error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
2887
2888 hfs_systemfile_unlock(hfsmp, lockflags);
2889
2890 /* After the updates are finished, clear the flags */
2891 cp->c_flag &= ~(C_MODIFIED | C_FORCEUPDATE);
2892
2893 hfs_end_transaction(hfsmp);
2894
2895 HFS_KNOTE(vp, NOTE_ATTRIB);
2896
2897 return (error);
2898 }
2899
2900 /*
2901 * Allocate a new node
2902 */
2903 static int
2904 hfs_makenode(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
2905 struct vnode_attr *vap, vfs_context_t ctx)
2906 {
2907 struct cnode *cp = NULL;
2908 struct cnode *dcp;
2909 struct vnode *tvp;
2910 struct hfsmount *hfsmp;
2911 struct cat_desc in_desc, out_desc;
2912 struct cat_attr attr;
2913 struct timeval tv;
2914 cat_cookie_t cookie;
2915 int lockflags;
2916 int error, started_tr = 0, got_cookie = 0;
2917 enum vtype vnodetype;
2918 int mode;
2919
2920 if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK)))
2921 return (error);
2922 dcp = VTOC(dvp);
2923 hfsmp = VTOHFS(dvp);
2924 *vpp = NULL;
2925 tvp = NULL;
2926 out_desc.cd_flags = 0;
2927 out_desc.cd_nameptr = NULL;
2928
2929 mode = MAKEIMODE(vap->va_type, vap->va_mode);
2930
2931 if ((mode & S_IFMT) == 0)
2932 mode |= S_IFREG;
2933 vnodetype = IFTOVT(mode);
2934
2935 /* Check if were out of usable disk space. */
2936 if ((hfs_freeblks(hfsmp, 1) <= 0) && (suser(vfs_context_ucred(ctx), NULL) != 0)) {
2937 error = ENOSPC;
2938 goto exit;
2939 }
2940
2941 microtime(&tv);
2942
2943 /* Setup the default attributes */
2944 bzero(&attr, sizeof(attr));
2945 attr.ca_mode = mode;
2946 attr.ca_nlink = vnodetype == VDIR ? 2 : 1;
2947 attr.ca_mtime = tv.tv_sec;
2948 if ((VTOVCB(dvp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
2949 attr.ca_mtime += 3600; /* Same as what hfs_update does */
2950 }
2951 attr.ca_atime = attr.ca_ctime = attr.ca_itime = attr.ca_mtime;
2952 attr.ca_atimeondisk = attr.ca_atime;
2953 /* On HFS+ the ThreadExists flag must always be set for files. */
2954 if (vnodetype != VDIR && (hfsmp->hfs_flags & HFS_STANDARD) == 0)
2955 attr.ca_recflags = kHFSThreadExistsMask;
2956
2957 attr.ca_uid = vap->va_uid;
2958 attr.ca_gid = vap->va_gid;
2959 VATTR_SET_SUPPORTED(vap, va_mode);
2960 VATTR_SET_SUPPORTED(vap, va_uid);
2961 VATTR_SET_SUPPORTED(vap, va_gid);
2962
2963 /* Tag symlinks with a type and creator. */
2964 if (vnodetype == VLNK) {
2965 struct FndrFileInfo *fip;
2966
2967 fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
2968 fip->fdType = SWAP_BE32(kSymLinkFileType);
2969 fip->fdCreator = SWAP_BE32(kSymLinkCreator);
2970 }
2971 if (cnp->cn_flags & ISWHITEOUT)
2972 attr.ca_flags |= UF_OPAQUE;
2973
2974 /* Setup the descriptor */
2975 in_desc.cd_nameptr = cnp->cn_nameptr;
2976 in_desc.cd_namelen = cnp->cn_namelen;
2977 in_desc.cd_parentcnid = dcp->c_cnid;
2978 in_desc.cd_flags = S_ISDIR(mode) ? CD_ISDIR : 0;
2979 in_desc.cd_hint = dcp->c_childhint;
2980 in_desc.cd_encoding = 0;
2981
2982 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2983 goto exit;
2984 }
2985 started_tr = 1;
2986
2987 /*
2988 * Reserve some space in the Catalog file.
2989 *
2990 * (we also add CAT_DELETE since our getnewvnode
2991 * request can cause an hfs_inactive call to
2992 * delete an unlinked file)
2993 */
2994 if ((error = cat_preflight(hfsmp, CAT_CREATE | CAT_DELETE, &cookie, 0))) {
2995 goto exit;
2996 }
2997 got_cookie = 1;
2998
2999 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
3000 error = cat_create(hfsmp, &in_desc, &attr, &out_desc);
3001 if (error == 0) {
3002 /* Update the parent directory */
3003 dcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
3004 dcp->c_nlink++;
3005 dcp->c_entries++;
3006 dcp->c_ctime = tv.tv_sec;
3007 dcp->c_mtime = tv.tv_sec;
3008 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
3009 HFS_KNOTE(dvp, NOTE_ATTRIB);
3010 }
3011 hfs_systemfile_unlock(hfsmp, lockflags);
3012 if (error)
3013 goto exit;
3014
3015 /* Invalidate negative cache entries in the directory */
3016 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
3017 cache_purge_negatives(dvp);
3018
3019 if (vnodetype == VDIR) {
3020 HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
3021 } else {
3022 HFS_KNOTE(dvp, NOTE_WRITE);
3023 };
3024
3025 hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE,
3026 (dcp->c_cnid == kHFSRootFolderID));
3027
3028 // XXXdbg
3029 // have to end the transaction here before we call hfs_getnewvnode()
3030 // because that can cause us to try and reclaim a vnode on a different
3031 // file system which could cause us to start a transaction which can
3032 // deadlock with someone on that other file system (since we could be
3033 // holding two transaction locks as well as various vnodes and we did
3034 // not obtain the locks on them in the proper order).
3035 //
3036 // NOTE: this means that if the quota check fails or we have to update
3037 // the change time on a block-special device that those changes
3038 // will happen as part of independent transactions.
3039 //
3040 if (started_tr) {
3041 hfs_end_transaction(hfsmp);
3042 started_tr = 0;
3043 }
3044
3045 /*
3046 * Create a vnode for the object just created.
3047 *
3048 * The cnode is locked on successful return.
3049 */
3050 error = hfs_getnewvnode(hfsmp, dvp, cnp, &out_desc, 0, &attr, NULL, &tvp);
3051 if (error)
3052 goto exit;
3053
3054 // XXXdbg
3055 //cache_enter(dvp, tvp, cnp);
3056
3057 cp = VTOC(tvp);
3058 #if QUOTA
3059 /*
3060 * We call hfs_chkiq with FORCE flag so that if we
3061 * fall through to the rmdir we actually have
3062 * accounted for the inode
3063 */
3064 if (vfs_flags(HFSTOVFS(hfsmp)) & MNT_QUOTA) {
3065 if ((error = hfs_getinoquota(cp)) ||
3066 (error = hfs_chkiq(cp, 1, vfs_context_ucred(ctx), FORCE))) {
3067
3068 if (vnode_isdir(tvp))
3069 (void) hfs_removedir(dvp, tvp, cnp, 0);
3070 else {
3071 hfs_unlock(cp);
3072 hfs_lock_truncate(cp, TRUE);
3073 hfs_lock(cp, HFS_FORCE_LOCK);
3074 (void) hfs_removefile(dvp, tvp, cnp, 0, 0);
3075 hfs_unlock_truncate(cp);
3076 }
3077 /*
3078 * we successfully allocated a new vnode, but
3079 * the quota check is telling us we're beyond
3080 * our limit, so we need to dump our lock + reference
3081 */
3082 hfs_unlock(cp);
3083 vnode_put(tvp);
3084
3085 goto exit;
3086 }
3087 }
3088 #endif /* QUOTA */
3089
3090 /* Remember if any ACL data was set. */
3091 if (VATTR_IS_ACTIVE(vap, va_acl) &&
3092 (vap->va_acl != NULL)) {
3093 cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
3094 cp->c_touch_chgtime = TRUE;
3095 (void) hfs_update(tvp, TRUE);
3096 }
3097 *vpp = tvp;
3098 exit:
3099 cat_releasedesc(&out_desc);
3100
3101 if (got_cookie) {
3102 cat_postflight(hfsmp, &cookie, 0);
3103 }
3104 /*
3105 * Check if a file is located in the "Cleanup At Startup"
3106 * directory. If it is then tag it as NODUMP so that we
3107 * can be lazy about zero filling data holes.
3108 */
3109 if ((error == 0) && dvp && (vnodetype == VREG) &&
3110 (dcp->c_desc.cd_nameptr != NULL) &&
3111 (strcmp(dcp->c_desc.cd_nameptr, CARBON_TEMP_DIR_NAME) == 0)) {
3112 struct vnode *ddvp;
3113
3114 hfs_unlock(dcp);
3115 dvp = NULL;
3116
3117 /*
3118 * The parent of "Cleanup At Startup" should
3119 * have the ASCII name of the userid.
3120 */
3121 if (hfs_vget(hfsmp, dcp->c_parentcnid, &ddvp, 0) == 0) {
3122 if (VTOC(ddvp)->c_desc.cd_nameptr) {
3123 uid_t uid;
3124
3125 uid = strtoul(VTOC(ddvp)->c_desc.cd_nameptr, 0, 0);
3126 if ((uid == cp->c_uid) ||
3127 (uid == vfs_context_ucred(ctx)->cr_uid)) {
3128 cp->c_flags |= UF_NODUMP;
3129 cp->c_touch_chgtime = TRUE;
3130 }
3131 }
3132 hfs_unlock(VTOC(ddvp));
3133 vnode_put(ddvp);
3134 }
3135 }
3136 if (dvp) {
3137 hfs_unlock(dcp);
3138 }
3139 if (error == 0 && cp != NULL) {
3140 hfs_unlock(cp);
3141 }
3142 if (started_tr) {
3143 hfs_end_transaction(hfsmp);
3144 started_tr = 0;
3145 }
3146
3147 return (error);
3148 }
3149
3150
3151 /*
3152 * WARNING - assumes caller has cnode lock.
3153 */
3154 __private_extern__
3155 int
3156 hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp, struct vnode **rvpp, __unused struct proc *p)
3157 {
3158 struct vnode *rvp;
3159 struct cnode *cp = VTOC(vp);
3160 int error;
3161 int vid;
3162
3163 if ((rvp = cp->c_rsrc_vp)) {
3164 vid = vnode_vid(rvp);
3165
3166 /* Use exising vnode */
3167 error = vnode_getwithvid(rvp, vid);
3168 if (error) {
3169 char * name = VTOC(vp)->c_desc.cd_nameptr;
3170
3171 if (name)
3172 printf("hfs_vgetrsrc: couldn't get"
3173 " resource fork for %s\n", name);
3174 return (error);
3175 }
3176 } else {
3177 struct cat_fork rsrcfork;
3178 struct componentname cn;
3179 int lockflags;
3180
3181 /*
3182 * Make sure cnode lock is exclusive, if not upgrade it.
3183 *
3184 * We assume that we were called from a read-only VNOP (getattr)
3185 * and that its safe to have the cnode lock dropped and reacquired.
3186 */
3187 if (cp->c_lockowner != current_thread()) {
3188 /*
3189 * If the upgrade fails we loose the lock and
3190 * have to take the exclusive lock on our own.
3191 */
3192 if (lck_rw_lock_shared_to_exclusive(&cp->c_rwlock) != 0)
3193 lck_rw_lock_exclusive(&cp->c_rwlock);
3194 cp->c_lockowner = current_thread();
3195 }
3196
3197 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
3198
3199 /* Get resource fork data */
3200 error = cat_lookup(hfsmp, &cp->c_desc, 1, (struct cat_desc *)0,
3201 (struct cat_attr *)0, &rsrcfork, NULL);
3202
3203 hfs_systemfile_unlock(hfsmp, lockflags);
3204 if (error)
3205 return (error);
3206
3207 /*
3208 * Supply hfs_getnewvnode with a component name.
3209 */
3210 cn.cn_pnbuf = NULL;
3211 if (cp->c_desc.cd_nameptr) {
3212 MALLOC_ZONE(cn.cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
3213 cn.cn_nameiop = LOOKUP;
3214 cn.cn_flags = ISLASTCN | HASBUF;
3215 cn.cn_context = NULL;
3216 cn.cn_pnlen = MAXPATHLEN;
3217 cn.cn_nameptr = cn.cn_pnbuf;
3218 cn.cn_hash = 0;
3219 cn.cn_consume = 0;
3220 cn.cn_namelen = sprintf(cn.cn_nameptr, "%s%s", cp->c_desc.cd_nameptr, _PATH_RSRCFORKSPEC);
3221 }
3222 error = hfs_getnewvnode(hfsmp, vnode_parent(vp), cn.cn_pnbuf ? &cn : NULL,
3223 &cp->c_desc, 2, &cp->c_attr, &rsrcfork, &rvp);
3224 if (cn.cn_pnbuf)
3225 FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
3226 if (error)
3227 return (error);
3228 }
3229
3230 *rvpp = rvp;
3231 return (0);
3232 }
3233
3234
3235 static void
3236 filt_hfsdetach(struct knote *kn)
3237 {
3238 struct vnode *vp;
3239
3240 vp = (struct vnode *)kn->kn_hook;
3241 if (vnode_getwithvid(vp, kn->kn_hookid))
3242 return;
3243
3244 if (1) { /* ! KNDETACH_VNLOCKED */
3245 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
3246 (void) KNOTE_DETACH(&VTOC(vp)->c_knotes, kn);
3247 hfs_unlock(VTOC(vp));
3248 }
3249 }
3250
3251 vnode_put(vp);
3252 }
3253
3254 /*ARGSUSED*/
3255 static int
3256 filt_hfsread(struct knote *kn, long hint)
3257 {
3258 struct vnode *vp = (struct vnode *)kn->kn_hook;
3259 int dropvp = 0;
3260
3261 if (hint == 0) {
3262 if ((vnode_getwithvid(vp, kn->kn_hookid) != 0)) {
3263 hint = NOTE_REVOKE;
3264 } else
3265 dropvp = 1;
3266 }
3267 if (hint == NOTE_REVOKE) {
3268 /*
3269 * filesystem is gone, so set the EOF flag and schedule
3270 * the knote for deletion.
3271 */
3272 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3273 return (1);
3274 }
3275
3276 /* poll(2) semantics dictate always saying there is data */
3277 kn->kn_data = (!(kn->kn_flags & EV_POLL)) ?
3278 VTOF(vp)->ff_size - kn->kn_fp->f_fglob->fg_offset : 1;
3279
3280 if (dropvp)
3281 vnode_put(vp);
3282
3283 return (kn->kn_data != 0);
3284 }
3285
3286 /*ARGSUSED*/
3287 static int
3288 filt_hfswrite(struct knote *kn, long hint)
3289 {
3290 int dropvp = 0;
3291
3292 if (hint == 0) {
3293 if ((vnode_getwithvid(kn->kn_hook, kn->kn_hookid) != 0)) {
3294 hint = NOTE_REVOKE;
3295 } else
3296 vnode_put(kn->kn_hook);
3297 }
3298 if (hint == NOTE_REVOKE) {
3299 /*
3300 * filesystem is gone, so set the EOF flag and schedule
3301 * the knote for deletion.
3302 */
3303 kn->kn_data = 0;
3304 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3305 return (1);
3306 }
3307 kn->kn_data = 0;
3308 return (1);
3309 }
3310
3311 static int
3312 filt_hfsvnode(struct knote *kn, long hint)
3313 {
3314
3315 if (hint == 0) {
3316 if ((vnode_getwithvid(kn->kn_hook, kn->kn_hookid) != 0)) {
3317 hint = NOTE_REVOKE;
3318 } else
3319 vnode_put(kn->kn_hook);
3320 }
3321 if (kn->kn_sfflags & hint)
3322 kn->kn_fflags |= hint;
3323 if ((hint == NOTE_REVOKE)) {
3324 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3325 return (1);
3326 }
3327
3328 return (kn->kn_fflags != 0);
3329 }
3330
3331 static struct filterops hfsread_filtops =
3332 { 1, NULL, filt_hfsdetach, filt_hfsread };
3333 static struct filterops hfswrite_filtops =
3334 { 1, NULL, filt_hfsdetach, filt_hfswrite };
3335 static struct filterops hfsvnode_filtops =
3336 { 1, NULL, filt_hfsdetach, filt_hfsvnode };
3337
3338 /*
3339 * Add a kqueue filter.
3340 */
3341 static int
3342 hfs_vnop_kqfiltadd(
3343 struct vnop_kqfilt_add_args /* {
3344 struct vnode *a_vp;
3345 struct knote *a_kn;
3346 struct proc *p;
3347 vfs_context_t a_context;
3348 } */ *ap)
3349 {
3350 struct vnode *vp = ap->a_vp;
3351 struct knote *kn = ap->a_kn;
3352 int error;
3353
3354 switch (kn->kn_filter) {
3355 case EVFILT_READ:
3356 if (vnode_isreg(vp)) {
3357 kn->kn_fop = &hfsread_filtops;
3358 } else {
3359 return EINVAL;
3360 };
3361 break;
3362 case EVFILT_WRITE:
3363 if (vnode_isreg(vp)) {
3364 kn->kn_fop = &hfswrite_filtops;
3365 } else {
3366 return EINVAL;
3367 };
3368 break;
3369 case EVFILT_VNODE:
3370 kn->kn_fop = &hfsvnode_filtops;
3371 break;
3372 default:
3373 return (1);
3374 }
3375
3376 kn->kn_hook = (caddr_t)vp;
3377 kn->kn_hookid = vnode_vid(vp);
3378
3379 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
3380 return (error);
3381 KNOTE_ATTACH(&VTOC(vp)->c_knotes, kn);
3382 hfs_unlock(VTOC(vp));
3383
3384 return (0);
3385 }
3386
3387 /*
3388 * Remove a kqueue filter
3389 */
3390 static int
3391 hfs_vnop_kqfiltremove(ap)
3392 struct vnop_kqfilt_remove_args /* {
3393 struct vnode *a_vp;
3394 uintptr_t ident;
3395 vfs_context_t a_context;
3396 } */ *ap;
3397 {
3398 int result;
3399
3400 result = ENOTSUP; /* XXX */
3401
3402 return (result);
3403 }
3404
3405 /*
3406 * Wrapper for special device reads
3407 */
3408 static int
3409 hfsspec_read(ap)
3410 struct vnop_read_args /* {
3411 struct vnode *a_vp;
3412 struct uio *a_uio;
3413 int a_ioflag;
3414 vfs_context_t a_context;
3415 } */ *ap;
3416 {
3417 /*
3418 * Set access flag.
3419 */
3420 VTOC(ap->a_vp)->c_touch_acctime = TRUE;
3421 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap));
3422 }
3423
3424 /*
3425 * Wrapper for special device writes
3426 */
3427 static int
3428 hfsspec_write(ap)
3429 struct vnop_write_args /* {
3430 struct vnode *a_vp;
3431 struct uio *a_uio;
3432 int a_ioflag;
3433 vfs_context_t a_context;
3434 } */ *ap;
3435 {
3436 /*
3437 * Set update and change flags.
3438 */
3439 VTOC(ap->a_vp)->c_touch_chgtime = TRUE;
3440 VTOC(ap->a_vp)->c_touch_modtime = TRUE;
3441 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap));
3442 }
3443
3444 /*
3445 * Wrapper for special device close
3446 *
3447 * Update the times on the cnode then do device close.
3448 */
3449 static int
3450 hfsspec_close(ap)
3451 struct vnop_close_args /* {
3452 struct vnode *a_vp;
3453 int a_fflag;
3454 vfs_context_t a_context;
3455 } */ *ap;
3456 {
3457 struct vnode *vp = ap->a_vp;
3458 struct cnode *cp;
3459
3460 if (vnode_isinuse(ap->a_vp, 1)) {
3461 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
3462 cp = VTOC(vp);
3463 hfs_touchtimes(VTOHFS(vp), cp);
3464 hfs_unlock(cp);
3465 }
3466 }
3467 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap));
3468 }
3469
3470 #if FIFO
3471 /*
3472 * Wrapper for fifo reads
3473 */
3474 static int
3475 hfsfifo_read(ap)
3476 struct vnop_read_args /* {
3477 struct vnode *a_vp;
3478 struct uio *a_uio;
3479 int a_ioflag;
3480 vfs_context_t a_context;
3481 } */ *ap;
3482 {
3483 extern int (**fifo_vnodeop_p)(void *);
3484
3485 /*
3486 * Set access flag.
3487 */
3488 VTOC(ap->a_vp)->c_touch_acctime = TRUE;
3489 return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_read), ap));
3490 }
3491
3492 /*
3493 * Wrapper for fifo writes
3494 */
3495 static int
3496 hfsfifo_write(ap)
3497 struct vnop_write_args /* {
3498 struct vnode *a_vp;
3499 struct uio *a_uio;
3500 int a_ioflag;
3501 vfs_context_t a_context;
3502 } */ *ap;
3503 {
3504 extern int (**fifo_vnodeop_p)(void *);
3505
3506 /*
3507 * Set update and change flags.
3508 */
3509 VTOC(ap->a_vp)->c_touch_chgtime = TRUE;
3510 VTOC(ap->a_vp)->c_touch_modtime = TRUE;
3511 return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_write), ap));
3512 }
3513
3514 /*
3515 * Wrapper for fifo close
3516 *
3517 * Update the times on the cnode then do device close.
3518 */
3519 static int
3520 hfsfifo_close(ap)
3521 struct vnop_close_args /* {
3522 struct vnode *a_vp;
3523 int a_fflag;
3524 vfs_context_t a_context;
3525 } */ *ap;
3526 {
3527 extern int (**fifo_vnodeop_p)(void *);
3528 struct vnode *vp = ap->a_vp;
3529 struct cnode *cp;
3530
3531 if (vnode_isinuse(ap->a_vp, 1)) {
3532 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
3533 cp = VTOC(vp);
3534 hfs_touchtimes(VTOHFS(vp), cp);
3535 hfs_unlock(cp);
3536 }
3537 }
3538 return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_close), ap));
3539 }
3540
3541 /*
3542 * kqfilt_add wrapper for fifos.
3543 *
3544 * Fall through to hfs kqfilt_add routines if needed
3545 */
3546 int
3547 hfsfifo_kqfilt_add(ap)
3548 struct vnop_kqfilt_add_args *ap;
3549 {
3550 extern int (**fifo_vnodeop_p)(void *);
3551 int error;
3552
3553 error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_add), ap);
3554 if (error)
3555 error = hfs_vnop_kqfiltadd(ap);
3556 return (error);
3557 }
3558
3559 /*
3560 * kqfilt_remove wrapper for fifos.
3561 *
3562 * Fall through to hfs kqfilt_remove routines if needed
3563 */
3564 int
3565 hfsfifo_kqfilt_remove(ap)
3566 struct vnop_kqfilt_remove_args *ap;
3567 {
3568 extern int (**fifo_vnodeop_p)(void *);
3569 int error;
3570
3571 error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_remove), ap);
3572 if (error)
3573 error = hfs_vnop_kqfiltremove(ap);
3574 return (error);
3575 }
3576
3577 #endif /* FIFO */
3578
3579 /*
3580 * Synchronize a file's in-core state with that on disk.
3581 */
3582 static int
3583 hfs_vnop_fsync(ap)
3584 struct vnop_fsync_args /* {
3585 struct vnode *a_vp;
3586 int a_waitfor;
3587 vfs_context_t a_context;
3588 } */ *ap;
3589 {
3590 struct vnode* vp = ap->a_vp;
3591 int error;
3592
3593 /*
3594 * We need to allow ENOENT lock errors since unlink
3595 * systenm call can call VNOP_FSYNC during vclean.
3596 */
3597 error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
3598 if (error)
3599 return (0);
3600
3601 error = hfs_fsync(vp, ap->a_waitfor, 0, vfs_context_proc(ap->a_context));
3602
3603 hfs_unlock(VTOC(vp));
3604 return (error);
3605 }
3606
3607 /*****************************************************************************
3608 *
3609 * VOP Tables
3610 *
3611 *****************************************************************************/
3612 int hfs_vnop_readdirattr(struct vnop_readdirattr_args *); /* in hfs_attrlist.c */
3613 int hfs_vnop_inactive(struct vnop_inactive_args *); /* in hfs_cnode.c */
3614 int hfs_vnop_reclaim(struct vnop_reclaim_args *); /* in hfs_cnode.c */
3615 int hfs_vnop_link(struct vnop_link_args *); /* in hfs_link.c */
3616 int hfs_vnop_lookup(struct vnop_lookup_args *); /* in hfs_lookup.c */
3617 int hfs_vnop_search(struct vnop_searchfs_args *); /* in hfs_search.c */
3618
3619 int hfs_vnop_read(struct vnop_read_args *); /* in hfs_readwrite.c */
3620 int hfs_vnop_write(struct vnop_write_args *); /* in hfs_readwrite.c */
3621 int hfs_vnop_ioctl(struct vnop_ioctl_args *); /* in hfs_readwrite.c */
3622 int hfs_vnop_select(struct vnop_select_args *); /* in hfs_readwrite.c */
3623 int hfs_vnop_strategy(struct vnop_strategy_args *); /* in hfs_readwrite.c */
3624 int hfs_vnop_allocate(struct vnop_allocate_args *); /* in hfs_readwrite.c */
3625 int hfs_vnop_pagein(struct vnop_pagein_args *); /* in hfs_readwrite.c */
3626 int hfs_vnop_pageout(struct vnop_pageout_args *); /* in hfs_readwrite.c */
3627 int hfs_vnop_bwrite(struct vnop_bwrite_args *); /* in hfs_readwrite.c */
3628 int hfs_vnop_blktooff(struct vnop_blktooff_args *); /* in hfs_readwrite.c */
3629 int hfs_vnop_offtoblk(struct vnop_offtoblk_args *); /* in hfs_readwrite.c */
3630 int hfs_vnop_blockmap(struct vnop_blockmap_args *); /* in hfs_readwrite.c */
3631 int hfs_vnop_getxattr(struct vnop_getxattr_args *); /* in hfs_xattr.c */
3632 int hfs_vnop_setxattr(struct vnop_setxattr_args *); /* in hfs_xattr.c */
3633 int hfs_vnop_removexattr(struct vnop_removexattr_args *); /* in hfs_xattr.c */
3634 int hfs_vnop_listxattr(struct vnop_listxattr_args *); /* in hfs_xattr.c */
3635
3636 int (**hfs_vnodeop_p)(void *);
3637
3638 #define VOPFUNC int (*)(void *)
3639
3640 struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
3641 { &vnop_default_desc, (VOPFUNC)vn_default_error },
3642 { &vnop_lookup_desc, (VOPFUNC)hfs_vnop_lookup }, /* lookup */
3643 { &vnop_create_desc, (VOPFUNC)hfs_vnop_create }, /* create */
3644 { &vnop_mknod_desc, (VOPFUNC)hfs_vnop_mknod }, /* mknod */
3645 { &vnop_open_desc, (VOPFUNC)hfs_vnop_open }, /* open */
3646 { &vnop_close_desc, (VOPFUNC)hfs_vnop_close }, /* close */
3647 { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */
3648 { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */
3649 { &vnop_read_desc, (VOPFUNC)hfs_vnop_read }, /* read */
3650 { &vnop_write_desc, (VOPFUNC)hfs_vnop_write }, /* write */
3651 { &vnop_ioctl_desc, (VOPFUNC)hfs_vnop_ioctl }, /* ioctl */
3652 { &vnop_select_desc, (VOPFUNC)hfs_vnop_select }, /* select */
3653 { &vnop_revoke_desc, (VOPFUNC)nop_revoke }, /* revoke */
3654 { &vnop_exchange_desc, (VOPFUNC)hfs_vnop_exchange }, /* exchange */
3655 { &vnop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */
3656 { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */
3657 { &vnop_remove_desc, (VOPFUNC)hfs_vnop_remove }, /* remove */
3658 { &vnop_link_desc, (VOPFUNC)hfs_vnop_link }, /* link */
3659 { &vnop_rename_desc, (VOPFUNC)hfs_vnop_rename }, /* rename */
3660 { &vnop_mkdir_desc, (VOPFUNC)hfs_vnop_mkdir }, /* mkdir */
3661 { &vnop_rmdir_desc, (VOPFUNC)hfs_vnop_rmdir }, /* rmdir */
3662 { &vnop_symlink_desc, (VOPFUNC)hfs_vnop_symlink }, /* symlink */
3663 { &vnop_readdir_desc, (VOPFUNC)hfs_vnop_readdir }, /* readdir */
3664 { &vnop_readdirattr_desc, (VOPFUNC)hfs_vnop_readdirattr }, /* readdirattr */
3665 { &vnop_readlink_desc, (VOPFUNC)hfs_vnop_readlink }, /* readlink */
3666 { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */
3667 { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */
3668 { &vnop_strategy_desc, (VOPFUNC)hfs_vnop_strategy }, /* strategy */
3669 { &vnop_pathconf_desc, (VOPFUNC)hfs_vnop_pathconf }, /* pathconf */
3670 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
3671 { &vnop_allocate_desc, (VOPFUNC)hfs_vnop_allocate }, /* allocate */
3672 { &vnop_searchfs_desc, (VOPFUNC)hfs_vnop_search }, /* search fs */
3673 { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite }, /* bwrite */
3674 { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* pagein */
3675 { &vnop_pageout_desc,(VOPFUNC) hfs_vnop_pageout }, /* pageout */
3676 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3677 { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */
3678 { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */
3679 { &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap }, /* blockmap */
3680 { &vnop_kqfilt_add_desc, (VOPFUNC)hfs_vnop_kqfiltadd }, /* kqfilt_add */
3681 { &vnop_kqfilt_remove_desc, (VOPFUNC)hfs_vnop_kqfiltremove }, /* kqfilt_remove */
3682 { &vnop_getxattr_desc, (VOPFUNC)hfs_vnop_getxattr},
3683 { &vnop_setxattr_desc, (VOPFUNC)hfs_vnop_setxattr},
3684 { &vnop_removexattr_desc, (VOPFUNC)hfs_vnop_removexattr},
3685 { &vnop_listxattr_desc, (VOPFUNC)hfs_vnop_listxattr},
3686 { NULL, (VOPFUNC)NULL }
3687 };
3688
3689 struct vnodeopv_desc hfs_vnodeop_opv_desc =
3690 { &hfs_vnodeop_p, hfs_vnodeop_entries };
3691
3692 int (**hfs_specop_p)(void *);
3693 struct vnodeopv_entry_desc hfs_specop_entries[] = {
3694 { &vnop_default_desc, (VOPFUNC)vn_default_error },
3695 { &vnop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */
3696 { &vnop_create_desc, (VOPFUNC)spec_create }, /* create */
3697 { &vnop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */
3698 { &vnop_open_desc, (VOPFUNC)spec_open }, /* open */
3699 { &vnop_close_desc, (VOPFUNC)hfsspec_close }, /* close */
3700 { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */
3701 { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */
3702 { &vnop_read_desc, (VOPFUNC)hfsspec_read }, /* read */
3703 { &vnop_write_desc, (VOPFUNC)hfsspec_write }, /* write */
3704 { &vnop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */
3705 { &vnop_select_desc, (VOPFUNC)spec_select }, /* select */
3706 { &vnop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */
3707 { &vnop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */
3708 { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */
3709 { &vnop_remove_desc, (VOPFUNC)spec_remove }, /* remove */
3710 { &vnop_link_desc, (VOPFUNC)spec_link }, /* link */
3711 { &vnop_rename_desc, (VOPFUNC)spec_rename }, /* rename */
3712 { &vnop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */
3713 { &vnop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */
3714 { &vnop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */
3715 { &vnop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */
3716 { &vnop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */
3717 { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */
3718 { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */
3719 { &vnop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */
3720 { &vnop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */
3721 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
3722 { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
3723 { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* Pagein */
3724 { &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout }, /* Pageout */
3725 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3726 { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */
3727 { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */
3728 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
3729 };
3730 struct vnodeopv_desc hfs_specop_opv_desc =
3731 { &hfs_specop_p, hfs_specop_entries };
3732
3733 #if FIFO
3734 int (**hfs_fifoop_p)(void *);
3735 struct vnodeopv_entry_desc hfs_fifoop_entries[] = {
3736 { &vnop_default_desc, (VOPFUNC)vn_default_error },
3737 { &vnop_lookup_desc, (VOPFUNC)fifo_lookup }, /* lookup */
3738 { &vnop_create_desc, (VOPFUNC)fifo_create }, /* create */
3739 { &vnop_mknod_desc, (VOPFUNC)fifo_mknod }, /* mknod */
3740 { &vnop_open_desc, (VOPFUNC)fifo_open }, /* open */
3741 { &vnop_close_desc, (VOPFUNC)hfsfifo_close }, /* close */
3742 { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */
3743 { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */
3744 { &vnop_read_desc, (VOPFUNC)hfsfifo_read }, /* read */
3745 { &vnop_write_desc, (VOPFUNC)hfsfifo_write }, /* write */
3746 { &vnop_ioctl_desc, (VOPFUNC)fifo_ioctl }, /* ioctl */
3747 { &vnop_select_desc, (VOPFUNC)fifo_select }, /* select */
3748 { &vnop_revoke_desc, (VOPFUNC)fifo_revoke }, /* revoke */
3749 { &vnop_mmap_desc, (VOPFUNC)fifo_mmap }, /* mmap */
3750 { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */
3751 { &vnop_remove_desc, (VOPFUNC)fifo_remove }, /* remove */
3752 { &vnop_link_desc, (VOPFUNC)fifo_link }, /* link */
3753 { &vnop_rename_desc, (VOPFUNC)fifo_rename }, /* rename */
3754 { &vnop_mkdir_desc, (VOPFUNC)fifo_mkdir }, /* mkdir */
3755 { &vnop_rmdir_desc, (VOPFUNC)fifo_rmdir }, /* rmdir */
3756 { &vnop_symlink_desc, (VOPFUNC)fifo_symlink }, /* symlink */
3757 { &vnop_readdir_desc, (VOPFUNC)fifo_readdir }, /* readdir */
3758 { &vnop_readlink_desc, (VOPFUNC)fifo_readlink }, /* readlink */
3759 { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */
3760 { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */
3761 { &vnop_strategy_desc, (VOPFUNC)fifo_strategy }, /* strategy */
3762 { &vnop_pathconf_desc, (VOPFUNC)fifo_pathconf }, /* pathconf */
3763 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
3764 { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
3765 { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* Pagein */
3766 { &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout }, /* Pageout */
3767 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3768 { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */
3769 { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */
3770 { &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap }, /* blockmap */
3771 { &vnop_kqfilt_add_desc, (VOPFUNC)hfsfifo_kqfilt_add }, /* kqfilt_add */
3772 { &vnop_kqfilt_remove_desc, (VOPFUNC)hfsfifo_kqfilt_remove }, /* kqfilt_remove */
3773 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
3774 };
3775 struct vnodeopv_desc hfs_fifoop_opv_desc =
3776 { &hfs_fifoop_p, hfs_fifoop_entries };
3777 #endif /* FIFO */
3778
3779
3780