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