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