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