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