]> git.saurik.com Git - apple/hfs.git/blob - livefiles_hfs_plugin/lf_hfs_vnops.c
hfs-522.100.5.tar.gz
[apple/hfs.git] / livefiles_hfs_plugin / lf_hfs_vnops.c
1 /* Copyright © 2017-2018 Apple Inc. All rights reserved.
2 *
3 * lf_hfs_vnops.c
4 * livefiles_hfs
5 *
6 * Created by Or Haimovich on 20/3/18.
7 */
8
9 #include <sys/stat.h>
10 #include <sys/mount.h>
11
12 #include "lf_hfs_vnops.h"
13 #include "lf_hfs.h"
14 #include "lf_hfs_catalog.h"
15 #include "lf_hfs_dirops_handler.h"
16 #include "lf_hfs_fileops_handler.h"
17 #include "lf_hfs_vfsutils.h"
18 #include "lf_hfs_logger.h"
19 #include "lf_hfs_attrlist.h"
20 #include "lf_hfs_btree.h"
21 #include "lf_hfs_vfsops.h"
22 #include "lf_hfs_utils.h"
23 #include "lf_hfs_readwrite_ops.h"
24 #include "lf_hfs_generic_buf.h"
25 #include "lf_hfs_endian.h"
26 #include <sys/stat.h>
27 #include <sys/mount.h>
28 #include "lf_hfs_link.h"
29 #include "lf_hfs_journal.h"
30 #include "lf_hfs_chash.h"
31
32 #define DOT_DIR_SIZE (UVFS_DIRENTRY_RECLEN(1))
33 #define DOT_X2_DIR_SIZE (UVFS_DIRENTRY_RECLEN(2))
34
35
36 /* Options for hfs_removedir and hfs_removefile */
37 #define HFSRM_SKIP_RESERVE 0x01
38 #define _PATH_RSRCFORKSPEC "/..namedfork/rsrc"
39
40 void
41 replace_desc(struct cnode *cp, struct cat_desc *cdp)
42 {
43 // fixes 4348457 and 4463138
44 if (&cp->c_desc == cdp) {
45 return;
46 }
47
48 /* First release allocated name buffer */
49 if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) {
50 const u_int8_t *name = cp->c_desc.cd_nameptr;
51
52 cp->c_desc.cd_nameptr = 0;
53 cp->c_desc.cd_namelen = 0;
54 cp->c_desc.cd_flags &= ~CD_HASBUF;
55 hfs_free((void*)name);
56 }
57 bcopy(cdp, &cp->c_desc, sizeof(cp->c_desc));
58
59 /* Cnode now owns the name buffer */
60 cdp->cd_nameptr = NULL;
61 cdp->cd_namelen = 0;
62 cdp->cd_flags &= ~CD_HASBUF;
63 }
64
65 static void SynthesizeDotAndDotX2(u_int64_t uCnid, void* puBuff, bool bIsDot, bool bIsLastEntry)
66 {
67 UVFSDirEntry* psDotEntry = (UVFSDirEntry*)puBuff;
68 uint8_t uNameLen = bIsDot? 1: 2;
69 memset( psDotEntry, 0, UVFS_DIRENTRY_RECLEN(uNameLen));
70
71 psDotEntry->de_fileid = uCnid;
72 psDotEntry->de_filetype = UVFS_FA_TYPE_DIR;
73 psDotEntry->de_reclen = bIsLastEntry ? 0 : UVFS_DIRENTRY_RECLEN(uNameLen);
74 psDotEntry->de_nextcookie = uNameLen;
75 psDotEntry->de_namelen = uNameLen;
76 uint8_t* puNameBuf = (uint8_t*)psDotEntry->de_name;
77 puNameBuf[0] = '.';
78 if ( bIsDot )
79 {
80 puNameBuf[1] = '\0';
81 }
82 else
83 {
84 puNameBuf[1] = '.';
85 puNameBuf[2] = '\0';
86 }
87 }
88
89 static int SyntisizeEntries(uint64_t* puOffset, ReadDirBuff_s* psReadDirBuffer, int iIsExtended, u_int64_t uCnid, u_int64_t uParentCnid, UVFSDirEntry** ppsDotDotEntry)
90 {
91 int iError = 0;
92 void* pvBuff = NULL;
93 if (!iIsExtended)
94 {
95 //Curently not supporting nonextended ReadDir
96 return ENOTSUP;
97 }
98
99 if (DOT_DIR_SIZE > psReadDirBuffer->uBufferResid)
100 {
101 goto exit;
102 }
103
104 pvBuff = hfs_malloc(DOT_DIR_SIZE);
105 if (pvBuff == NULL)
106 {
107 LFHFS_LOG(LEVEL_ERROR, "SyntisizeEntries: Failed to allocate buffer for DOT entry\n");
108 return ENOMEM;
109 }
110
111 if (*puOffset == 0)
112 {
113 bool bIsEnoughRoomForAll = (DOT_DIR_SIZE + DOT_X2_DIR_SIZE > psReadDirBuffer->uBufferResid);
114 SynthesizeDotAndDotX2(uCnid, pvBuff, true, bIsEnoughRoomForAll);
115 memcpy(psReadDirBuffer->pvBuffer + READDIR_BUF_OFFSET(psReadDirBuffer) , pvBuff, DOT_DIR_SIZE);
116 (*puOffset)++;
117 psReadDirBuffer->uBufferResid -= DOT_DIR_SIZE;
118 }
119
120 if (DOT_X2_DIR_SIZE > psReadDirBuffer->uBufferResid)
121 {
122 goto exit;
123 }
124
125 hfs_free(pvBuff);
126 pvBuff = hfs_malloc(DOT_X2_DIR_SIZE);
127 if (pvBuff == NULL)
128 {
129 LFHFS_LOG(LEVEL_ERROR, "SyntisizeEntries: Failed to allocate buffer for DOTx2 entry\n");
130 return ENOMEM;
131 }
132
133 if (*puOffset == 1)
134 {
135 SynthesizeDotAndDotX2(uParentCnid, pvBuff, false, false);
136 memcpy(psReadDirBuffer->pvBuffer + READDIR_BUF_OFFSET(psReadDirBuffer), pvBuff, DOT_X2_DIR_SIZE);
137 *ppsDotDotEntry = (UVFSDirEntry*) (psReadDirBuffer->pvBuffer + READDIR_BUF_OFFSET(psReadDirBuffer));
138 (*puOffset)++;
139 psReadDirBuffer->uBufferResid -= DOT_X2_DIR_SIZE;
140 }
141
142 exit:
143 if (pvBuff)
144 hfs_free(pvBuff);
145 return iError;
146 }
147
148 /*
149 * hfs_vnop_readdir reads directory entries into the buffer pointed
150 * to by uio, in a filesystem independent format. Up to uio_resid
151 * bytes of data can be transferred. The data in the buffer is a
152 * series of packed dirent structures where each one contains the
153 * following entries:
154 *
155 * u_int32_t d_fileno; // file number of entry
156 * u_int16_t d_reclen; // length of this record
157 * u_int8_t d_type; // file type
158 * u_int8_t d_namlen; // length of string in d_name
159 * char d_name[MAXNAMELEN+1]; // null terminated file name
160 *
161 * The current position (uio_offset) refers to the next block of
162 * entries. The offset can only be set to a value previously
163 * returned by hfs_vnop_readdir or zero. This offset does not have
164 * to match the number of bytes returned (in uio_resid).
165 *
166 * In fact, the offset used by HFS is essentially an index (26 bits)
167 * with a tag (6 bits). The tag is for associating the next request
168 * with the current request. This enables us to have multiple threads
169 * reading the directory while the directory is also being modified.
170 *
171 * Each tag/index pair is tied to a unique directory hint. The hint
172 * contains information (filename) needed to build the catalog b-tree
173 * key for finding the next set of entries.
174 *
175 * If the directory is marked as deleted-but-in-use (cp->c_flag & C_DELETED),
176 * do NOT synthesize entries for "." and "..".
177 */
178 int
179 hfs_vnop_readdir(vnode_t vp, int *eofflag, int *numdirent, ReadDirBuff_s* psReadDirBuffer, uint64_t puCookie, int flags)
180 {
181 struct cnode *cp = NULL;
182 struct hfsmount *hfsmp = VTOHFS(vp);
183 directoryhint_t *dirhint = NULL;
184 directoryhint_t localhint;
185 bool bLocalEOFflag = false;
186 int error = 0;
187 uint64_t offset;
188 user_size_t user_original_resid = psReadDirBuffer->uBufferResid;
189 int items = 0;
190 cnid_t cnid_hint = 0;
191 int bump_valence = 0;
192 *numdirent = 0;
193 uint64_t startoffset = offset = puCookie;
194 bool extended = (flags & VNODE_READDIR_EXTENDED);
195 bool nfs_cookies = extended && (flags & VNODE_READDIR_REQSEEKOFF);
196
197 if (psReadDirBuffer->pvBuffer == NULL || psReadDirBuffer->uBufferResid < sizeof(UVFSDirEntry))
198 {
199 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readdir: readDir input is not valid\n");
200 return EINVAL;
201 }
202
203 /* Note that the dirhint calls require an exclusive lock. */
204 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
205 {
206 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readdir: Failed to lock vnode\n");
207 return error;
208 }
209 cp = VTOC(vp);
210
211 /* Pick up cnid hint (if any). */
212 if (nfs_cookies)
213 {
214 cnid_hint = (cnid_t)(offset >> 32);
215 offset &= 0x00000000ffffffffLL;
216 if (cnid_hint == INT_MAX)
217 { /* searching pass the last item */
218 bLocalEOFflag = true;
219 goto out;
220 }
221 }
222
223 /*
224 * Synthesize entries for "." and "..", unless the directory has
225 * been deleted, but not closed yet (lazy delete in progress).
226 */
227 UVFSDirEntry* psDotDotEntry = NULL;
228 if (!(cp->c_flag & C_DELETED))
229 {
230 if ( (error = SyntisizeEntries(&offset, psReadDirBuffer, extended, cp->c_cnid, cp->c_parentcnid, &psDotDotEntry)) != 0 )
231 {
232 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readdir: Failed to syntisize dot/dotdot entries\n");
233 goto out;
234 }
235 }
236
237 /* Convert offset into a catalog directory index. */
238 int index = (offset & HFS_INDEX_MASK) - 2;
239 unsigned int tag = (unsigned int) (offset & ~HFS_INDEX_MASK);
240
241 /* Lock catalog during cat_findname and cat_getdirentries. */
242 int lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
243
244 /* When called from NFS, try and resolve a cnid hint. */
245 if (nfs_cookies && cnid_hint != 0)
246 {
247 if (cat_findname(hfsmp, cnid_hint, &localhint.dh_desc) == 0)
248 {
249 if ( localhint.dh_desc.cd_parentcnid == cp->c_fileid)
250 {
251 localhint.dh_index = index - 1;
252 localhint.dh_time = 0;
253 bzero(&localhint.dh_link, sizeof(localhint.dh_link));
254 dirhint = &localhint; /* don't forget to release the descriptor */
255 }
256 else
257 {
258 cat_releasedesc(&localhint.dh_desc);
259 }
260 }
261 }
262
263 /* Get a directory hint (cnode must be locked exclusive) */
264 if (dirhint == NULL)
265 {
266 dirhint = hfs_getdirhint(cp, ((index - 1) & HFS_INDEX_MASK) | tag, 0);
267
268 /* Hide tag from catalog layer. */
269 dirhint->dh_index &= HFS_INDEX_MASK;
270 if (dirhint->dh_index == HFS_INDEX_MASK)
271 {
272 dirhint->dh_index = -1;
273 }
274 }
275
276 if (index == 0)
277 {
278 dirhint->dh_threadhint = cp->c_dirthreadhint;
279 }
280 else
281 {
282 /*
283 * If we have a non-zero index, there is a possibility that during the last
284 * call to hfs_vnop_readdir we hit EOF for this directory. If that is the case
285 * then we don't want to return any new entries for the caller. Just return 0
286 * items, mark the eofflag, and bail out. Because we won't have done any work, the
287 * code at the end of the function will release the dirhint for us.
288 *
289 * Don't forget to unlock the catalog lock on the way out, too.
290 */
291 if (dirhint->dh_desc.cd_flags & CD_EOF)
292 {
293 error = 0;
294 bLocalEOFflag = true;
295 offset = startoffset;
296 if (user_original_resid > 0) {
297 psReadDirBuffer->uBufferResid = user_original_resid;
298 }
299 hfs_systemfile_unlock (hfsmp, lockflags);
300
301 goto seekoffcalc;
302 }
303 }
304
305 /* Pack the buffer with dirent entries. */
306 error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, psReadDirBuffer, flags, &items, &bLocalEOFflag, psDotDotEntry);
307
308 if (index == 0 && error == 0)
309 {
310 cp->c_dirthreadhint = dirhint->dh_threadhint;
311 }
312
313 hfs_systemfile_unlock(hfsmp, lockflags);
314
315 if (error != 0)
316 {
317 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readdir: Failed to get dir entries\n");
318 goto out;
319 }
320
321 /* Get index to the next item */
322 index += items;
323
324 if (items >= (int)cp->c_entries)
325 {
326 bLocalEOFflag = true;
327 }
328
329 /*
330 * Detect valence FS corruption.
331 *
332 * We are holding the cnode lock exclusive, so there should not be
333 * anybody modifying the valence field of this cnode. If we enter
334 * this block, that means we observed filesystem corruption, because
335 * this directory reported a valence of 0, yet we found at least one
336 * item. In this case, we need to minimally self-heal this
337 * directory to prevent userland from tripping over a directory
338 * that appears empty (getattr of valence reports 0), but actually
339 * has contents.
340 *
341 * We'll force the cnode update at the end of the function after
342 * completing all of the normal getdirentries steps.
343 */
344 if ((cp->c_entries == 0) && (items > 0))
345 {
346 /* disk corruption */
347 cp->c_entries++;
348 /* Mark the cnode as dirty. */
349 cp->c_flag |= C_MODIFIED;
350 LFHFS_LOG(LEVEL_DEBUG, "hfs_vnop_readdir: repairing valence to non-zero! \n");
351 bump_valence++;
352 }
353
354
355 /* Convert catalog directory index back into an offset. */
356 while (tag == 0)
357 tag = (++cp->c_dirhinttag) << HFS_INDEX_BITS;
358 offset = ((index + 2) | tag);
359 dirhint->dh_index |= tag;
360
361 seekoffcalc:
362 cp->c_touch_acctime = TRUE;
363
364 if (numdirent)
365 {
366 if (startoffset == 0)
367 items += 2;
368 else if (startoffset == 1)
369 items += 1;
370
371 *numdirent = items;
372 }
373
374 out:
375 /* If we didn't do anything then go ahead and dump the hint. */
376 if ((dirhint != NULL) && (dirhint != &localhint) && (offset == startoffset))
377 {
378 hfs_reldirhint(cp, dirhint);
379 bLocalEOFflag = true;
380 }
381
382 if (eofflag)
383 {
384 *eofflag = bLocalEOFflag;
385 }
386
387 if (dirhint == &localhint)
388 {
389 cat_releasedesc(&localhint.dh_desc);
390 }
391
392 if (bump_valence)
393 {
394 /* force the update before dropping the cnode lock*/
395 hfs_update(vp, 0);
396 }
397
398 hfs_unlock(cp);
399
400 return (error);
401 }
402
403 /*
404 * readdirattr operation will return attributes for the items in the
405 * directory specified.
406 *
407 * It does not do . and .. entries. The problem is if you are at the root of the
408 * hfs directory and go to .. you could be crossing a mountpoint into a
409 * different (ufs) file system. The attributes that apply for it may not
410 * apply for the file system you are doing the readdirattr on. To make life
411 * simpler, this call will only return entries in its directory, hfs like.
412 */
413 int
414 hfs_vnop_readdirattr(vnode_t vp, int *eofflag, int *numdirent, ReadDirBuff_s* psReadDirBuffer, uint64_t puCookie)
415 {
416 int error;
417 uint32_t newstate;
418 uint32_t uMaxCount = (uint32_t) psReadDirBuffer->uBufferResid / _UVFS_DIRENTRYATTR_RECLEN(UVFS_DIRENTRYATTR_NAMEOFF,0);
419
420 if (psReadDirBuffer->pvBuffer == NULL || psReadDirBuffer->uBufferResid < sizeof(UVFSDirEntry))
421 {
422 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readdirattr: buffer input is invalid\n");
423 return EINVAL;
424 }
425
426 error = hfs_readdirattr_internal(vp, psReadDirBuffer, uMaxCount, &newstate, eofflag, numdirent, puCookie);
427
428 return (error);
429 }
430
431 /*
432 * Sync all hfs B-trees. Use this instead of journal_flush for a volume
433 * without a journal. Note that the volume bitmap does not get written;
434 * we rely on fsck_hfs to fix that up (which it can do without any loss
435 * of data).
436 */
437 static int
438 hfs_metasync_all(struct hfsmount *hfsmp)
439 {
440 int lockflags;
441
442 /* Lock all of the B-trees so we get a mutually consistent state */
443 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG|SFL_EXTENTS|SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
444
445 #if LF_HFS_FULL_VNODE_SUPPORT
446 //Curently we don't keep any cache for btree buffers.
447 //When we will have a cache we will have to flush it out here.
448 /* Sync each of the B-trees */
449 if (hfsmp->hfs_catalog_vp)
450 hfs_btsync(hfsmp->hfs_catalog_vp, 0);
451 if (hfsmp->hfs_extents_vp)
452 hfs_btsync(hfsmp->hfs_extents_vp, 0);
453 if (hfsmp->hfs_attribute_vp)
454 hfs_btsync(hfsmp->hfs_attribute_vp, 0);
455 #endif
456 hfs_systemfile_unlock(hfsmp, lockflags);
457
458 return 0;
459 }
460 /*
461 * cnode must be locked
462 */
463 int
464 hfs_fsync(struct vnode *vp, int waitfor, hfs_fsync_mode_t fsyncmode)
465 {
466 struct cnode *cp = VTOC(vp);
467 struct filefork *fp = NULL;
468 int retval = 0;
469 struct timeval tv;
470 int took_trunc_lock = 0;
471 int fsync_default = 1;
472
473 /*
474 * Applications which only care about data integrity rather than full
475 * file integrity may opt out of (delay) expensive metadata update
476 * operations as a performance optimization.
477 */
478 int wait = (waitfor == MNT_WAIT); /* attributes necessary for data retrieval */
479 if (fsyncmode != HFS_FSYNC)
480 fsync_default = 0;
481
482 /* HFS directories don't have any data blocks. */
483 if (vnode_isdir(vp))
484 goto metasync;
485 fp = VTOF(vp);
486
487 /*
488 * For system files flush the B-tree header and
489 * for regular files write out any clusters
490 */
491 if (vnode_issystem(vp))
492 {
493 if (VTOF(vp)->fcbBTCBPtr != NULL)
494 {
495 // XXXdbg
496 if (VTOHFS(vp)->jnl == NULL)
497 {
498 BTFlushPath(VTOF(vp));
499 }
500 }
501 }
502 else
503 {
504 //TBD- Since we always flush the data for every file when it is being updated
505 // we don't need to do that here.
506 // hfs_unlock(cp);
507 // hfs_lock_truncate(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
508 // took_trunc_lock = 1;
509 //
510 // if (fp->ff_unallocblocks != 0)
511 // {
512 // hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
513 //
514 // hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
515 // }
516 //
517 //#if LF_HFS_FULL_VNODE_SUPPORT
518 // /* Don't hold cnode lock when calling into cluster layer. */
519 // (void) cluster_push(vp, waitdata ? IO_SYNC : 0);
520 //#endif
521 //
522 // hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
523 }
524
525 /*
526 * When MNT_WAIT is requested and the zero fill timeout
527 * has expired then we must explicitly zero out any areas
528 * that are currently marked invalid (holes).
529 *
530 * Files with NODUMP can bypass zero filling here.
531 */
532 if (fp && (((cp->c_flag & C_ALWAYS_ZEROFILL) && !TAILQ_EMPTY(&fp->ff_invalidranges)) ||
533 ((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
534 ((cp->c_bsdflags & UF_NODUMP) == 0) &&
535 (vnode_issystem(vp) ==0) &&
536 cp->c_zftimeout != 0)))
537 {
538 microtime(&tv);
539 if ((cp->c_flag & C_ALWAYS_ZEROFILL) == 0 && fsync_default && tv.tv_sec < (long)cp->c_zftimeout)
540 {
541 /* Remember that a force sync was requested. */
542 cp->c_flag |= C_ZFWANTSYNC;
543 goto datasync;
544 }
545 if (!TAILQ_EMPTY(&fp->ff_invalidranges))
546 {
547 if (!took_trunc_lock || (cp->c_truncatelockowner == HFS_SHARED_OWNER))
548 {
549 hfs_unlock(cp);
550 if (took_trunc_lock) {
551 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
552 }
553 hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
554 hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
555 took_trunc_lock = 1;
556 }
557
558 #if LF_HFS_FULL_VNODE_SUPPORT
559 hfs_flush_invalid_ranges(vp);
560 hfs_unlock(cp);
561 (void) cluster_push(vp, waitdata ? IO_SYNC : 0);
562 hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
563 #endif
564 }
565 }
566
567 datasync:
568 if (took_trunc_lock)
569 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
570
571 // TBD - symlink can't be dirsty since we always write the data fully to the device
572 // else if (fsync_default && vnode_islnk(vp) && vnode_hasdirtyblks(vp) && vnode_isrecycled(vp))
573 // {
574 // /*
575 // * If it's a symlink that's dirty and is about to be recycled,
576 // * we need to flush the journal.
577 // */
578 // fsync_default = 0;
579 // }
580
581 metasync:
582 if (vnode_isreg(vp) && vnode_issystem(vp))
583 {
584 if (VTOF(vp)->fcbBTCBPtr != NULL)
585 {
586 microtime(&tv);
587 BTSetLastSync(VTOF(vp), (u_int32_t) tv.tv_sec);
588 }
589 cp->c_touch_acctime = FALSE;
590 cp->c_touch_chgtime = FALSE;
591 cp->c_touch_modtime = FALSE;
592 }
593 else
594 {
595 retval = hfs_update(vp, HFS_UPDATE_FORCE);
596 /*
597 * When MNT_WAIT is requested push out the catalog record for
598 * this file. If they asked for a full fsync, we can skip this
599 * because the journal_flush or hfs_metasync_all will push out
600 * all of the metadata changes.
601 */
602 #if 0
603 /*
604 * As we are not supporting any write buf caches / delay writes,
605 * this is not needed.
606 */
607 if ((retval == 0) && wait && fsync_default && cp->c_hint &&
608 !ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
609 hfs_metasync(VTOHFS(vp), (daddr64_t)cp->c_hint);
610 }
611 #endif
612 /*
613 * If this was a full fsync, make sure all metadata
614 * changes get to stable storage.
615 */
616 if (!fsync_default)
617 {
618 if (VTOHFS(vp)->jnl) {
619 if (fsyncmode == HFS_FSYNC_FULL)
620 hfs_flush(VTOHFS(vp), HFS_FLUSH_FULL);
621 else
622 hfs_flush(VTOHFS(vp), HFS_FLUSH_JOURNAL_BARRIER);
623 }
624 else
625 {
626 retval = hfs_metasync_all(VTOHFS(vp));
627 hfs_flush(VTOHFS(vp), HFS_FLUSH_CACHE);
628 }
629 }
630 }
631
632 #if LF_HFS_FULL_VNODE_SUPPORT
633 if (!hfs_is_dirty(cp) && !ISSET(cp->c_flag, C_DELETED))
634 vnode_cleardirty(vp);
635 #endif
636
637 return (retval);
638 }
639
640 /*
641 * hfs_removefile
642 *
643 * Similar to hfs_vnop_remove except there are additional options.
644 * This function may be used to remove directories if they have
645 * lots of EA's -- note the 'allow_dirs' argument.
646 *
647 * This function is able to delete blocks & fork data for the resource
648 * fork even if it does not exist in core (and have a backing vnode).
649 * It should infer the correct behavior based on the number of blocks
650 * in the cnode and whether or not the resource fork pointer exists or
651 * not. As a result, one only need pass in the 'vp' corresponding to the
652 * data fork of this file (or main vnode in the case of a directory).
653 * Passing in a resource fork will result in an error.
654 *
655 * Because we do not create any vnodes in this function, we are not at
656 * risk of deadlocking against ourselves by double-locking.
657 *
658 * Requires cnode and truncate locks to be held.
659 */
660 int
661 hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
662 int flags, int skip_reserve, int allow_dirs, int only_unlink)
663 {
664 struct cnode *cp;
665 struct cnode *dcp;
666 struct vnode *rsrc_vp = NULL;
667 struct hfsmount *hfsmp;
668 struct cat_desc desc;
669 int dataforkbusy = 0;
670 int rsrcforkbusy = 0;
671 int lockflags;
672 int error = 0;
673 int started_tr = 0;
674 int isbigfile = 0, defer_remove=0;
675 bool isdir= false;
676 int update_vh = 0;
677
678 cp = VTOC(vp);
679 dcp = VTOC(dvp);
680 hfsmp = VTOHFS(vp);
681
682 /* Check if we lost a race post lookup. */
683 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
684 return (EINVAL);
685 }
686
687 if (!hfs_valid_cnode(hfsmp, dvp, cnp, cp->c_fileid, NULL, &error))
688 {
689 return error;
690 }
691
692 /* Make sure a remove is permitted */
693 /* Don't allow deleting the journal or journal_info_block. */
694 if (VNODE_IS_RSRC(vp) || vnode_issystem(vp) || IsEntryAJnlFile(hfsmp, cp->c_fileid))
695 {
696 LFHFS_LOG(LEVEL_ERROR, "hfs_removefile: Removing %s file is not premited\n", VNODE_IS_RSRC(vp) ? "Resource" : (vnode_issystem(vp)? "System" : "Journal"));
697 return (EPERM);
698 }
699 else
700 {
701 /*
702 * We know it's a data fork.
703 * Probe the cnode to see if we have a valid resource fork
704 * in hand or not.
705 */
706 rsrc_vp = cp->c_rsrc_vp;
707 }
708
709 /*
710 * Hard links require special handling.
711 */
712 if (cp->c_flag & C_HARDLINK)
713 {
714 /* A directory hard link with a link count of one is
715 * treated as a regular directory. Therefore it should
716 * only be removed using rmdir().
717 */
718 if (IS_DIR(vp) && (cp->c_linkcount == 1) && (allow_dirs == 0))
719 {
720 LFHFS_LOG(LEVEL_ERROR, "hfs_removefile: Trying to remove an hardlink directory\n");
721 return (EPERM);
722 }
723
724 return hfs_unlink(hfsmp, dvp, vp, cnp, skip_reserve);
725 }
726
727 /* Directories should call hfs_rmdir! (unless they have a lot of attributes) */
728 if (IS_DIR(vp))
729 {
730 if (!allow_dirs)
731 {
732 return (EPERM); /* POSIX */
733 }
734 isdir = true;
735 }
736
737 /* Sanity check the parent ids. */
738 if ((cp->c_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
739 (cp->c_parentcnid != dcp->c_fileid))
740 {
741 LFHFS_LOG(LEVEL_ERROR, "hfs_removefile: Parent ID's are wrong\n");
742 return (EINVAL);
743 }
744
745 dcp->c_flag |= C_DIR_MODIFICATION;
746
747 // this guy is going away so mark him as such
748 cp->c_flag |= C_DELETED;
749
750 /*
751 * If the caller was operating on a file (as opposed to a
752 * directory with EAs), then we need to figure out
753 * whether or not it has a valid resource fork vnode.
754 *
755 * If there was a valid resource fork vnode, then we need
756 * to use hfs_truncate to eliminate its data. If there is
757 * no vnode, then we hold the cnode lock which would
758 * prevent it from being created. As a result,
759 * we can use the data deletion functions which do not
760 * require that a cnode/vnode pair exist.
761 */
762
763 /* Check if this file is being used. */
764 if ( !isdir )
765 {
766 dataforkbusy = 0; /*vnode_isinuse(vp, 0);*/
767 /*
768 * At this point, we know that 'vp' points to the
769 * a data fork because we checked it up front. And if
770 * there is no rsrc fork, rsrc_vp will be NULL.
771 */
772 if (rsrc_vp && (cp->c_blocks - VTOF(vp)->ff_blocks))
773 {
774 rsrcforkbusy = 0; /*vnode_isinuse(rsrc_vp, 0);*/
775 }
776
777 /* Check if we have to break the deletion into multiple pieces. */
778 isbigfile = cp->c_datafork->ff_size >= HFS_BIGFILE_SIZE;
779 }
780
781 /* Check if the file has xattrs. If it does we'll have to delete them in
782 individual transactions in case there are too many */
783 if ((hfsmp->hfs_attribute_vp != NULL) && (cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0)
784 {
785 defer_remove = 1;
786 }
787
788 /* If we are explicitly told to only unlink item and move to hidden dir, then do it */
789 if (only_unlink)
790 {
791 defer_remove = 1;
792 }
793
794 /*
795 * Carbon semantics prohibit deleting busy files.
796 * (enforced when VNODE_REMOVE_NODELETEBUSY is requested)
797 */
798 if (dataforkbusy || rsrcforkbusy)
799 {
800 if ((flags & VNODE_REMOVE_NODELETEBUSY) || (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0))
801 {
802 error = EBUSY;
803 goto out;
804 }
805 }
806
807 /*
808 * Do a ubc_setsize to indicate we need to wipe contents if:
809 * 1) item is a regular file.
810 * 2) Neither fork is busy AND we are not told to unlink this.
811 *
812 * We need to check for the defer_remove since it can be set without
813 * having a busy data or rsrc fork
814 */
815 if (isdir == 0 && (!dataforkbusy || !rsrcforkbusy) && (defer_remove == 0))
816 {
817 /*
818 * A ubc_setsize can cause a pagein so defer it
819 * until after the cnode lock is dropped. The
820 * cnode lock cannot be dropped/reacquired here
821 * since we might already hold the journal lock.
822 */
823 if (!dataforkbusy && cp->c_datafork->ff_blocks && !isbigfile)
824 {
825 cp->c_flag |= C_NEED_DATA_SETSIZE;
826 }
827 if (!rsrcforkbusy && rsrc_vp)
828 {
829 cp->c_flag |= C_NEED_RSRC_SETSIZE;
830 }
831 }
832
833 if ((error = hfs_start_transaction(hfsmp)) != 0)
834 {
835 goto out;
836 }
837 started_tr = 1;
838
839 /*
840 * Prepare to truncate any non-busy forks. Busy forks will
841 * get truncated when their vnode goes inactive.
842 * Note that we will only enter this region if we
843 * can avoid creating an open-unlinked file. If
844 * either region is busy, we will have to create an open
845 * unlinked file.
846 *
847 * Since we are deleting the file, we need to stagger the runtime
848 * modifications to do things in such a way that a crash won't
849 * result in us getting overlapped extents or any other
850 * bad inconsistencies. As such, we call prepare_release_storage
851 * which updates the UBC, updates quota information, and releases
852 * any loaned blocks that belong to this file. No actual
853 * truncation or bitmap manipulation is done until *AFTER*
854 * the catalog record is removed.
855 */
856 if (isdir == 0 && (!dataforkbusy && !rsrcforkbusy) && (only_unlink == 0))
857 {
858 if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0)
859 {
860 error = hfs_prepare_release_storage (hfsmp, vp);
861 if (error)
862 {
863 goto out;
864 }
865 update_vh = 1;
866 }
867
868 /*
869 * If the resource fork vnode does not exist, we can skip this step.
870 */
871 if (!rsrcforkbusy && rsrc_vp)
872 {
873 error = hfs_prepare_release_storage (hfsmp, rsrc_vp);
874 if (error)
875 {
876 goto out;
877 }
878 update_vh = 1;
879 }
880 }
881
882 /*
883 * Protect against a race with rename by using the component
884 * name passed in and parent id from dvp (instead of using
885 * the cp->c_desc which may have changed). Also, be aware that
886 * because we allow directories to be passed in, we need to special case
887 * this temporary descriptor in case we were handed a directory.
888 */
889 if (isdir)
890 {
891 desc.cd_flags = CD_ISDIR;
892 }
893 else
894 {
895 desc.cd_flags = 0;
896 }
897 desc.cd_encoding = cp->c_desc.cd_encoding;
898 desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
899 desc.cd_namelen = cnp->cn_namelen;
900 desc.cd_parentcnid = dcp->c_fileid;
901 desc.cd_hint = cp->c_desc.cd_hint;
902 desc.cd_cnid = cp->c_cnid;
903 struct timeval tv;
904 microtime(&tv);
905
906 /*
907 * There are two cases to consider:
908 * 1. File/Dir is busy/big/defer_remove ==> move/rename the file/dir
909 * 2. File is not in use ==> remove the file
910 *
911 * We can get a directory in case 1 because it may have had lots of attributes,
912 * which need to get removed here.
913 */
914 if (dataforkbusy || rsrcforkbusy || isbigfile || defer_remove)
915 {
916 char delname[32];
917 struct cat_desc to_desc;
918 struct cat_desc todir_desc;
919
920 /*
921 * Orphan this file or directory (move to hidden directory).
922 * Again, we need to take care that we treat directories as directories,
923 * and files as files. Because directories with attributes can be passed in
924 * check to make sure that we have a directory or a file before filling in the
925 * temporary descriptor's flags. We keep orphaned directories AND files in
926 * the FILE_HARDLINKS private directory since we're generalizing over all
927 * orphaned filesystem objects.
928 */
929 bzero(&todir_desc, sizeof(todir_desc));
930 todir_desc.cd_parentcnid = 2;
931
932 MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid);
933 bzero(&to_desc, sizeof(to_desc));
934 to_desc.cd_nameptr = (const u_int8_t *)delname;
935 to_desc.cd_namelen = strlen(delname);
936 to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
937 if (isdir)
938 {
939 to_desc.cd_flags = CD_ISDIR;
940 }
941 else
942 {
943 to_desc.cd_flags = 0;
944 }
945 to_desc.cd_cnid = cp->c_cnid;
946
947 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
948 if (!skip_reserve)
949 {
950 if ((error = cat_preflight(hfsmp, CAT_RENAME, NULL)))
951 {
952 hfs_systemfile_unlock(hfsmp, lockflags);
953 goto out;
954 }
955 }
956
957 error = cat_rename(hfsmp, &desc, &todir_desc, &to_desc, (struct cat_desc *)NULL);
958
959 if (error == 0)
960 {
961 hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries++;
962 if (isdir == 1)
963 {
964 INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
965 }
966 (void) cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS], &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
967
968 /* Update the parent directory */
969 if (dcp->c_entries > 0)
970 dcp->c_entries--;
971 if (isdir == 1)
972 {
973 DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
974 }
975 dcp->c_dirchangecnt++;
976 hfs_incr_gencount(dcp);
977
978 dcp->c_ctime = tv.tv_sec;
979 dcp->c_mtime = tv.tv_sec;
980 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
981
982 /* Update the file or directory's state */
983 cp->c_flag |= C_DELETED;
984 cp->c_ctime = tv.tv_sec;
985 --cp->c_linkcount;
986 (void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
987 }
988
989 hfs_systemfile_unlock(hfsmp, lockflags);
990 if (error)
991 goto out;
992 }
993 else
994 {
995 /*
996 * Nobody is using this item; we can safely remove everything.
997 */
998
999 struct filefork *temp_rsrc_fork = NULL;
1000 u_int32_t fileid = cp->c_fileid;
1001
1002 /*
1003 * Figure out if we need to read the resource fork data into
1004 * core before wiping out the catalog record.
1005 *
1006 * 1) Must not be a directory
1007 * 2) cnode's c_rsrcfork ptr must be NULL.
1008 * 3) rsrc fork must have actual blocks
1009 */
1010 if ((isdir == 0) && (cp->c_rsrcfork == NULL) && (cp->c_blocks - VTOF(vp)->ff_blocks))
1011 {
1012 /*
1013 * The resource fork vnode & filefork did not exist.
1014 * Create a temporary one for use in this function only.
1015 */
1016 temp_rsrc_fork = hfs_mallocz(sizeof(struct filefork));
1017 temp_rsrc_fork->ff_cp = cp;
1018 rl_init(&temp_rsrc_fork->ff_invalidranges);
1019 }
1020
1021 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1022
1023 /* Look up the resource fork first, if necessary */
1024 if (temp_rsrc_fork)
1025 {
1026 error = cat_lookup (hfsmp, &desc, 1, (struct cat_desc*) NULL, (struct cat_attr*) NULL, &temp_rsrc_fork->ff_data, NULL);
1027 if (error)
1028 {
1029 hfs_free(temp_rsrc_fork);
1030 hfs_systemfile_unlock (hfsmp, lockflags);
1031 goto out;
1032 }
1033 }
1034
1035 if (!skip_reserve)
1036 {
1037 if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL)))
1038 {
1039 if (temp_rsrc_fork)
1040 {
1041 hfs_free(temp_rsrc_fork);
1042 }
1043 hfs_systemfile_unlock(hfsmp, lockflags);
1044 goto out;
1045 }
1046 }
1047
1048 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1049
1050 if (error && error != ENXIO && error != ENOENT)
1051 {
1052 LFHFS_LOG(LEVEL_ERROR, "hfs_removefile: deleting file %s (id=%d) vol=%s err=%d\n",
1053 cp->c_desc.cd_nameptr, cp->c_attr.ca_fileid, hfsmp->vcbVN, error);
1054 }
1055
1056 if (error == 0)
1057 {
1058 /* Update the parent directory */
1059 if (dcp->c_entries > 0)
1060 {
1061 dcp->c_entries--;
1062 }
1063 dcp->c_dirchangecnt++;
1064 hfs_incr_gencount(dcp);
1065
1066 dcp->c_ctime = tv.tv_sec;
1067 dcp->c_mtime = tv.tv_sec;
1068 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1069 }
1070
1071 hfs_systemfile_unlock(hfsmp, lockflags);
1072
1073 if (error)
1074 {
1075 if (temp_rsrc_fork)
1076 {
1077 hfs_free(temp_rsrc_fork);
1078 }
1079 goto out;
1080 }
1081
1082 /*
1083 * Now that we've wiped out the catalog record, the file effectively doesn't
1084 * exist anymore. So update the quota records to reflect the loss of the
1085 * data fork and the resource fork.
1086 */
1087
1088 if (IS_LNK(vp) && cp->c_datafork->ff_symlinkptr)
1089 {
1090 hfs_free(cp->c_datafork->ff_symlinkptr);
1091 cp->c_datafork->ff_symlinkptr = NULL;
1092 }
1093
1094 /*
1095 * If we didn't get any errors deleting the catalog entry, then go ahead
1096 * and release the backing store now. The filefork pointers are still valid.
1097 */
1098 if (temp_rsrc_fork)
1099 {
1100 error = hfs_release_storage (hfsmp, cp->c_datafork, temp_rsrc_fork, fileid);
1101 }
1102 else
1103 {
1104 /* if cp->c_rsrcfork == NULL, hfs_release_storage will skip over it. */
1105 error = hfs_release_storage (hfsmp, cp->c_datafork, cp->c_rsrcfork, fileid);
1106 }
1107 if (error)
1108 {
1109 /*
1110 * If we encountered an error updating the extents and bitmap,
1111 * mark the volume inconsistent. At this point, the catalog record has
1112 * already been deleted, so we can't recover it at this point. We need
1113 * to proceed and update the volume header and mark the cnode C_NOEXISTS.
1114 * The subsequent fsck should be able to recover the free space for us.
1115 */
1116 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
1117 }
1118 else
1119 {
1120 /* reset update_vh to 0, since hfs_release_storage should have done it for us */
1121 update_vh = 0;
1122 }
1123
1124 /* Get rid of the temporary rsrc fork */
1125 if (temp_rsrc_fork)
1126 {
1127 hfs_free(temp_rsrc_fork);
1128 }
1129
1130 cp->c_flag |= C_NOEXISTS;
1131 cp->c_flag &= ~C_DELETED;
1132
1133 cp->c_touch_chgtime = TRUE;
1134 --cp->c_linkcount;
1135
1136 /*
1137 * We must never get a directory if we're in this else block. We could
1138 * accidentally drop the number of files in the volume header if we did.
1139 */
1140 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
1141 }
1142
1143 /*
1144 * All done with this cnode's descriptor...
1145 *
1146 * Note: all future catalog calls for this cnode must be by
1147 * fileid only. This is OK for HFS (which doesn't have file
1148 * thread records) since HFS doesn't support the removal of
1149 * busy files.
1150 */
1151 cat_releasedesc(&cp->c_desc);
1152
1153 out:
1154 if (error)
1155 {
1156 cp->c_flag &= ~C_DELETED;
1157 }
1158
1159 if (update_vh)
1160 {
1161 /*
1162 * If we bailed out earlier, we may need to update the volume header
1163 * to deal with the borrowed blocks accounting.
1164 */
1165 hfs_volupdate (hfsmp, VOL_UPDATE, 0);
1166 }
1167
1168 if (started_tr)
1169 {
1170 hfs_end_transaction(hfsmp);
1171 }
1172
1173 dcp->c_flag &= ~C_DIR_MODIFICATION;
1174 //TBD - We have wakeup here but can't see anyone who's msleeping on c_flag...
1175 //wakeup((caddr_t)&dcp->c_flag);
1176
1177 return (error);
1178 }
1179
1180 /*
1181 * Remove a file or link.
1182 */
1183 int
1184 hfs_vnop_remove(struct vnode* psParentDir,struct vnode *psFileToRemove, struct componentname* psCN, int iFlags)
1185 {
1186 struct cnode *dcp = VTOC(psParentDir);
1187 struct cnode *cp = VTOC(psFileToRemove);
1188 struct vnode *rvp = NULL;
1189 int error = 0;
1190
1191 if (psParentDir == psFileToRemove)
1192 {
1193 return (EINVAL);
1194 }
1195
1196 relock:
1197
1198 hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
1199
1200 if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK)))
1201 {
1202 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1203 if (rvp)
1204 {
1205 hfs_chash_lower_OpenLookupCounter(cp);
1206 rvp = NULL;
1207 }
1208 return (error);
1209 }
1210
1211 /*
1212 * Lazily respond to determining if there is a valid resource fork
1213 * vnode attached to 'cp' if it is a regular file or symlink.
1214 * If the vnode does not exist, then we may proceed without having to
1215 * create it.
1216 *
1217 * If, however, it does exist, then we need to acquire an iocount on the
1218 * vnode after acquiring its vid. This ensures that if we have to do I/O
1219 * against it, it can't get recycled from underneath us in the middle
1220 * of this call.
1221 *
1222 * Note: this function may be invoked for directory hardlinks, so just skip these
1223 * steps if 'vp' is a directory.
1224 */
1225 enum vtype vtype = psFileToRemove->sFSParams.vnfs_vtype;
1226 if ((vtype == VLNK) || (vtype == VREG))
1227 {
1228 if ((cp->c_rsrc_vp) && (rvp == NULL))
1229 {
1230 /* We need to acquire the rsrc vnode */
1231 rvp = cp->c_rsrc_vp;
1232 hfs_chash_raise_OpenLookupCounter(cp);
1233 /* Unlock everything to acquire iocount on the rsrc vnode */
1234 hfs_unlock_truncate (cp, HFS_LOCK_DEFAULT);
1235 hfs_unlockpair (dcp, cp);
1236
1237 goto relock;
1238 }
1239 }
1240
1241 /*
1242 * Check to see if we raced rmdir for the parent directory
1243 * hfs_removefile already checks for a race on vp/cp
1244 */
1245 if (dcp->c_flag & (C_DELETED | C_NOEXISTS))
1246 {
1247 error = ENOENT;
1248 goto rm_done;
1249 }
1250
1251 error = hfs_removefile(psParentDir, psFileToRemove, psCN, iFlags, 0, 0, 0);
1252
1253 /*
1254 * Drop the truncate lock before unlocking the cnode
1255 * (which can potentially perform a vnode_put and
1256 * recycle the vnode which in turn might require the
1257 * truncate lock)
1258 */
1259 rm_done:
1260 //Update Directory version
1261 psParentDir->sExtraData.sDirData.uDirVersion++;
1262
1263 hfs_unlockpair(dcp, cp);
1264 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1265
1266 if (rvp)
1267 {
1268 hfs_chash_lower_OpenLookupCounter(cp);
1269 rvp = NULL;
1270 }
1271 return (error);
1272 }
1273
1274 /*
1275 * Remove a directory.
1276 */
1277 int
1278 hfs_vnop_rmdir(struct vnode *dvp, struct vnode *vp, struct componentname* psCN)
1279 {
1280 int error = 0;
1281 struct cnode *dcp = VTOC(dvp);
1282 struct cnode *cp = VTOC(vp);
1283
1284 if (!S_ISDIR(cp->c_mode))
1285 {
1286 return (ENOTDIR);
1287 }
1288 if (dvp == vp)
1289 {
1290 return (EINVAL);
1291 }
1292
1293 if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK)))
1294 {
1295 return (error);
1296 }
1297
1298 /* Check for a race with rmdir on the parent directory */
1299 if (dcp->c_flag & (C_DELETED | C_NOEXISTS))
1300 {
1301 hfs_unlockpair (dcp, cp);
1302 return ENOENT;
1303 }
1304
1305 error = hfs_removedir(dvp, vp, psCN, 0, 0);
1306
1307 hfs_unlockpair(dcp, cp);
1308
1309 return (error);
1310 }
1311
1312 /*
1313 * Remove a directory
1314 *
1315 * Both dvp and vp cnodes are locked
1316 */
1317 int
1318 hfs_removedir(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, int skip_reserve, int only_unlink)
1319 {
1320 struct cnode *cp;
1321 struct cnode *dcp;
1322 struct hfsmount * hfsmp;
1323 struct cat_desc desc;
1324 int lockflags;
1325 int error = 0, started_tr = 0;
1326
1327 cp = VTOC(vp);
1328 dcp = VTOC(dvp);
1329 hfsmp = VTOHFS(vp);
1330
1331 if (cp->c_flag & (C_NOEXISTS | C_DELETED)){
1332 return (EINVAL);
1333 }
1334
1335 if (cp->c_entries != 0){
1336 return (ENOTEMPTY);
1337 }
1338
1339 /* Deal with directory hardlinks */
1340 if (cp->c_flag & C_HARDLINK)
1341 {
1342 /*
1343 * Note that if we have a directory which was a hardlink at any point,
1344 * its actual directory data is stored in the directory inode in the hidden
1345 * directory rather than the leaf element(s) present in the namespace.
1346 *
1347 * If there are still other hardlinks to this directory,
1348 * then we'll just eliminate this particular link and the vnode will still exist.
1349 * If this is the last link to an empty directory, then we'll open-unlink the
1350 * directory and it will be only tagged with C_DELETED (as opposed to C_NOEXISTS).
1351 *
1352 * We could also return EBUSY here.
1353 */
1354
1355 return hfs_unlink(hfsmp, dvp, vp, cnp, skip_reserve);
1356 }
1357
1358 /*
1359 * In a few cases, we may want to allow the directory to persist in an
1360 * open-unlinked state. If the directory is being open-unlinked (still has usecount
1361 * references), or if it has EAs, or if it was being deleted as part of a rename,
1362 * then we go ahead and move it to the hidden directory.
1363 *
1364 * If the directory is being open-unlinked, then we want to keep the catalog entry
1365 * alive so that future EA calls and fchmod/fstat etc. do not cause issues later.
1366 *
1367 * If the directory had EAs, then we want to use the open-unlink trick so that the
1368 * EA removal is not done in one giant transaction. Otherwise, it could cause a panic
1369 * due to overflowing the journal.
1370 *
1371 * Finally, if it was deleted as part of a rename, we move it to the hidden directory
1372 * in order to maintain rename atomicity.
1373 *
1374 * Note that the allow_dirs argument to hfs_removefile specifies that it is
1375 * supposed to handle directories for this case.
1376 */
1377
1378 if (((hfsmp->hfs_attribute_vp != NULL) && ((cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0)) || (only_unlink != 0))
1379 {
1380
1381 int ret = hfs_removefile(dvp, vp, cnp, 0, 0, 1, only_unlink);
1382 // Will be released in the layer above where it was created
1383 // vnode_recycle(vp);
1384 return ret;
1385 }
1386
1387 dcp->c_flag |= C_DIR_MODIFICATION;
1388
1389 if ((error = hfs_start_transaction(hfsmp)) != 0)
1390 {
1391 goto out;
1392 }
1393 started_tr = 1;
1394
1395 /*
1396 * Verify the directory is empty (and valid).
1397 * (Rmdir ".." won't be valid since
1398 * ".." will contain a reference to
1399 * the current directory and thus be
1400 * non-empty.)
1401 */
1402 if ((dcp->c_bsdflags & (UF_APPEND | SF_APPEND)) || (cp->c_bsdflags & ((UF_IMMUTABLE | SF_IMMUTABLE | UF_APPEND | SF_APPEND))))
1403 {
1404 error = EPERM;
1405 goto out;
1406 }
1407
1408 /*
1409 * Protect against a race with rename by using the component
1410 * name passed in and parent id from dvp (instead of using
1411 * the cp->c_desc which may have changed).
1412 */
1413 desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
1414 desc.cd_namelen = cnp->cn_namelen;
1415 desc.cd_parentcnid = dcp->c_fileid;
1416 desc.cd_cnid = cp->c_cnid;
1417 desc.cd_flags = CD_ISDIR;
1418 desc.cd_encoding = cp->c_encoding;
1419 desc.cd_hint = 0;
1420
1421 if (!hfs_valid_cnode(hfsmp, dvp, cnp, cp->c_fileid, NULL, &error))
1422 {
1423 error = 0;
1424 goto out;
1425 }
1426
1427 /* Remove entry from catalog */
1428 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1429
1430 if (!skip_reserve)
1431 {
1432 /*
1433 * Reserve some space in the Catalog file.
1434 */
1435 if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL)))
1436 {
1437 hfs_systemfile_unlock(hfsmp, lockflags);
1438 goto out;
1439 }
1440 }
1441
1442 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1443
1444 if (!error)
1445 {
1446 /* The parent lost a child */
1447 if (dcp->c_entries > 0)
1448 dcp->c_entries--;
1449 DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
1450 dcp->c_dirchangecnt++;
1451 hfs_incr_gencount(dcp);
1452
1453 dcp->c_touch_chgtime = TRUE;
1454 dcp->c_touch_modtime = TRUE;
1455 dcp->c_flag |= C_MODIFIED;
1456
1457 hfs_update(dcp->c_vp, 0);
1458 }
1459
1460 hfs_systemfile_unlock(hfsmp, lockflags);
1461
1462 if (error)
1463 goto out;
1464
1465 hfs_volupdate(hfsmp, VOL_RMDIR, (dcp->c_cnid == kHFSRootFolderID));
1466
1467 /* Mark C_NOEXISTS since the catalog entry is now gone */
1468 cp->c_flag |= C_NOEXISTS;
1469
1470 out:
1471 dvp->sExtraData.sDirData.uDirVersion++;
1472
1473 dcp->c_flag &= ~C_DIR_MODIFICATION;
1474 //TBD - We have wakeup here but can't see anyone who's msleeping on c_flag...
1475 // wakeup((caddr_t)&dcp->c_flag);
1476
1477 if (started_tr)
1478 {
1479 hfs_end_transaction(hfsmp);
1480 }
1481
1482 return (error);
1483 }
1484
1485 int hfs_vnop_setattr( vnode_t vp, const UVFSFileAttributes *attr )
1486 {
1487 int err = 0;
1488 if ( attr->fa_validmask == 0 )
1489 {
1490 return 0;
1491 }
1492
1493 if ( ( attr->fa_validmask & READ_ONLY_FA_FIELDS )
1494 /*|| ( attr->fa_validmask & ~VALID_IN_ATTR_MASK )*/)
1495 {
1496 return EINVAL;
1497 }
1498
1499 struct cnode *cp = NULL;
1500
1501 /* Don't allow modification of the journal. */
1502 struct hfsmount *hfsmp = VTOHFS(vp);
1503 if (hfs_is_journal_file(hfsmp, VTOC(vp))) {
1504 return (EPERM);
1505 }
1506
1507 /*
1508 * File size change request.
1509 * We are guaranteed that this is not a directory, and that
1510 * the filesystem object is writeable.
1511 */
1512
1513 if ( attr->fa_validmask & UVFS_FA_VALID_SIZE )
1514 {
1515 if (!vnode_isreg(vp))
1516 {
1517 if (vnode_isdir(vp) || vnode_islnk(vp))
1518 {
1519 return EPERM;
1520 }
1521 //otherwise return EINVAL
1522 return EINVAL;
1523 }
1524
1525 // Take truncate lock
1526 hfs_lock_truncate(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
1527
1528 // hfs_truncate will deal with the cnode lock
1529 err = hfs_truncate(vp, attr->fa_size, 0, 0);
1530
1531 hfs_unlock_truncate(VTOC(vp), HFS_LOCK_DEFAULT);
1532 if (err)
1533 return err;
1534 }
1535
1536
1537 if ((err = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
1538 return (err);
1539 cp = VTOC(vp);
1540
1541
1542 if ( attr->fa_validmask & UVFS_FA_VALID_UID )
1543 {
1544 cp->c_flag |= C_MODIFIED;
1545 cp->c_touch_chgtime = TRUE;
1546 cp->c_uid = attr->fa_uid;
1547 }
1548
1549 if ( attr->fa_validmask & UVFS_FA_VALID_GID )
1550 {
1551 cp->c_flag |= C_MODIFIED;
1552 cp->c_touch_chgtime = TRUE;
1553 cp->c_gid = attr->fa_gid;
1554 }
1555
1556 if ( attr->fa_validmask & UVFS_FA_VALID_MODE )
1557 {
1558 mode_t new_mode = (cp->c_mode & ~ALLPERMS) | (attr->fa_mode & ALLPERMS);
1559 if (new_mode != cp->c_mode) {
1560 cp->c_mode = new_mode;
1561 cp->c_flag |= C_MINOR_MOD;
1562 }
1563 }
1564
1565 if ( attr->fa_validmask & UVFS_FA_VALID_BSD_FLAGS )
1566 {
1567 cp->c_bsdflags = attr->fa_bsd_flags;
1568 }
1569
1570 /*
1571 * Timestamp updates.
1572 */
1573 if ( attr->fa_validmask & UVFS_FA_VALID_ATIME )
1574 {
1575 cp->c_atime = attr->fa_atime.tv_sec;
1576 cp->c_touch_acctime = FALSE;
1577 }
1578
1579 if ( attr->fa_validmask & UVFS_FA_VALID_BIRTHTIME )
1580 {
1581 cp->c_ctime = attr->fa_birthtime.tv_sec;
1582 }
1583
1584 if ( attr->fa_validmask & UVFS_FA_VALID_MTIME )
1585 {
1586 cp->c_mtime = attr->fa_mtime.tv_sec;
1587 cp->c_touch_modtime = FALSE;
1588 cp->c_touch_chgtime = TRUE;
1589
1590 hfs_clear_might_be_dirty_flag(cp);
1591 }
1592
1593 err = hfs_update(vp, 0);
1594
1595 /* Purge origin cache for cnode, since caller now has correct link ID for it
1596 * We purge it here since it was acquired for us during lookup, and we no longer need it.
1597 */
1598 if ((cp->c_flag & C_HARDLINK) && (!IS_DIR(vp))){
1599 hfs_relorigin(cp, 0);
1600 }
1601
1602 hfs_unlock(cp);
1603
1604 return err;
1605 }
1606
1607 /*
1608 * Update a cnode's on-disk metadata.
1609 *
1610 * The cnode must be locked exclusive. See declaration for possible
1611 * options.
1612 */
1613 int
1614 hfs_update(struct vnode *vp, int options)
1615 {
1616 #pragma unused (options)
1617
1618 struct cnode *cp = VTOC(vp);
1619 const struct cat_fork *dataforkp = NULL;
1620 const struct cat_fork *rsrcforkp = NULL;
1621 struct cat_fork datafork;
1622 struct cat_fork rsrcfork;
1623 struct hfsmount *hfsmp;
1624 int lockflags;
1625 int error = 0;
1626
1627 if (ISSET(cp->c_flag, C_NOEXISTS))
1628 return 0;
1629
1630 hfsmp = VTOHFS(vp);
1631
1632 if (((vnode_issystem(vp) && (cp->c_cnid < kHFSFirstUserCatalogNodeID))) ||
1633 hfsmp->hfs_catalog_vp == NULL){
1634 return (0);
1635 }
1636
1637 if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (cp->c_mode == 0)) {
1638 CLR(cp->c_flag, C_MODIFIED | C_MINOR_MOD | C_NEEDS_DATEADDED);
1639 cp->c_touch_acctime = 0;
1640 cp->c_touch_chgtime = 0;
1641 cp->c_touch_modtime = 0;
1642 return (0);
1643 }
1644
1645 hfs_touchtimes(hfsmp, cp);
1646
1647 if (!ISSET(cp->c_flag, C_MODIFIED | C_MINOR_MOD)
1648 && !hfs_should_save_atime(cp)) {
1649 // Nothing to update
1650 return 0;
1651 }
1652
1653 bool check_txn = false;
1654 if (!ISSET(options, HFS_UPDATE_FORCE) && !ISSET(cp->c_flag, C_MODIFIED)) {
1655 /*
1656 * This must be a minor modification. If the current
1657 * transaction already has an update for this node, then we
1658 * bundle in the modification.
1659 */
1660 if (hfsmp->jnl
1661 && journal_current_txn(hfsmp->jnl) == cp->c_update_txn) {
1662 check_txn = true;
1663 }
1664 else
1665 {
1666 error = 0;
1667 goto exit;
1668 }
1669 }
1670
1671 error = hfs_start_transaction(hfsmp);
1672 if ( error != 0 )
1673 {
1674 goto exit;
1675 }
1676
1677 if (check_txn
1678 && journal_current_txn(hfsmp->jnl) != cp->c_update_txn) {
1679 hfs_end_transaction(hfsmp);
1680 error = 0;
1681 goto exit;
1682 }
1683
1684 /*
1685 * Modify the values passed to cat_update based on whether or not
1686 * the file has invalid ranges or borrowed blocks.
1687 */
1688 dataforkp = hfs_prepare_fork_for_update(cp->c_datafork, NULL, &datafork, hfsmp->blockSize);
1689 rsrcforkp = hfs_prepare_fork_for_update(cp->c_rsrcfork, NULL, &rsrcfork, hfsmp->blockSize);
1690
1691 /*
1692 * Lock the Catalog b-tree file.
1693 */
1694 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1695
1696 error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
1697
1698 if (hfsmp->jnl)
1699 cp->c_update_txn = journal_current_txn(hfsmp->jnl);
1700
1701 hfs_systemfile_unlock(hfsmp, lockflags);
1702
1703 CLR(cp->c_flag, C_MODIFIED | C_MINOR_MOD);
1704
1705 hfs_end_transaction(hfsmp);
1706
1707 exit:
1708
1709 return error;
1710 }
1711
1712 /*
1713 * Prepares a fork for cat_update by making sure ff_size and ff_blocks
1714 * are no bigger than the valid data on disk thus reducing the chance
1715 * of exposing uninitialised data in the event of a non clean unmount.
1716 * fork_buf is where to put the temporary copy if required. (It can
1717 * be inside pfork.)
1718 */
1719 const struct cat_fork *
1720 hfs_prepare_fork_for_update(filefork_t *ff, const struct cat_fork *cf, struct cat_fork *cf_buf, uint32_t block_size)
1721 {
1722 if (!ff)
1723 return NULL;
1724
1725 if (!cf)
1726 cf = &ff->ff_data;
1727 if (!cf_buf)
1728 cf_buf = &ff->ff_data;
1729
1730 off_t max_size = ff->ff_size;
1731
1732 if (!ff->ff_unallocblocks && ff->ff_size <= max_size)
1733 return cf; // Nothing to do
1734
1735 if (ff->ff_blocks < ff->ff_unallocblocks) {
1736 LFHFS_LOG(LEVEL_ERROR, "hfs_prepare_fork_for_update: ff_blocks %d is less than unalloc blocks %d\n",
1737 ff->ff_blocks, ff->ff_unallocblocks);
1738 hfs_assert(0);
1739 }
1740
1741 struct cat_fork *out = cf_buf;
1742
1743 if (out != cf)
1744 bcopy(cf, out, sizeof(*cf));
1745
1746 // Adjust cf_blocks for cf_vblocks
1747 out->cf_blocks -= out->cf_vblocks;
1748
1749 /*
1750 * Here we trim the size with the updated cf_blocks. This is
1751 * probably unnecessary now because the invalid ranges should
1752 * catch this (but that wasn't always the case).
1753 */
1754 off_t alloc_bytes = blk_to_bytes(out->cf_blocks, block_size);
1755 if (out->cf_size > alloc_bytes)
1756 out->cf_size = alloc_bytes;
1757
1758 // Trim cf_size to first invalid range
1759 if (out->cf_size > max_size)
1760 out->cf_size = max_size;
1761
1762 return out;
1763 }
1764
1765 /*
1766 * Read contents of a symbolic link.
1767 */
1768 int
1769 hfs_vnop_readlink( struct vnode *vp, void* data, size_t dataSize, size_t *actuallyRead )
1770 {
1771 struct cnode *cp;
1772 struct filefork *fp;
1773 int error;
1774
1775 if (!vnode_islnk(vp))
1776 {
1777 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readlink: Received node is not a symlink\n");
1778 return (EINVAL);
1779 }
1780
1781 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
1782 return (error);
1783 cp = VTOC(vp);
1784 fp = VTOF(vp);
1785
1786 /* Zero length sym links are not allowed */
1787 if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
1788 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readlink: Symlink is with invalid content length\n");
1789 error = EINVAL;
1790 goto exit;
1791 }
1792
1793 if ( dataSize < (size_t)fp->ff_size+1 )
1794 {
1795 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readlink: Received buffer size is too small\n");
1796 error = ENOBUFS;
1797 goto exit;
1798 }
1799
1800 /* Cache the path so we don't waste buffer cache resources */
1801 if (fp->ff_symlinkptr == NULL) {
1802 GenericLFBufPtr bp = NULL;
1803
1804 fp->ff_symlinkptr = hfs_mallocz(fp->ff_size);
1805 if ( fp->ff_symlinkptr == NULL )
1806 {
1807 error = ENOMEM;
1808 goto exit;
1809 }
1810
1811 bp = lf_hfs_generic_buf_allocate( vp, 0, roundup((int)fp->ff_size, VTOHFS(vp)->hfs_physical_block_size), 0);
1812 error = lf_hfs_generic_buf_read(bp);
1813 if (error) {
1814 lf_hfs_generic_buf_release(bp);
1815 if (fp->ff_symlinkptr) {
1816 hfs_free(fp->ff_symlinkptr);
1817 fp->ff_symlinkptr = NULL;
1818 }
1819 goto exit;
1820 }
1821 bcopy(bp->pvData, fp->ff_symlinkptr, (size_t)fp->ff_size);
1822 lf_hfs_generic_buf_release(bp);
1823 }
1824
1825 memcpy(data, fp->ff_symlinkptr, fp->ff_size);
1826 ((uint8_t*)data)[fp->ff_size] = 0;
1827 *actuallyRead = fp->ff_size+1;
1828
1829 exit:
1830 hfs_unlock(cp);
1831 return (error);
1832 }
1833
1834 /*
1835 * Make a directory.
1836 */
1837 int
1838 hfs_vnop_mkdir(vnode_t a_dvp, vnode_t *a_vpp, struct componentname *a_cnp, UVFSFileAttributes* a_vap)
1839 {
1840 int iErr = 0;
1841
1842 /***** HACK ALERT ********/
1843 a_cnp->cn_flags |= MAKEENTRY;
1844 a_vap->fa_type = UVFS_FA_TYPE_DIR;
1845
1846 iErr = hfs_makenode(a_dvp, a_vpp, a_cnp, a_vap);
1847
1848 #if HFS_CRASH_TEST
1849 CRASH_ABORT(CRASH_ABORT_MAKE_DIR, a_dvp->mount, NULL);
1850 #endif
1851
1852 return(iErr);
1853 }
1854
1855 /*
1856 * Create a regular file.
1857 */
1858 int
1859 hfs_vnop_create(vnode_t a_dvp, vnode_t *a_vpp, struct componentname *a_cnp, UVFSFileAttributes* a_vap)
1860 {
1861 a_vap->fa_type = UVFS_FA_TYPE_FILE;
1862 return hfs_makenode(a_dvp, a_vpp, a_cnp, a_vap);
1863 }
1864
1865 /*
1866 * Allocate a new node
1867 */
1868 int
1869 hfs_makenode(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, UVFSFileAttributes *psGivenAttr)
1870 {
1871 struct hfsmount *hfsmp = VTOHFS(dvp);
1872 struct cnode *dcp = NULL;
1873 struct cnode *cp = NULL;
1874 struct vnode *tvp = NULL;
1875 enum vtype vnodetype = UVFSTOV(psGivenAttr->fa_type);
1876 mode_t mode = MAKEIMODE(vnodetype);
1877 struct cat_attr attr = {0};
1878 int lockflags;
1879 int error, started_tr = 0;
1880
1881 int newvnode_flags = 0;
1882 u_int32_t gnv_flags = 0;
1883 int nocache = 0;
1884 struct cat_desc out_desc = {0};
1885 out_desc.cd_flags = 0;
1886 out_desc.cd_nameptr = NULL;
1887
1888 if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
1889 return (error);
1890 dcp = VTOC(dvp);
1891
1892 /* Don't allow creation of new entries in open-unlinked directories */
1893 if (dcp->c_flag & (C_DELETED | C_NOEXISTS))
1894 {
1895 error = ENOENT;
1896 goto exit;
1897 }
1898
1899 if ( !(psGivenAttr->fa_validmask & UVFS_FA_VALID_MODE) && (vnodetype != VDIR) )
1900 {
1901 LFHFS_LOG(LEVEL_ERROR, "hfs_makenode: Invalid mode or type[%#llx, %d]",
1902 (unsigned long long)psGivenAttr->fa_validmask, psGivenAttr->fa_type);
1903 error = EINVAL;
1904 goto exit;
1905 }
1906
1907 if ( ( psGivenAttr->fa_validmask & READ_ONLY_FA_FIELDS ) /*|| ( psGivenAttr->fa_validmask & ~VALID_IN_ATTR_MASK )*/ )
1908 {
1909 LFHFS_LOG(LEVEL_ERROR, "hfs_makenode: Setting readonly fields or invalid mask[%#llx, %#llx]", (unsigned long long)psGivenAttr->fa_validmask, (unsigned long long)READ_ONLY_FA_FIELDS);
1910 error = EINVAL;
1911 goto exit;
1912 }
1913
1914 dcp->c_flag |= C_DIR_MODIFICATION;
1915
1916 *vpp = NULL;
1917
1918 /* Check if were out of usable disk space. */
1919 if (hfs_freeblks(hfsmp, 1) == 0)
1920 {
1921 error = ENOSPC;
1922 goto exit;
1923 }
1924
1925 struct timeval tv;
1926 microtime(&tv);
1927
1928 /* Setup the default attributes */
1929 if ( psGivenAttr->fa_validmask & UVFS_FA_VALID_MODE )
1930 {
1931 mode = (mode & ~ALLPERMS) | (psGivenAttr->fa_mode & ALLPERMS);
1932 }
1933
1934 attr.ca_mode = mode;
1935 attr.ca_linkcount = 1;
1936 attr.ca_itime = tv.tv_sec;
1937 attr.ca_atime = attr.ca_ctime = attr.ca_mtime = attr.ca_itime;
1938 attr.ca_atimeondisk = attr.ca_atime;
1939
1940 /*
1941 * HFS+ only: all files get ThreadExists
1942 */
1943 if (vnodetype == VDIR)
1944 {
1945 if (hfsmp->hfs_flags & HFS_FOLDERCOUNT)
1946 {
1947 attr.ca_recflags = kHFSHasFolderCountMask;
1948 }
1949 }
1950 else
1951 {
1952 attr.ca_recflags = kHFSThreadExistsMask;
1953 }
1954
1955 /*
1956 * Add the date added to the item. See above, as
1957 * all of the dates are set to the itime.
1958 */
1959 hfs_write_dateadded (&attr, attr.ca_atime);
1960
1961 /* Initialize the gen counter to 1 */
1962 hfs_write_gencount(&attr, (uint32_t)1);
1963
1964 if ( psGivenAttr->fa_validmask & UVFS_FA_VALID_UID )
1965 {
1966 attr.ca_uid = psGivenAttr->fa_uid;
1967 }
1968
1969 if ( psGivenAttr->fa_validmask & UVFS_FA_VALID_GID )
1970 {
1971 attr.ca_gid = psGivenAttr->fa_gid;
1972 }
1973
1974 /* Tag symlinks with a type and creator. */
1975 if (vnodetype == VLNK)
1976 {
1977 struct FndrFileInfo *fip;
1978
1979 fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
1980 fip->fdType = SWAP_BE32(kSymLinkFileType);
1981 fip->fdCreator = SWAP_BE32(kSymLinkCreator);
1982 }
1983
1984 /* Setup the descriptor */
1985 struct cat_desc in_desc ={0};
1986 in_desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
1987 in_desc.cd_namelen = cnp->cn_namelen;
1988 in_desc.cd_parentcnid = dcp->c_fileid;
1989 in_desc.cd_flags = S_ISDIR(mode) ? CD_ISDIR : 0;
1990 in_desc.cd_hint = dcp->c_childhint;
1991 in_desc.cd_encoding = 0;
1992
1993 if ((error = hfs_start_transaction(hfsmp)) != 0)
1994 {
1995 goto exit;
1996 }
1997 started_tr = 1;
1998
1999 // have to also lock the attribute file because cat_create() needs
2000 // to check that any fileID it wants to use does not have orphaned
2001 // attributes in it.
2002 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
2003 cnid_t new_id = 0;
2004
2005 /* Reserve some space in the Catalog file. */
2006 error = cat_preflight(hfsmp, CAT_CREATE, NULL);
2007 if (error != 0)
2008 {
2009 hfs_systemfile_unlock(hfsmp, lockflags);
2010 goto exit;
2011 }
2012
2013 error = cat_acquire_cnid(hfsmp, &new_id);
2014 if (error != 0)
2015 {
2016 hfs_systemfile_unlock (hfsmp, lockflags);
2017 goto exit;
2018 }
2019
2020 error = cat_create(hfsmp, new_id, &in_desc, &attr, &out_desc);
2021 if (error == 0) {
2022 /* Update the parent directory */
2023 dcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
2024 dcp->c_entries++;
2025
2026 if (vnodetype == VDIR)
2027 {
2028 INC_FOLDERCOUNT(hfsmp, dcp->c_attr);
2029 }
2030 dcp->c_dirchangecnt++;
2031 hfs_incr_gencount(dcp);
2032
2033 dcp->c_touch_chgtime = dcp->c_touch_modtime = true;
2034 dcp->c_flag |= C_MODIFIED;
2035
2036 hfs_update(dcp->c_vp, 0);
2037 }
2038 hfs_systemfile_unlock(hfsmp, lockflags);
2039 if (error)
2040 goto exit;
2041
2042 uint32_t txn = hfsmp->jnl ? journal_current_txn(hfsmp->jnl) : 0;
2043
2044 hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE, (dcp->c_cnid == kHFSRootFolderID));
2045
2046 // XXXdbg
2047 // have to end the transaction here before we call hfs_getnewvnode()
2048 // because that can cause us to try and reclaim a vnode on a different
2049 // file system which could cause us to start a transaction which can
2050 // deadlock with someone on that other file system (since we could be
2051 // holding two transaction locks as well as various vnodes and we did
2052 // not obtain the locks on them in the proper order).
2053 //
2054 // NOTE: this means that if the quota check fails or we have to update
2055 // the change time on a block-special device that those changes
2056 // will happen as part of independent transactions.
2057 //
2058 if (started_tr)
2059 {
2060 hfs_end_transaction(hfsmp);
2061 started_tr = 0;
2062 }
2063
2064 gnv_flags |= GNV_CREATE;
2065 if (nocache)
2066 {
2067 gnv_flags |= GNV_NOCACHE;
2068 }
2069
2070 /*
2071 * Create a vnode for the object just created.
2072 *
2073 * NOTE: Maintaining the cnode lock on the parent directory is important,
2074 * as it prevents race conditions where other threads want to look up entries
2075 * in the directory and/or add things as we are in the process of creating
2076 * the vnode below. However, this has the potential for causing a
2077 * double lock panic when dealing with shadow files on a HFS boot partition.
2078 * The panic could occur if we are not cleaning up after ourselves properly
2079 * when done with a shadow file or in the error cases. The error would occur if we
2080 * try to create a new vnode, and then end up reclaiming another shadow vnode to
2081 * create the new one. However, if everything is working properly, this should
2082 * be a non-issue as we would never enter that reclaim codepath.
2083 *
2084 * The cnode is locked on successful return.
2085 */
2086 error = hfs_getnewvnode(hfsmp, dvp, cnp, &out_desc, gnv_flags, &attr,
2087 NULL, &tvp, &newvnode_flags);
2088 if (error)
2089 goto exit;
2090
2091 cp = VTOC(tvp);
2092
2093 cp->c_update_txn = txn;
2094
2095 *vpp = tvp;
2096
2097 exit:
2098 cat_releasedesc(&out_desc);
2099
2100 //Update Directory version
2101 dvp->sExtraData.sDirData.uDirVersion++;
2102
2103 /*
2104 * Make sure we release cnode lock on dcp.
2105 */
2106 if (dcp)
2107 {
2108 dcp->c_flag &= ~C_DIR_MODIFICATION;
2109
2110 //TBD - We have wakeup here but can't see anyone who's msleeping on c_flag...
2111 //wakeup((caddr_t)&dcp->c_flag);
2112 hfs_unlock(dcp);
2113 }
2114
2115 if (cp != NULL) {
2116 hfs_unlock(cp);
2117 }
2118 if (started_tr) {
2119 hfs_end_transaction(hfsmp);
2120 }
2121
2122 return (error);
2123 }
2124
2125 /*
2126 * Create a symbolic link.
2127 */
2128 int
2129 hfs_vnop_symlink(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, char* symlink_content, UVFSFileAttributes *attrp)
2130 {
2131 struct vnode *vp = NULL;
2132 struct cnode *cp = NULL;
2133 struct hfsmount *hfsmp;
2134 struct filefork *fp;
2135 GenericLFBufPtr bp = NULL;
2136 char *datap;
2137 int started_tr = 0;
2138 uint64_t len;
2139 int error;
2140
2141 hfsmp = VTOHFS(dvp);
2142
2143 len = strlen(symlink_content);
2144 if (len > MAXPATHLEN)
2145 {
2146 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_symlink: Received symlink content too long\n");
2147 return (ENAMETOOLONG);
2148 }
2149
2150 if (len == 0 )
2151 {
2152 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_symlink: Received zero length symlink content\n");
2153 return (EINVAL);
2154 }
2155
2156 /* Check for free space */
2157 if (((u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize) < len) {
2158 return (ENOSPC);
2159 }
2160
2161 attrp->fa_type = UVFS_FA_TYPE_SYMLINK;
2162 attrp->fa_mode |= S_IFLNK;
2163 attrp->fa_validmask |= UVFS_FA_VALID_MODE;
2164
2165 /* Create the vnode */
2166 if ((error = hfs_makenode(dvp, vpp, cnp, attrp))) {
2167 goto out;
2168 }
2169 vp = *vpp;
2170 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
2171 goto out;
2172 }
2173 cp = VTOC(vp);
2174 fp = VTOF(vp);
2175
2176 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
2177 goto out;
2178 }
2179
2180 #if QUOTA
2181 (void)hfs_getinoquota(cp);
2182 #endif /* QUOTA */
2183
2184 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2185 goto out;
2186 }
2187 started_tr = 1;
2188
2189 /*
2190 * Allocate space for the link.
2191 *
2192 * Since we're already inside a transaction,
2193 *
2194 * Don't need truncate lock since a symlink is treated as a system file.
2195 */
2196 error = hfs_truncate(vp, len, IO_NOZEROFILL, 0);
2197
2198 /* On errors, remove the symlink file */
2199 if (error) {
2200 /*
2201 * End the transaction so we don't re-take the cnode lock
2202 * below while inside a transaction (lock order violation).
2203 */
2204 hfs_end_transaction(hfsmp);
2205 /* hfs_removefile() requires holding the truncate lock */
2206 hfs_unlock(cp);
2207 hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
2208 hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2209
2210 if (hfs_start_transaction(hfsmp) != 0) {
2211 started_tr = 0;
2212 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
2213 goto out;
2214 }
2215
2216 (void) hfs_removefile(dvp, vp, cnp, 0, 0, 0, 0);
2217 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
2218 goto out;
2219 }
2220
2221 /* Write the sym-link to disk */
2222 bp = lf_hfs_generic_buf_allocate( vp, 0, roundup((int)fp->ff_size, hfsmp->hfs_physical_block_size), 0);
2223 error = lf_hfs_generic_buf_read( bp );
2224 if ( error != 0 )
2225 {
2226 goto out;
2227 }
2228
2229 if (hfsmp->jnl)
2230 {
2231 journal_modify_block_start(hfsmp->jnl, bp);
2232 }
2233 datap = bp->pvData;
2234 assert(bp->uDataSize >= len);
2235 bzero(datap, bp->uDataSize);
2236 bcopy(symlink_content, datap, len);
2237 if (hfsmp->jnl)
2238 {
2239 journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
2240 bp = NULL; // block will be released by the journal
2241 }
2242 else
2243 {
2244 error = lf_hfs_generic_buf_write(bp);
2245 if ( error != 0 )
2246 {
2247 goto out;
2248 }
2249 }
2250 out:
2251 if (started_tr)
2252 hfs_end_transaction(hfsmp);
2253
2254 if ((cp != NULL) && (vp != NULL)) {
2255 hfs_unlock(cp);
2256 }
2257 if (error) {
2258 if (vp) {
2259 // vnode_put(vp);
2260 }
2261 *vpp = NULL;
2262 }
2263
2264 if ( bp ) {
2265 lf_hfs_generic_buf_release(bp);
2266 }
2267
2268 hfs_flush(hfsmp, HFS_FLUSH_FULL);
2269
2270 return (error);
2271 }
2272
2273 /*
2274 * Rename a cnode.
2275 *
2276 * The VFS layer guarantees that:
2277 * - source and destination will either both be directories, or
2278 * both not be directories.
2279 * - all the vnodes are from the same file system
2280 *
2281 * When the target is a directory, HFS must ensure that its empty.
2282 *
2283 * Note that this function requires up to 6 vnodes in order to work properly
2284 * if it is operating on files (and not on directories). This is because only
2285 * files can have resource forks, and we now require iocounts to be held on the
2286 * vnodes corresponding to the resource forks (if applicable) as well as
2287 * the files or directories undergoing rename. The problem with not holding
2288 * iocounts on the resource fork vnodes is that it can lead to a deadlock
2289 * situation: The rsrc fork of the source file may be recycled and reclaimed
2290 * in order to provide a vnode for the destination file's rsrc fork. Since
2291 * data and rsrc forks share the same cnode, we'd eventually try to lock the
2292 * source file's cnode in order to sync its rsrc fork to disk, but it's already
2293 * been locked. By taking the rsrc fork vnodes up front we ensure that they
2294 * cannot be recycled, and that the situation mentioned above cannot happen.
2295 */
2296 int
2297 hfs_vnop_renamex(struct vnode *fdvp,struct vnode *fvp, struct componentname *fcnp, struct vnode *tdvp, struct vnode *tvp, struct componentname *tcnp)
2298 {
2299
2300 /*
2301 * Note that we only need locals for the target/destination's
2302 * resource fork vnode (and only if necessary). We don't care if the
2303 * source has a resource fork vnode or not.
2304 */
2305 struct vnode *tvp_rsrc = NULL;
2306 struct cnode *tcp = NULL;
2307 struct cnode *error_cnode;
2308 struct cat_desc from_desc;
2309
2310 struct hfsmount *hfsmp = VTOHFS(tdvp);
2311 int tvp_deleted = 0;
2312 int started_tr = 0, got_cookie = 0;
2313 int took_trunc_lock = 0;
2314 int lockflags;
2315 int error;
2316
2317 int rename_exclusive = 0;
2318
2319 retry:
2320 /* When tvp exists, take the truncate lock for hfs_removefile(). */
2321 if (tvp && (vnode_isreg(tvp) || vnode_islnk(tvp))) {
2322 hfs_lock_truncate(VTOC(tvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
2323 took_trunc_lock = 1;
2324 }
2325
2326 if (tvp && VTOC(tvp) == NULL)
2327 return (EINVAL);
2328
2329 error = hfs_lockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL, HFS_EXCLUSIVE_LOCK, &error_cnode);
2330 if (error)
2331 {
2332 if (took_trunc_lock)
2333 {
2334 hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
2335 took_trunc_lock = 0;
2336 }
2337
2338 /*
2339 * We hit an error path. If we were trying to re-acquire the locks
2340 * after coming through here once, we might have already obtained
2341 * an iocount on tvp's resource fork vnode. Drop that before dealing
2342 * with the failure. Note this is safe -- since we are in an
2343 * error handling path, we can't be holding the cnode locks.
2344 */
2345 if (tvp_rsrc && tcp)
2346 {
2347 hfs_chash_lower_OpenLookupCounter(tcp);
2348 tvp_rsrc = NULL;
2349 }
2350
2351 /*
2352 * tvp might no longer exist. If the cause of the lock failure
2353 * was tvp, then we can try again with tvp/tcp set to NULL.
2354 * This is ok because the vfs syscall will vnode_put the vnodes
2355 * after we return from hfs_vnop_rename.
2356 */
2357 if ((error == ENOENT) && (tvp != NULL) && (error_cnode == VTOC(tvp))) {
2358 tcp = NULL;
2359 tvp = NULL;
2360 goto retry;
2361 }
2362
2363 /* If we want to reintroduce notifications for failed renames, this
2364 is the place to do it. */
2365
2366 return (error);
2367 }
2368
2369 struct cnode* fdcp = VTOC(fdvp);
2370 struct cnode* fcp = VTOC(fvp);
2371 struct cnode* tdcp = VTOC(tdvp);
2372 tcp = tvp ? VTOC(tvp) : NULL;
2373
2374 /*
2375 * If caller requested an exclusive rename (VFS_RENAME_EXCL) and 'tcp' exists
2376 * then we must fail the operation.
2377 */
2378 if (tcp && rename_exclusive)
2379 {
2380 error = EEXIST;
2381 goto out;
2382 }
2383
2384 /*
2385 * Acquire iocounts on the destination's resource fork vnode
2386 * if necessary. If dst/src are files and the dst has a resource
2387 * fork vnode, then we need to try and acquire an iocount on the rsrc vnode.
2388 * If it does not exist, then we don't care and can skip it.
2389 */
2390 if ((vnode_isreg(fvp)) || (vnode_islnk(fvp)))
2391 {
2392 if ((tvp) && (tcp->c_rsrc_vp) && (tvp_rsrc == NULL))
2393 {
2394 tvp_rsrc = tcp->c_rsrc_vp;
2395 hfs_chash_raise_OpenLookupCounter(tcp);
2396
2397 /* Unlock everything to acquire iocount on this rsrc vnode */
2398 if (took_trunc_lock)
2399 {
2400 hfs_unlock_truncate (VTOC(tvp), HFS_LOCK_DEFAULT);
2401 took_trunc_lock = 0;
2402 }
2403
2404 hfs_unlockfour(fdcp, fcp, tdcp, tcp);
2405
2406 goto retry;
2407 }
2408 }
2409
2410 /* Ensure we didn't race src or dst parent directories with rmdir. */
2411 if (fdcp->c_flag & (C_NOEXISTS | C_DELETED))
2412 {
2413 error = ENOENT;
2414 goto out;
2415 }
2416
2417 if (tdcp->c_flag & (C_NOEXISTS | C_DELETED))
2418 {
2419 error = ENOENT;
2420 goto out;
2421 }
2422
2423
2424 /* Check for a race against unlink. The hfs_valid_cnode checks validate
2425 * the parent/child relationship with fdcp and tdcp, as well as the
2426 * component name of the target cnodes.
2427 */
2428 if ((fcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, fdvp, fcnp, fcp->c_fileid, NULL, &error))
2429 {
2430 error = ENOENT;
2431 goto out;
2432 }
2433
2434 if (tcp && ((tcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, tdvp, tcnp, tcp->c_fileid, NULL, &error)))
2435 {
2436 //
2437 // hmm, the destination vnode isn't valid any more.
2438 // in this case we can just drop him and pretend he
2439 // never existed in the first place.
2440 //
2441 if (took_trunc_lock)
2442 {
2443 hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
2444 took_trunc_lock = 0;
2445 }
2446 error = 0;
2447
2448 hfs_unlockfour(fdcp, fcp, tdcp, tcp);
2449
2450 tcp = NULL;
2451 tvp = NULL;
2452
2453 // retry the locking with tvp null'ed out
2454 goto retry;
2455 }
2456
2457 fdcp->c_flag |= C_DIR_MODIFICATION;
2458 if (fdvp != tdvp)
2459 {
2460 tdcp->c_flag |= C_DIR_MODIFICATION;
2461 }
2462
2463 /*
2464 * Disallow renaming of a directory hard link if the source and
2465 * destination parent directories are different, or a directory whose
2466 * descendant is a directory hard link and the one of the ancestors
2467 * of the destination directory is a directory hard link.
2468 */
2469 if (vnode_isdir(fvp) && (fdvp != tdvp))
2470 {
2471 if (fcp->c_flag & C_HARDLINK) {
2472 error = EPERM;
2473 goto out;
2474 }
2475 if (fcp->c_attr.ca_recflags & kHFSHasChildLinkMask)
2476 {
2477 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2478 if (cat_check_link_ancestry(hfsmp, tdcp->c_fileid, 0))
2479 {
2480 error = EPERM;
2481 hfs_systemfile_unlock(hfsmp, lockflags);
2482 goto out;
2483 }
2484 hfs_systemfile_unlock(hfsmp, lockflags);
2485 }
2486 }
2487
2488 /*
2489 * The following edge case is caught here:
2490 * (to cannot be a descendent of from)
2491 *
2492 * o fdvp
2493 * /
2494 * /
2495 * o fvp
2496 * \
2497 * \
2498 * o tdvp
2499 * /
2500 * /
2501 * o tvp
2502 */
2503 if (tdcp->c_parentcnid == fcp->c_fileid)
2504 {
2505 error = EINVAL;
2506 goto out;
2507 }
2508
2509 /*
2510 * The following two edge cases are caught here:
2511 * (note tvp is not empty)
2512 *
2513 * o tdvp o tdvp
2514 * / /
2515 * / /
2516 * o tvp tvp o fdvp
2517 * \ \
2518 * \ \
2519 * o fdvp o fvp
2520 * /
2521 * /
2522 * o fvp
2523 */
2524 if (tvp && vnode_isdir(tvp) && (tcp->c_entries != 0) && fvp != tvp)
2525 {
2526 error = ENOTEMPTY;
2527 goto out;
2528 }
2529
2530 /*
2531 * The following edge case is caught here:
2532 * (the from child and parent are the same)
2533 *
2534 * o tdvp
2535 * /
2536 * /
2537 * fdvp o fvp
2538 */
2539 if (fdvp == fvp)
2540 {
2541 error = EINVAL;
2542 goto out;
2543 }
2544
2545 /*
2546 * Make sure "from" vnode and its parent are changeable.
2547 */
2548 if ((fcp->c_bsdflags & (SF_IMMUTABLE | UF_IMMUTABLE | UF_APPEND | SF_APPEND)) || (fdcp->c_bsdflags & (UF_APPEND | SF_APPEND)))
2549 {
2550 error = EPERM;
2551 goto out;
2552 }
2553
2554 /* Don't allow modification of the journal or journal_info_block */
2555 if (hfs_is_journal_file(hfsmp, fcp) || (tcp && hfs_is_journal_file(hfsmp, tcp)))
2556 {
2557 error = EPERM;
2558 goto out;
2559 }
2560
2561 struct cat_desc out_desc = {0};
2562 from_desc.cd_nameptr = (const u_int8_t *)fcnp->cn_nameptr;
2563 from_desc.cd_namelen = fcnp->cn_namelen;
2564 from_desc.cd_parentcnid = fdcp->c_fileid;
2565 from_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
2566 from_desc.cd_cnid = fcp->c_cnid;
2567
2568 struct cat_desc to_desc = {0};
2569 to_desc.cd_nameptr = (const u_int8_t *)tcnp->cn_nameptr;
2570 to_desc.cd_namelen = tcnp->cn_namelen;
2571 to_desc.cd_parentcnid = tdcp->c_fileid;
2572 to_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
2573 to_desc.cd_cnid = fcp->c_cnid;
2574
2575 if ((error = hfs_start_transaction(hfsmp)) != 0)
2576 {
2577 goto out;
2578 }
2579 started_tr = 1;
2580
2581 /* hfs_vnop_link() and hfs_vnop_rename() set kHFSHasChildLinkMask
2582 * inside a journal transaction and without holding a cnode lock.
2583 * As setting of this bit depends on being in journal transaction for
2584 * concurrency, check this bit again after we start journal transaction for rename
2585 * to ensure that this directory does not have any descendant that
2586 * is a directory hard link.
2587 */
2588 if (vnode_isdir(fvp) && (fdvp != tdvp))
2589 {
2590 if (fcp->c_attr.ca_recflags & kHFSHasChildLinkMask)
2591 {
2592 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2593 if (cat_check_link_ancestry(hfsmp, tdcp->c_fileid, 0)) {
2594 error = EPERM;
2595 hfs_systemfile_unlock(hfsmp, lockflags);
2596 goto out;
2597 }
2598 hfs_systemfile_unlock(hfsmp, lockflags);
2599 }
2600 }
2601
2602 // if it's a hardlink then re-lookup the name so
2603 // that we get the correct cnid in from_desc (see
2604 // the comment in hfs_removefile for more details)
2605 if (fcp->c_flag & C_HARDLINK)
2606 {
2607 struct cat_desc tmpdesc;
2608 cnid_t real_cnid;
2609
2610 tmpdesc.cd_nameptr = (const u_int8_t *)fcnp->cn_nameptr;
2611 tmpdesc.cd_namelen = fcnp->cn_namelen;
2612 tmpdesc.cd_parentcnid = fdcp->c_fileid;
2613 tmpdesc.cd_hint = fdcp->c_childhint;
2614 tmpdesc.cd_flags = fcp->c_desc.cd_flags & CD_ISDIR;
2615 tmpdesc.cd_encoding = 0;
2616
2617 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2618
2619 if (cat_lookup(hfsmp, &tmpdesc, 0, NULL, NULL, NULL, &real_cnid) != 0)
2620 {
2621 hfs_systemfile_unlock(hfsmp, lockflags);
2622 goto out;
2623 }
2624
2625 // use the real cnid instead of whatever happened to be there
2626 from_desc.cd_cnid = real_cnid;
2627 hfs_systemfile_unlock(hfsmp, lockflags);
2628 }
2629
2630 /*
2631 * Reserve some space in the Catalog file.
2632 */
2633 cat_cookie_t cookie;
2634 if ((error = cat_preflight(hfsmp, CAT_RENAME + CAT_DELETE, &cookie)))
2635 {
2636 goto out;
2637 }
2638 got_cookie = 1;
2639
2640 /*
2641 * If the destination exists then it may need to be removed.
2642 *
2643 * Due to HFS's locking system, we should always move the
2644 * existing 'tvp' element to the hidden directory in hfs_vnop_rename.
2645 * Because the VNOP_LOOKUP call enters and exits the filesystem independently
2646 * of the actual vnop that it was trying to do (stat, link, readlink),
2647 * we must release the cnode lock of that element during the interim to
2648 * do MAC checking, vnode authorization, and other calls. In that time,
2649 * the item can be deleted (or renamed over). However, only in the rename
2650 * case is it inappropriate to return ENOENT from any of those calls. Either
2651 * the call should return information about the old element (stale), or get
2652 * information about the newer element that we are about to write in its place.
2653 *
2654 * HFS lookup has been modified to detect a rename and re-drive its
2655 * lookup internally. For other calls that have already succeeded in
2656 * their lookup call and are waiting to acquire the cnode lock in order
2657 * to proceed, that cnode lock will not fail due to the cnode being marked
2658 * C_NOEXISTS, because it won't have been marked as such. It will only
2659 * have C_DELETED. Thus, they will simply act on the stale open-unlinked
2660 * element. All future callers will get the new element.
2661 *
2662 * To implement this behavior, we pass the "only_unlink" argument to
2663 * hfs_removefile and hfs_removedir. This will result in the vnode acting
2664 * as though it is open-unlinked. Additionally, when we are done moving the
2665 * element to the hidden directory, we vnode_recycle the target so that it is
2666 * reclaimed as soon as possible. Reclaim and inactive are both
2667 * capable of clearing out unused blocks for an open-unlinked file or dir.
2668 */
2669 if (tvp)
2670 {
2671 /*
2672 * When fvp matches tvp they could be case variants
2673 * or matching hard links.
2674 */
2675 if (fvp == tvp)
2676 {
2677 if (!(fcp->c_flag & C_HARDLINK))
2678 {
2679 /*
2680 * If they're not hardlinks, then fvp == tvp must mean we
2681 * are using case-insensitive HFS because case-sensitive would
2682 * not use the same vnode for both. In this case we just update
2683 * the catalog for: a -> A
2684 */
2685 goto skip_rm; /* simple case variant */
2686
2687 }
2688 /* For all cases below, we must be using hardlinks */
2689 else if ((fdvp != tdvp) || (hfsmp->hfs_flags & HFS_CASE_SENSITIVE))
2690 {
2691 /*
2692 * If the parent directories are not the same, AND the two items
2693 * are hardlinks, posix says to do nothing:
2694 * dir1/fred <-> dir2/bob and the op was mv dir1/fred -> dir2/bob
2695 * We just return 0 in this case.
2696 *
2697 * If case sensitivity is on, and we are using hardlinks
2698 * then renaming is supposed to do nothing.
2699 * dir1/fred <-> dir2/FRED, and op == mv dir1/fred -> dir2/FRED
2700 */
2701 goto out;
2702
2703 }
2704 else if (hfs_namecmp((const u_int8_t *)fcnp->cn_nameptr, fcnp->cn_namelen, (const u_int8_t *)tcnp->cn_nameptr, tcnp->cn_namelen) == 0)
2705 {
2706 /*
2707 * If we get here, then the following must be true:
2708 * a) We are running case-insensitive HFS+.
2709 * b) Both paths 'fvp' and 'tvp' are in the same parent directory.
2710 * c) the two names are case-variants of each other.
2711 *
2712 * In this case, we are really only dealing with a single catalog record
2713 * whose name is being updated.
2714 *
2715 * op is dir1/fred -> dir1/FRED
2716 *
2717 * We need to special case the name matching, because if
2718 * dir1/fred <-> dir1/bob were the two links, and the
2719 * op was dir1/fred -> dir1/bob
2720 * That would fail/do nothing.
2721 */
2722 goto skip_rm; /* case-variant hardlink in the same dir */
2723 }
2724 else
2725 {
2726 goto out; /* matching hardlink, nothing to do */
2727 }
2728 }
2729
2730
2731 if (vnode_isdir(tvp))
2732 {
2733 /*
2734 * hfs_removedir will eventually call hfs_removefile on the directory
2735 * we're working on, because only hfs_removefile does the renaming of the
2736 * item to the hidden directory. The directory will stay around in the
2737 * hidden directory with C_DELETED until it gets an inactive or a reclaim.
2738 * That way, we can destroy all of the EAs as needed and allow new ones to be
2739 * written.
2740 */
2741 error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_SKIP_RESERVE, 0);
2742 }
2743 else
2744 {
2745 error = hfs_removefile(tdvp, tvp, tcnp, 0, HFSRM_SKIP_RESERVE, 0, 0);
2746
2747 /*
2748 * If the destination file had a resource fork vnode, then we need to get rid of
2749 * its blocks when there are no more references to it. Because the call to
2750 * hfs_removefile above always open-unlinks things, we need to force an inactive/reclaim
2751 * on the resource fork vnode, in order to prevent block leaks. Otherwise,
2752 * the resource fork vnode could prevent the data fork vnode from going out of scope
2753 * because it holds a v_parent reference on it. So we mark it for termination
2754 * with a call to vnode_recycle. hfs_vnop_reclaim has been modified so that it
2755 * can clean up the blocks of open-unlinked files and resource forks.
2756 *
2757 * We can safely call vnode_recycle on the resource fork because we took an iocount
2758 * reference on it at the beginning of the function.
2759 */
2760
2761 if ((error == 0) && (tcp->c_flag & C_DELETED) && (tvp_rsrc))
2762 {
2763 hfs_chash_lower_OpenLookupCounter(tcp);
2764 tvp_rsrc = NULL;
2765 }
2766 }
2767
2768 if (error)
2769 {
2770 goto out;
2771 }
2772
2773 tvp_deleted = 1;
2774
2775 if ( ((VTOC(tvp)->c_flag & C_HARDLINK) == 0 ) || (VTOC(tvp)->c_linkcount == 0) )
2776 {
2777 INVALIDATE_NODE(tvp);
2778 }
2779
2780 /* Mark 'tcp' as being deleted due to a rename */
2781 tcp->c_flag |= C_RENAMED;
2782
2783 /*
2784 * Aggressively mark tvp/tcp for termination to ensure that we recover all blocks
2785 * as quickly as possible.
2786 */
2787 //TBD -- Need to see what we are doing with recycle
2788 // vnode_recycle(tvp);
2789 }
2790
2791 skip_rm:
2792 /*
2793 * All done with tvp and fvp.
2794 *
2795 * We also jump to this point if there was no destination observed during lookup and namei.
2796 * However, because only iocounts are held at the VFS layer, there is nothing preventing a
2797 * competing thread from racing us and creating a file or dir at the destination of this rename
2798 * operation. If this occurs, it may cause us to get a spurious EEXIST out of the cat_rename
2799 * call below. To preserve rename's atomicity, we need to signal VFS to re-drive the
2800 * namei/lookup and restart the rename operation. EEXIST is an allowable errno to be bubbled
2801 * out of the rename syscall, but not for this reason, since it is a synonym errno for ENOTEMPTY.
2802 * To signal VFS, we return ERECYCLE (which is also used for lookup restarts). This errno
2803 * will be swallowed and it will restart the operation.
2804 */
2805
2806 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
2807 error = cat_rename(hfsmp, &from_desc, &tdcp->c_desc, &to_desc, &out_desc);
2808 hfs_systemfile_unlock(hfsmp, lockflags);
2809
2810 if (error)
2811 {
2812 if (error == EEXIST)
2813 {
2814 error = ERECYCLE;
2815 }
2816 goto out;
2817 }
2818
2819 /* Update cnode's catalog descriptor */
2820 replace_desc(fcp, &out_desc);
2821 fcp->c_parentcnid = tdcp->c_fileid;
2822 fcp->c_hint = 0;
2823
2824 /*
2825 * Now indicate this cnode needs to have date-added written to the
2826 * finderinfo, but only if moving to a different directory, or if
2827 * it doesn't already have it.
2828 */
2829 if (fdvp != tdvp || !ISSET(fcp->c_attr.ca_recflags, kHFSHasDateAddedMask))
2830 fcp->c_flag |= C_NEEDS_DATEADDED;
2831
2832 (void) hfs_update (fvp, 0);
2833
2834 hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_RMDIR : VOL_RMFILE, (fdcp->c_cnid == kHFSRootFolderID));
2835 hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_MKDIR : VOL_MKFILE, (tdcp->c_cnid == kHFSRootFolderID));
2836
2837 /* Update both parent directories. */
2838 if (fdvp != tdvp)
2839 {
2840 if (vnode_isdir(fvp))
2841 {
2842 /* If the source directory has directory hard link
2843 * descendants, set the kHFSHasChildLinkBit in the
2844 * destination parent hierarchy
2845 */
2846 if ((fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) && !(tdcp->c_attr.ca_recflags & kHFSHasChildLinkMask))
2847 {
2848
2849 tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
2850
2851 error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid);
2852 if (error)
2853 {
2854 LFHFS_LOG(LEVEL_DEBUG, "hfs_vnop_rename: error updating parent chain for %u\n", tdcp->c_cnid);
2855 error = 0;
2856 }
2857 }
2858 INC_FOLDERCOUNT(hfsmp, tdcp->c_attr);
2859 DEC_FOLDERCOUNT(hfsmp, fdcp->c_attr);
2860 }
2861 tdcp->c_entries++;
2862 tdcp->c_dirchangecnt++;
2863 tdcp->c_flag |= C_MODIFIED;
2864 hfs_incr_gencount(tdcp);
2865
2866 if (fdcp->c_entries > 0)
2867 fdcp->c_entries--;
2868 fdcp->c_dirchangecnt++;
2869 fdcp->c_flag |= C_MODIFIED;
2870 fdcp->c_touch_chgtime = TRUE;
2871 fdcp->c_touch_modtime = TRUE;
2872
2873 if (ISSET(fcp->c_flag, C_HARDLINK))
2874 {
2875 hfs_relorigin(fcp, fdcp->c_fileid);
2876 if (fdcp->c_fileid != fdcp->c_cnid)
2877 hfs_relorigin(fcp, fdcp->c_cnid);
2878 }
2879
2880 (void) hfs_update(fdvp, 0);
2881 }
2882 hfs_incr_gencount(fdcp);
2883
2884 tdcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
2885 tdcp->c_touch_chgtime = TRUE;
2886 tdcp->c_touch_modtime = TRUE;
2887
2888 (void) hfs_update(tdvp, 0);
2889
2890 /* Update the vnode's name now that the rename has completed. */
2891 vnode_update_identity(fvp, tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_hash, (VNODE_UPDATE_PARENT | VNODE_UPDATE_NAME));
2892
2893 /*
2894 * At this point, we may have a resource fork vnode attached to the
2895 * 'from' vnode. If it exists, we will want to update its name, because
2896 * it contains the old name + _PATH_RSRCFORKSPEC. ("/..namedfork/rsrc").
2897 *
2898 * Note that the only thing we need to update here is the name attached to
2899 * the vnode, since a resource fork vnode does not have a separate resource
2900 * cnode -- it's still 'fcp'.
2901 */
2902 if (fcp->c_rsrc_vp)
2903 {
2904 char* rsrc_path = NULL;
2905 int len;
2906
2907 /* Create a new temporary buffer that's going to hold the new name */
2908 rsrc_path = hfs_malloc(MAXPATHLEN);
2909 len = snprintf (rsrc_path, MAXPATHLEN, "%s%s", tcnp->cn_nameptr, _PATH_RSRCFORKSPEC);
2910 len = MIN(len, MAXPATHLEN);
2911
2912 /*
2913 * vnode_update_identity will do the following for us:
2914 * 1) release reference on the existing rsrc vnode's name.
2915 * 2) attach the new name to the resource vnode
2916 * 3) update the vnode's vid
2917 */
2918 vnode_update_identity (fcp->c_rsrc_vp, fvp, rsrc_path, len, 0, (VNODE_UPDATE_NAME | VNODE_UPDATE_CACHE));
2919
2920 /* Free the memory associated with the resource fork's name */
2921 hfs_free(rsrc_path);
2922 }
2923 out:
2924 if (got_cookie)
2925 {
2926 cat_postflight(hfsmp, &cookie);
2927 }
2928 if (started_tr)
2929 {
2930 hfs_end_transaction(hfsmp);
2931 }
2932
2933 fdvp->sExtraData.sDirData.uDirVersion++;
2934 fdcp->c_flag &= ~C_DIR_MODIFICATION;
2935 //TBD - We have wakeup here but can't see anyone who's msleeping on c_flag...
2936 // wakeup((caddr_t)&fdcp->c_flag);
2937
2938 if (fdvp != tdvp)
2939 {
2940 tdvp->sExtraData.sDirData.uDirVersion++;
2941 tdcp->c_flag &= ~C_DIR_MODIFICATION;
2942 //TBD - We have wakeup here but can't see anyone who's msleeping on c_flag...
2943 // wakeup((caddr_t)&tdcp->c_flag);
2944
2945 }
2946
2947 /* Now vnode_put the resource forks vnodes if necessary */
2948 if (tvp_rsrc)
2949 {
2950 hfs_chash_lower_OpenLookupCounter(tcp);
2951 tvp_rsrc = NULL;
2952 }
2953
2954 hfs_unlockfour(fdcp, fcp, tdcp, tcp);
2955
2956 if (took_trunc_lock)
2957 {
2958 hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
2959 }
2960
2961 /* After tvp is removed the only acceptable error is EIO */
2962 if (error && tvp_deleted)
2963 error = EIO;
2964
2965 return (error);
2966 }
2967
2968 /*
2969 * link vnode operation
2970 *
2971 * IN vnode_t a_vp;
2972 * IN vnode_t a_tdvp;
2973 * IN struct componentname *a_cnp;
2974 * IN vfs_context_t a_context;
2975 */
2976 int
2977 hfs_vnop_link(vnode_t vp, vnode_t tdvp, struct componentname *cnp)
2978 {
2979 struct hfsmount *hfsmp = VTOHFS(vp);;
2980 struct cnode *cp = VTOC(vp);;
2981 struct cnode *tdcp;
2982 struct cnode *fdcp = NULL;
2983 struct cat_desc todesc;
2984 cnid_t parentcnid;
2985 int lockflags = 0;
2986 int intrans = 0;
2987 enum vtype v_type = vp->sFSParams.vnfs_vtype;
2988 int error, ret;
2989
2990 /*
2991 * For now, return ENOTSUP for a symlink target. This can happen
2992 * for linkat(2) when called without AT_SYMLINK_FOLLOW.
2993 */
2994 if (v_type == VLNK || v_type == VDIR)
2995 return (EPERM );
2996
2997 /* Make sure our private directory exists. */
2998 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0) {
2999 return (ENOTSUP);
3000 }
3001
3002 if (hfs_freeblks(hfsmp, 0) == 0) {
3003 return (ENOSPC);
3004 }
3005
3006 /* Lock the cnodes. */
3007 if ((error = hfs_lockpair(VTOC(tdvp), VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
3008 return (error);
3009 }
3010
3011 tdcp = VTOC(tdvp);
3012 /* grab the parent CNID from originlist after grabbing cnode locks */
3013 parentcnid = hfs_currentparent(cp, /* have_lock: */ true);
3014
3015 if (tdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
3016 error = ENOENT;
3017 goto out;
3018 }
3019
3020 /* Check the source for errors:
3021 * too many links, immutable, race with unlink
3022 */
3023 if (cp->c_linkcount >= HFS_LINK_MAX) {
3024 error = EMLINK;
3025 goto out;
3026 }
3027 if (cp->c_bsdflags & (UF_IMMUTABLE | SF_IMMUTABLE | UF_APPEND | SF_APPEND)) {
3028 error = EPERM;
3029 goto out;
3030 }
3031 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
3032 error = ENOENT;
3033 goto out;
3034 }
3035
3036 tdcp->c_flag |= C_DIR_MODIFICATION;
3037
3038 if (hfs_start_transaction(hfsmp) != 0) {
3039 error = EINVAL;
3040 goto out;
3041 }
3042 intrans = 1;
3043
3044 todesc.cd_flags = (v_type == VDIR) ? CD_ISDIR : 0;
3045 todesc.cd_encoding = 0;
3046 todesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
3047 todesc.cd_namelen = cnp->cn_namelen;
3048 todesc.cd_parentcnid = tdcp->c_fileid;
3049 todesc.cd_hint = 0;
3050 todesc.cd_cnid = 0;
3051
3052 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
3053
3054 /* If destination exists then we lost a race with create. */
3055 if (cat_lookup(hfsmp, &todesc, 0, NULL, NULL, NULL, NULL) == 0) {
3056 error = EEXIST;
3057 goto out;
3058 }
3059 if (cp->c_flag & C_HARDLINK) {
3060 struct cat_attr cattr;
3061
3062 /* If inode is missing then we lost a race with unlink. */
3063 if ((cat_idlookup(hfsmp, cp->c_fileid, 0, 0, NULL, &cattr, NULL) != 0) ||
3064 (cattr.ca_fileid != cp->c_fileid)) {
3065 error = ENOENT;
3066 goto out;
3067 }
3068 } else {
3069 cnid_t fileid;
3070
3071 /* If source is missing then we lost a race with unlink. */
3072 if ((cat_lookup(hfsmp, &cp->c_desc, 0, NULL, NULL, NULL, &fileid) != 0) ||
3073 (fileid != cp->c_fileid)) {
3074 error = ENOENT;
3075 goto out;
3076 }
3077 }
3078 /*
3079 * All directory links must reside in an non-ARCHIVED hierarchy.
3080 */
3081 if (v_type == VDIR) {
3082 /*
3083 * - Source parent and destination parent cannot match
3084 * - A link is not permitted in the root directory
3085 * - Parent of 'pointed at' directory is not the root directory
3086 * - The 'pointed at' directory (source) is not an ancestor
3087 * of the new directory hard link (destination).
3088 * - No ancestor of the new directory hard link (destination)
3089 * is a directory hard link.
3090 */
3091 if ((parentcnid == tdcp->c_fileid) ||
3092 (tdcp->c_fileid == kHFSRootFolderID) ||
3093 (parentcnid == kHFSRootFolderID) ||
3094 cat_check_link_ancestry(hfsmp, tdcp->c_fileid, cp->c_fileid)) {
3095 error = EPERM; /* abide by the rules, you did not */
3096 goto out;
3097 }
3098 }
3099 hfs_systemfile_unlock(hfsmp, lockflags);
3100 lockflags = 0;
3101
3102 cp->c_linkcount++;
3103 cp->c_flag |= C_MODIFIED;
3104 cp->c_touch_chgtime = TRUE;
3105 error = hfs_makelink(hfsmp, vp, cp, tdcp, cnp);
3106 if (error) {
3107 cp->c_linkcount--;
3108 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
3109 } else {
3110 /* Update the target directory and volume stats */
3111 tdcp->c_entries++;
3112 if (v_type == VDIR) {
3113 INC_FOLDERCOUNT(hfsmp, tdcp->c_attr);
3114 tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
3115
3116 /* Set kHFSHasChildLinkBit in the destination hierarchy */
3117 error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid);
3118 if (error) {
3119 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_link: error updating destination parent chain for id=%u, vol=%s\n", tdcp->c_cnid, hfsmp->vcbVN);
3120 }
3121 }
3122 tdcp->c_dirchangecnt++;
3123 tdcp->c_flag |= C_MODIFIED;
3124 hfs_incr_gencount(tdcp);
3125 tdcp->c_touch_chgtime = TRUE;
3126 tdcp->c_touch_modtime = TRUE;
3127
3128 error = hfs_update(tdvp, 0);
3129 if (error) {
3130 if (error != EIO && error != ENXIO) {
3131 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_link: error %d updating tdvp %p\n", error, tdvp);
3132 error = EIO;
3133 }
3134 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
3135 }
3136
3137 hfs_volupdate(hfsmp, VOL_MKFILE, (tdcp->c_cnid == kHFSRootFolderID));
3138 }
3139
3140 if (error == 0 && (ret = hfs_update(vp, 0)) != 0) {
3141 if (ret != EIO && ret != ENXIO)
3142 LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_link: error %d updating vp @ %p\n", ret, vp);
3143 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
3144 }
3145
3146 out:
3147 if (lockflags) {
3148 hfs_systemfile_unlock(hfsmp, lockflags);
3149 }
3150 if (intrans) {
3151 hfs_end_transaction(hfsmp);
3152 }
3153
3154 tdcp->c_flag &= ~C_DIR_MODIFICATION;
3155 //TBD - We have wakeup here but can't see anyone who's msleeping on c_flag...
3156 // wakeup((caddr_t)&tdcp->c_flag);
3157
3158 if (fdcp) {
3159 hfs_unlockfour(tdcp, cp, fdcp, NULL);
3160 } else {
3161 hfs_unlockpair(tdcp, cp);
3162 }
3163
3164 return (error);
3165 }
3166
3167 int hfs_removefile_callback(GenericLFBuf *psBuff, void *pvArgs) {
3168
3169 journal_kill_block(((struct hfsmount *)pvArgs)->jnl, psBuff);
3170
3171 return (0);
3172 }
3173
3174
3175 /*
3176 * hfs_vgetrsrc acquires a resource fork vnode corresponding to the
3177 * cnode that is found in 'vp'. The cnode should be locked upon entry
3178 * and will be returned locked, but it may be dropped temporarily.
3179 *
3180 * If the resource fork vnode does not exist, HFS will attempt to acquire an
3181 * empty (uninitialized) vnode from VFS so as to avoid deadlocks with
3182 * jetsam. If we let the normal getnewvnode code produce the vnode for us
3183 * we would be doing so while holding the cnode lock of our cnode.
3184 *
3185 * On success, *rvpp wlll hold the resource fork vnode with an
3186 * iocount. *Don't* forget the vnode_put.
3187 */
3188 int
3189 hfs_vgetrsrc( struct vnode *vp, struct vnode **rvpp)
3190 {
3191 struct hfsmount *hfsmp = VTOHFS(vp);
3192 struct vnode *rvp = NULL;
3193 struct cnode *cp = VTOC(vp);
3194 int error = 0;
3195
3196 restart:
3197 /* Attempt to use existing vnode */
3198 if ((rvp = cp->c_rsrc_vp)) {
3199 hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
3200 hfs_chash_raise_OpenLookupCounter(cp);
3201
3202 } else {
3203 struct cat_fork rsrcfork;
3204 struct cat_desc *descptr = NULL;
3205 struct cat_desc to_desc;
3206 int newvnode_flags = 0;
3207
3208 hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
3209
3210 /*
3211 * We could have raced with another thread here while we dropped our cnode
3212 * lock. See if the cnode now has a resource fork vnode and restart if appropriate.
3213 *
3214 * Note: We just released the cnode lock, so there is a possibility that the
3215 * cnode that we just acquired has been deleted or even removed from disk
3216 * completely, though this is unlikely. If the file is open-unlinked, the
3217 * check below will resolve it for us. If it has been completely
3218 * removed (even from the catalog!), then when we examine the catalog
3219 * directly, below, while holding the catalog lock, we will not find the
3220 * item and we can fail out properly.
3221 */
3222 if (cp->c_rsrc_vp) {
3223 /* Drop the empty vnode before restarting */
3224 hfs_unlock(cp);
3225 rvp = NULL;
3226 goto restart;
3227 }
3228
3229 /*
3230 * hfs_vgetsrc may be invoked for a cnode that has already been marked
3231 * C_DELETED. This is because we need to continue to provide rsrc
3232 * fork access to open-unlinked files. In this case, build a fake descriptor
3233 * like in hfs_removefile. If we don't do this, buildkey will fail in
3234 * cat_lookup because this cnode has no name in its descriptor.
3235 */
3236 if ((cp->c_flag & C_DELETED ) && (cp->c_desc.cd_namelen == 0)) {
3237 char delname[32];
3238 bzero (&to_desc, sizeof(to_desc));
3239 bzero (delname, 32);
3240 MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid);
3241 to_desc.cd_nameptr = (const u_int8_t*) delname;
3242 to_desc.cd_namelen = strlen(delname);
3243 to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
3244 to_desc.cd_flags = 0;
3245 to_desc.cd_cnid = cp->c_cnid;
3246
3247 descptr = &to_desc;
3248 }
3249 else {
3250 descptr = &cp->c_desc;
3251 }
3252
3253
3254 int lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
3255
3256 /*
3257 * We call cat_idlookup (instead of cat_lookup) below because we can't
3258 * trust the descriptor in the provided cnode for lookups at this point.
3259 * Between the time of the original lookup of this vnode and now, the
3260 * descriptor could have gotten swapped or replaced. If this occurred,
3261 * the parent/name combo originally desired may not necessarily be provided
3262 * if we use the descriptor. Even worse, if the vnode represents
3263 * a hardlink, we could have removed one of the links from the namespace
3264 * but left the descriptor alone, since hfs_unlink does not invalidate
3265 * the descriptor in the cnode if other links still point to the inode.
3266 *
3267 * Consider the following (slightly contrived) scenario:
3268 * /tmp/a <--> /tmp/b (hardlinks).
3269 * 1. Thread A: open rsrc fork on /tmp/b.
3270 * 1a. Thread A: does lookup, goes out to lunch right before calling getnamedstream.
3271 * 2. Thread B does 'mv /foo/b /tmp/b'
3272 * 2. Thread B succeeds.
3273 * 3. Thread A comes back and wants rsrc fork info for /tmp/b.
3274 *
3275 * Even though the hardlink backing /tmp/b is now eliminated, the descriptor
3276 * is not removed/updated during the unlink process. So, if you were to
3277 * do a lookup on /tmp/b, you'd acquire an entirely different record's resource
3278 * fork.
3279 *
3280 * As a result, we use the fileid, which should be invariant for the lifetime
3281 * of the cnode (possibly barring calls to exchangedata).
3282 *
3283 * Addendum: We can't do the above for HFS standard since we aren't guaranteed to
3284 * have thread records for files. They were only required for directories. So
3285 * we need to do the lookup with the catalog name. This is OK since hardlinks were
3286 * never allowed on HFS standard.
3287 */
3288
3289 /* Get resource fork data */
3290 error = cat_idlookup (hfsmp, cp->c_fileid, 0, 1, NULL, NULL, &rsrcfork);
3291
3292 hfs_systemfile_unlock(hfsmp, lockflags);
3293 if (error) {
3294 LFHFS_LOG(LEVEL_ERROR, "hfs_vgetrsrc: cat_idlookup failed with error [%d]\n", error);
3295 hfs_unlock(cp);
3296 hfs_chash_lower_OpenLookupCounter(cp);
3297 return (error);
3298 }
3299 /*
3300 * Supply hfs_getnewvnode with a component name.
3301 */
3302 struct componentname cn;
3303 cn.cn_pnbuf = NULL;
3304 if (descptr->cd_nameptr) {
3305 void *buf = hfs_malloc(MAXPATHLEN);
3306
3307 cn = (struct componentname){
3308 .cn_nameiop = LOOKUP,
3309 .cn_flags = ISLASTCN,
3310 .cn_pnlen = MAXPATHLEN,
3311 .cn_pnbuf = buf,
3312 .cn_nameptr = buf,
3313 .cn_namelen = snprintf(buf, MAXPATHLEN,
3314 "%s%s", descptr->cd_nameptr,
3315 _PATH_RSRCFORKSPEC)
3316 };
3317
3318 // Should never happen because cn.cn_nameptr won't ever be long...
3319 if (cn.cn_namelen >= MAXPATHLEN) {
3320 hfs_free(buf);
3321 LFHFS_LOG(LEVEL_ERROR, "hfs_vgetrsrc: cnode name too long [ENAMETOOLONG]\n");
3322 hfs_unlock(cp);
3323 hfs_chash_lower_OpenLookupCounter(cp);
3324 return ENAMETOOLONG;
3325 }
3326 }
3327
3328 /*
3329 * We are about to call hfs_getnewvnode and pass in the vnode that we acquired
3330 * earlier when we were not holding any locks. The semantics of GNV_USE_VP require that
3331 * either hfs_getnewvnode consume the vnode and vend it back to us, properly initialized,
3332 * or it will consume/dispose of it properly if it errors out.
3333 */
3334 error = hfs_getnewvnode(hfsmp, NULL, cn.cn_pnbuf ? &cn : NULL,
3335 descptr, (GNV_WANTRSRC | GNV_SKIPLOCK),
3336 &cp->c_attr, &rsrcfork, &rvp, &newvnode_flags);
3337
3338 hfs_free(cn.cn_pnbuf);
3339 if (error)
3340 return (error);
3341 } /* End 'else' for rsrc fork not existing */
3342
3343 *rvpp = rvp;
3344 return (0);
3345 }