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