#include <sys/stat.h>
#include <sys/mount.h>
#include "lf_hfs_vnops.h"
#include "lf_hfs.h"
#include "lf_hfs_catalog.h"
#include "lf_hfs_dirops_handler.h"
#include "lf_hfs_fileops_handler.h"
#include "lf_hfs_vfsutils.h"
#include "lf_hfs_logger.h"
#include "lf_hfs_attrlist.h"
#include "lf_hfs_btree.h"
#include "lf_hfs_vfsops.h"
#include "lf_hfs_utils.h"
#include "lf_hfs_readwrite_ops.h"
#include "lf_hfs_generic_buf.h"
#include "lf_hfs_endian.h"
#include <sys/stat.h>
#include <sys/mount.h>
#include "lf_hfs_link.h"
#include "lf_hfs_journal.h"
#include "lf_hfs_chash.h"
#define DOT_DIR_SIZE (UVFS_DIRENTRY_RECLEN(1))
#define DOT_X2_DIR_SIZE (UVFS_DIRENTRY_RECLEN(2))
#define HFSRM_SKIP_RESERVE 0x01
#define _PATH_RSRCFORKSPEC "/..namedfork/rsrc"
void
replace_desc(struct cnode *cp, struct cat_desc *cdp)
{
if (&cp->c_desc == cdp) {
return;
}
if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) {
const u_int8_t *name = cp->c_desc.cd_nameptr;
cp->c_desc.cd_nameptr = 0;
cp->c_desc.cd_namelen = 0;
cp->c_desc.cd_flags &= ~CD_HASBUF;
hfs_free((void*)name);
}
bcopy(cdp, &cp->c_desc, sizeof(cp->c_desc));
cdp->cd_nameptr = NULL;
cdp->cd_namelen = 0;
cdp->cd_flags &= ~CD_HASBUF;
}
static void SynthesizeDotAndDotX2(u_int64_t uCnid, void* puBuff, bool bIsDot, bool bIsLastEntry)
{
UVFSDirEntry* psDotEntry = (UVFSDirEntry*)puBuff;
uint8_t uNameLen = bIsDot? 1: 2;
memset( psDotEntry, 0, UVFS_DIRENTRY_RECLEN(uNameLen));
psDotEntry->de_fileid = uCnid;
psDotEntry->de_filetype = UVFS_FA_TYPE_DIR;
psDotEntry->de_reclen = bIsLastEntry ? 0 : UVFS_DIRENTRY_RECLEN(uNameLen);
psDotEntry->de_nextcookie = uNameLen;
psDotEntry->de_namelen = uNameLen;
uint8_t* puNameBuf = (uint8_t*)psDotEntry->de_name;
puNameBuf[0] = '.';
if ( bIsDot )
{
puNameBuf[1] = '\0';
}
else
{
puNameBuf[1] = '.';
puNameBuf[2] = '\0';
}
}
static int SyntisizeEntries(uint64_t* puOffset, ReadDirBuff_s* psReadDirBuffer, int iIsExtended, u_int64_t uCnid, u_int64_t uParentCnid, UVFSDirEntry** ppsDotDotEntry)
{
int iError = 0;
void* pvBuff = NULL;
if (!iIsExtended)
{
return ENOTSUP;
}
if (DOT_DIR_SIZE > psReadDirBuffer->uBufferResid)
{
goto exit;
}
pvBuff = hfs_malloc(DOT_DIR_SIZE);
if (pvBuff == NULL)
{
LFHFS_LOG(LEVEL_ERROR, "SyntisizeEntries: Failed to allocate buffer for DOT entry\n");
return ENOMEM;
}
if (*puOffset == 0)
{
bool bIsEnoughRoomForAll = (DOT_DIR_SIZE + DOT_X2_DIR_SIZE > psReadDirBuffer->uBufferResid);
SynthesizeDotAndDotX2(uCnid, pvBuff, true, bIsEnoughRoomForAll);
memcpy(psReadDirBuffer->pvBuffer + READDIR_BUF_OFFSET(psReadDirBuffer) , pvBuff, DOT_DIR_SIZE);
(*puOffset)++;
psReadDirBuffer->uBufferResid -= DOT_DIR_SIZE;
}
if (DOT_X2_DIR_SIZE > psReadDirBuffer->uBufferResid)
{
goto exit;
}
hfs_free(pvBuff);
pvBuff = hfs_malloc(DOT_X2_DIR_SIZE);
if (pvBuff == NULL)
{
LFHFS_LOG(LEVEL_ERROR, "SyntisizeEntries: Failed to allocate buffer for DOTx2 entry\n");
return ENOMEM;
}
if (*puOffset == 1)
{
SynthesizeDotAndDotX2(uParentCnid, pvBuff, false, false);
memcpy(psReadDirBuffer->pvBuffer + READDIR_BUF_OFFSET(psReadDirBuffer), pvBuff, DOT_X2_DIR_SIZE);
*ppsDotDotEntry = (UVFSDirEntry*) (psReadDirBuffer->pvBuffer + READDIR_BUF_OFFSET(psReadDirBuffer));
(*puOffset)++;
psReadDirBuffer->uBufferResid -= DOT_X2_DIR_SIZE;
}
exit:
if (pvBuff)
hfs_free(pvBuff);
return iError;
}
int
hfs_vnop_readdir(vnode_t vp, int *eofflag, int *numdirent, ReadDirBuff_s* psReadDirBuffer, uint64_t puCookie, int flags)
{
struct cnode *cp = NULL;
struct hfsmount *hfsmp = VTOHFS(vp);
directoryhint_t *dirhint = NULL;
directoryhint_t localhint;
bool bLocalEOFflag = false;
int error = 0;
uint64_t offset;
user_size_t user_original_resid = psReadDirBuffer->uBufferResid;
int items = 0;
cnid_t cnid_hint = 0;
int bump_valence = 0;
*numdirent = 0;
uint64_t startoffset = offset = puCookie;
bool extended = (flags & VNODE_READDIR_EXTENDED);
bool nfs_cookies = extended && (flags & VNODE_READDIR_REQSEEKOFF);
if (psReadDirBuffer->pvBuffer == NULL || psReadDirBuffer->uBufferResid < sizeof(UVFSDirEntry))
{
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readdir: readDir input is not valid\n");
return EINVAL;
}
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
{
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readdir: Failed to lock vnode\n");
return error;
}
cp = VTOC(vp);
if (nfs_cookies)
{
cnid_hint = (cnid_t)(offset >> 32);
offset &= 0x00000000ffffffffLL;
if (cnid_hint == INT_MAX)
{
bLocalEOFflag = true;
goto out;
}
}
UVFSDirEntry* psDotDotEntry = NULL;
if (!(cp->c_flag & C_DELETED))
{
if ( (error = SyntisizeEntries(&offset, psReadDirBuffer, extended, cp->c_cnid, cp->c_parentcnid, &psDotDotEntry)) != 0 )
{
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readdir: Failed to syntisize dot/dotdot entries\n");
goto out;
}
}
int index = (offset & HFS_INDEX_MASK) - 2;
unsigned int tag = (unsigned int) (offset & ~HFS_INDEX_MASK);
int lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (nfs_cookies && cnid_hint != 0)
{
if (cat_findname(hfsmp, cnid_hint, &localhint.dh_desc) == 0)
{
if ( localhint.dh_desc.cd_parentcnid == cp->c_fileid)
{
localhint.dh_index = index - 1;
localhint.dh_time = 0;
bzero(&localhint.dh_link, sizeof(localhint.dh_link));
dirhint = &localhint;
}
else
{
cat_releasedesc(&localhint.dh_desc);
}
}
}
if (dirhint == NULL)
{
dirhint = hfs_getdirhint(cp, ((index - 1) & HFS_INDEX_MASK) | tag, 0);
dirhint->dh_index &= HFS_INDEX_MASK;
if (dirhint->dh_index == HFS_INDEX_MASK)
{
dirhint->dh_index = -1;
}
}
if (index == 0)
{
dirhint->dh_threadhint = cp->c_dirthreadhint;
}
else
{
if (dirhint->dh_desc.cd_flags & CD_EOF)
{
error = 0;
bLocalEOFflag = true;
offset = startoffset;
if (user_original_resid > 0) {
psReadDirBuffer->uBufferResid = user_original_resid;
}
hfs_systemfile_unlock (hfsmp, lockflags);
goto seekoffcalc;
}
}
error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, psReadDirBuffer, flags, &items, &bLocalEOFflag, psDotDotEntry);
if (index == 0 && error == 0)
{
cp->c_dirthreadhint = dirhint->dh_threadhint;
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error != 0)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readdir: Failed to get dir entries\n");
goto out;
}
index += items;
if (items >= (int)cp->c_entries)
{
bLocalEOFflag = true;
}
if ((cp->c_entries == 0) && (items > 0))
{
cp->c_entries++;
cp->c_flag |= C_MODIFIED;
LFHFS_LOG(LEVEL_DEBUG, "hfs_vnop_readdir: repairing valence to non-zero! \n");
bump_valence++;
}
while (tag == 0)
tag = (++cp->c_dirhinttag) << HFS_INDEX_BITS;
offset = ((index + 2) | tag);
dirhint->dh_index |= tag;
seekoffcalc:
cp->c_touch_acctime = TRUE;
if (numdirent)
{
if (startoffset == 0)
items += 2;
else if (startoffset == 1)
items += 1;
*numdirent = items;
}
out:
if ((dirhint != NULL) && (dirhint != &localhint) && (offset == startoffset))
{
hfs_reldirhint(cp, dirhint);
bLocalEOFflag = true;
}
if (eofflag)
{
*eofflag = bLocalEOFflag;
}
if (dirhint == &localhint)
{
cat_releasedesc(&localhint.dh_desc);
}
if (bump_valence)
{
hfs_update(vp, 0);
}
hfs_unlock(cp);
return (error);
}
int
hfs_vnop_readdirattr(vnode_t vp, int *eofflag, int *numdirent, ReadDirBuff_s* psReadDirBuffer, uint64_t puCookie)
{
int error;
uint32_t newstate;
uint32_t uMaxCount = (uint32_t) psReadDirBuffer->uBufferResid / _UVFS_DIRENTRYATTR_RECLEN(UVFS_DIRENTRYATTR_NAMEOFF,0);
if (psReadDirBuffer->pvBuffer == NULL || psReadDirBuffer->uBufferResid < sizeof(UVFSDirEntry))
{
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readdirattr: buffer input is invalid\n");
return EINVAL;
}
error = hfs_readdirattr_internal(vp, psReadDirBuffer, uMaxCount, &newstate, eofflag, numdirent, puCookie);
return (error);
}
static int
hfs_metasync_all(struct hfsmount *hfsmp)
{
int lockflags;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG|SFL_EXTENTS|SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
#if LF_HFS_FULL_VNODE_SUPPORT
if (hfsmp->hfs_catalog_vp)
hfs_btsync(hfsmp->hfs_catalog_vp, 0);
if (hfsmp->hfs_extents_vp)
hfs_btsync(hfsmp->hfs_extents_vp, 0);
if (hfsmp->hfs_attribute_vp)
hfs_btsync(hfsmp->hfs_attribute_vp, 0);
#endif
hfs_systemfile_unlock(hfsmp, lockflags);
return 0;
}
int
hfs_fsync(struct vnode *vp, int waitfor, hfs_fsync_mode_t fsyncmode)
{
struct cnode *cp = VTOC(vp);
struct filefork *fp = NULL;
int retval = 0;
struct timeval tv;
int took_trunc_lock = 0;
int fsync_default = 1;
int wait = (waitfor == MNT_WAIT);
if (fsyncmode != HFS_FSYNC)
fsync_default = 0;
if (vnode_isdir(vp))
goto metasync;
fp = VTOF(vp);
if (vnode_issystem(vp))
{
if (VTOF(vp)->fcbBTCBPtr != NULL)
{
if (VTOHFS(vp)->jnl == NULL)
{
BTFlushPath(VTOF(vp));
}
}
}
else
{
}
if (fp && (((cp->c_flag & C_ALWAYS_ZEROFILL) && !TAILQ_EMPTY(&fp->ff_invalidranges)) ||
((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
((cp->c_bsdflags & UF_NODUMP) == 0) &&
(vnode_issystem(vp) ==0) &&
cp->c_zftimeout != 0)))
{
microtime(&tv);
if ((cp->c_flag & C_ALWAYS_ZEROFILL) == 0 && fsync_default && tv.tv_sec < (long)cp->c_zftimeout)
{
cp->c_flag |= C_ZFWANTSYNC;
goto datasync;
}
if (!TAILQ_EMPTY(&fp->ff_invalidranges))
{
if (!took_trunc_lock || (cp->c_truncatelockowner == HFS_SHARED_OWNER))
{
hfs_unlock(cp);
if (took_trunc_lock) {
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
}
hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
took_trunc_lock = 1;
}
#if LF_HFS_FULL_VNODE_SUPPORT
hfs_flush_invalid_ranges(vp);
hfs_unlock(cp);
(void) cluster_push(vp, waitdata ? IO_SYNC : 0);
hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
#endif
}
}
datasync:
if (took_trunc_lock)
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
metasync:
if (vnode_isreg(vp) && vnode_issystem(vp))
{
if (VTOF(vp)->fcbBTCBPtr != NULL)
{
microtime(&tv);
BTSetLastSync(VTOF(vp), (u_int32_t) tv.tv_sec);
}
cp->c_touch_acctime = FALSE;
cp->c_touch_chgtime = FALSE;
cp->c_touch_modtime = FALSE;
}
else
{
retval = hfs_update(vp, HFS_UPDATE_FORCE);
#if 0
if ((retval == 0) && wait && fsync_default && cp->c_hint &&
!ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
hfs_metasync(VTOHFS(vp), (daddr64_t)cp->c_hint);
}
#endif
if (!fsync_default)
{
if (VTOHFS(vp)->jnl) {
if (fsyncmode == HFS_FSYNC_FULL)
hfs_flush(VTOHFS(vp), HFS_FLUSH_FULL);
else
hfs_flush(VTOHFS(vp), HFS_FLUSH_JOURNAL_BARRIER);
}
else
{
retval = hfs_metasync_all(VTOHFS(vp));
hfs_flush(VTOHFS(vp), HFS_FLUSH_CACHE);
}
}
}
#if LF_HFS_FULL_VNODE_SUPPORT
if (!hfs_is_dirty(cp) && !ISSET(cp->c_flag, C_DELETED))
vnode_cleardirty(vp);
#endif
return (retval);
}
int
hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
int flags, int skip_reserve, int allow_dirs, int only_unlink)
{
struct cnode *cp;
struct cnode *dcp;
struct vnode *rsrc_vp = NULL;
struct hfsmount *hfsmp;
struct cat_desc desc;
int dataforkbusy = 0;
int rsrcforkbusy = 0;
int lockflags;
int error = 0;
int started_tr = 0;
int isbigfile = 0, defer_remove=0;
bool isdir= false;
int update_vh = 0;
cp = VTOC(vp);
dcp = VTOC(dvp);
hfsmp = VTOHFS(vp);
if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
return (EINVAL);
}
if (!hfs_valid_cnode(hfsmp, dvp, cnp, cp->c_fileid, NULL, &error))
{
return error;
}
if (VNODE_IS_RSRC(vp) || vnode_issystem(vp) || IsEntryAJnlFile(hfsmp, cp->c_fileid))
{
LFHFS_LOG(LEVEL_ERROR, "hfs_removefile: Removing %s file is not premited\n", VNODE_IS_RSRC(vp) ? "Resource" : (vnode_issystem(vp)? "System" : "Journal"));
return (EPERM);
}
else
{
rsrc_vp = cp->c_rsrc_vp;
}
if (cp->c_flag & C_HARDLINK)
{
if (IS_DIR(vp) && (cp->c_linkcount == 1) && (allow_dirs == 0))
{
LFHFS_LOG(LEVEL_ERROR, "hfs_removefile: Trying to remove an hardlink directory\n");
return (EPERM);
}
return hfs_unlink(hfsmp, dvp, vp, cnp, skip_reserve);
}
if (IS_DIR(vp))
{
if (!allow_dirs)
{
return (EPERM);
}
isdir = true;
}
if ((cp->c_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
(cp->c_parentcnid != dcp->c_fileid))
{
LFHFS_LOG(LEVEL_ERROR, "hfs_removefile: Parent ID's are wrong\n");
return (EINVAL);
}
dcp->c_flag |= C_DIR_MODIFICATION;
cp->c_flag |= C_DELETED;
if ( !isdir )
{
dataforkbusy = 0;
if (rsrc_vp && (cp->c_blocks - VTOF(vp)->ff_blocks))
{
rsrcforkbusy = 0;
}
isbigfile = cp->c_datafork->ff_size >= HFS_BIGFILE_SIZE;
}
if ((hfsmp->hfs_attribute_vp != NULL) && (cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0)
{
defer_remove = 1;
}
if (only_unlink)
{
defer_remove = 1;
}
if (dataforkbusy || rsrcforkbusy)
{
if ((flags & VNODE_REMOVE_NODELETEBUSY) || (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0))
{
error = EBUSY;
goto out;
}
}
if (isdir == 0 && (!dataforkbusy || !rsrcforkbusy) && (defer_remove == 0))
{
if (!dataforkbusy && cp->c_datafork->ff_blocks && !isbigfile)
{
cp->c_flag |= C_NEED_DATA_SETSIZE;
}
if (!rsrcforkbusy && rsrc_vp)
{
cp->c_flag |= C_NEED_RSRC_SETSIZE;
}
}
if ((error = hfs_start_transaction(hfsmp)) != 0)
{
goto out;
}
started_tr = 1;
if (isdir == 0 && (!dataforkbusy && !rsrcforkbusy) && (only_unlink == 0))
{
if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0)
{
error = hfs_prepare_release_storage (hfsmp, vp);
if (error)
{
goto out;
}
update_vh = 1;
}
if (!rsrcforkbusy && rsrc_vp)
{
error = hfs_prepare_release_storage (hfsmp, rsrc_vp);
if (error)
{
goto out;
}
update_vh = 1;
}
}
if (isdir)
{
desc.cd_flags = CD_ISDIR;
}
else
{
desc.cd_flags = 0;
}
desc.cd_encoding = cp->c_desc.cd_encoding;
desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
desc.cd_namelen = cnp->cn_namelen;
desc.cd_parentcnid = dcp->c_fileid;
desc.cd_hint = cp->c_desc.cd_hint;
desc.cd_cnid = cp->c_cnid;
struct timeval tv;
microtime(&tv);
if (dataforkbusy || rsrcforkbusy || isbigfile || defer_remove)
{
char delname[32];
struct cat_desc to_desc;
struct cat_desc todir_desc;
bzero(&todir_desc, sizeof(todir_desc));
todir_desc.cd_parentcnid = 2;
MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid);
bzero(&to_desc, sizeof(to_desc));
to_desc.cd_nameptr = (const u_int8_t *)delname;
to_desc.cd_namelen = strlen(delname);
to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
if (isdir)
{
to_desc.cd_flags = CD_ISDIR;
}
else
{
to_desc.cd_flags = 0;
}
to_desc.cd_cnid = cp->c_cnid;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
if (!skip_reserve)
{
if ((error = cat_preflight(hfsmp, CAT_RENAME, NULL)))
{
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
}
error = cat_rename(hfsmp, &desc, &todir_desc, &to_desc, (struct cat_desc *)NULL);
if (error == 0)
{
hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries++;
if (isdir == 1)
{
INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
}
(void) cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS], &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
if (dcp->c_entries > 0)
dcp->c_entries--;
if (isdir == 1)
{
DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
}
dcp->c_dirchangecnt++;
hfs_incr_gencount(dcp);
dcp->c_ctime = tv.tv_sec;
dcp->c_mtime = tv.tv_sec;
(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
cp->c_flag |= C_DELETED;
cp->c_ctime = tv.tv_sec;
--cp->c_linkcount;
(void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
goto out;
}
else
{
struct filefork *temp_rsrc_fork = NULL;
u_int32_t fileid = cp->c_fileid;
if ((isdir == 0) && (cp->c_rsrcfork == NULL) && (cp->c_blocks - VTOF(vp)->ff_blocks))
{
temp_rsrc_fork = hfs_mallocz(sizeof(struct filefork));
temp_rsrc_fork->ff_cp = cp;
rl_init(&temp_rsrc_fork->ff_invalidranges);
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
if (temp_rsrc_fork)
{
error = cat_lookup (hfsmp, &desc, 1, (struct cat_desc*) NULL, (struct cat_attr*) NULL, &temp_rsrc_fork->ff_data, NULL);
if (error)
{
hfs_free(temp_rsrc_fork);
hfs_systemfile_unlock (hfsmp, lockflags);
goto out;
}
}
if (!skip_reserve)
{
if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL)))
{
if (temp_rsrc_fork)
{
hfs_free(temp_rsrc_fork);
}
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
}
error = cat_delete(hfsmp, &desc, &cp->c_attr);
if (error && error != ENXIO && error != ENOENT)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_removefile: deleting file %s (id=%d) vol=%s err=%d\n",
cp->c_desc.cd_nameptr, cp->c_attr.ca_fileid, hfsmp->vcbVN, error);
}
if (error == 0)
{
if (dcp->c_entries > 0)
{
dcp->c_entries--;
}
dcp->c_dirchangecnt++;
hfs_incr_gencount(dcp);
dcp->c_ctime = tv.tv_sec;
dcp->c_mtime = tv.tv_sec;
(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
{
if (temp_rsrc_fork)
{
hfs_free(temp_rsrc_fork);
}
goto out;
}
if (IS_LNK(vp) && cp->c_datafork->ff_symlinkptr)
{
hfs_free(cp->c_datafork->ff_symlinkptr);
cp->c_datafork->ff_symlinkptr = NULL;
}
if (temp_rsrc_fork)
{
error = hfs_release_storage (hfsmp, cp->c_datafork, temp_rsrc_fork, fileid);
}
else
{
error = hfs_release_storage (hfsmp, cp->c_datafork, cp->c_rsrcfork, fileid);
}
if (error)
{
hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
}
else
{
update_vh = 0;
}
if (temp_rsrc_fork)
{
hfs_free(temp_rsrc_fork);
}
cp->c_flag |= C_NOEXISTS;
cp->c_flag &= ~C_DELETED;
cp->c_touch_chgtime = TRUE;
--cp->c_linkcount;
hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
}
cat_releasedesc(&cp->c_desc);
out:
if (error)
{
cp->c_flag &= ~C_DELETED;
}
if (update_vh)
{
hfs_volupdate (hfsmp, VOL_UPDATE, 0);
}
if (started_tr)
{
hfs_end_transaction(hfsmp);
}
dcp->c_flag &= ~C_DIR_MODIFICATION;
return (error);
}
int
hfs_vnop_remove(struct vnode* psParentDir,struct vnode *psFileToRemove, struct componentname* psCN, int iFlags)
{
struct cnode *dcp = VTOC(psParentDir);
struct cnode *cp = VTOC(psFileToRemove);
struct vnode *rvp = NULL;
int error = 0;
if (psParentDir == psFileToRemove)
{
return (EINVAL);
}
relock:
hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK)))
{
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
if (rvp)
{
hfs_chash_lower_OpenLookupCounter(cp);
rvp = NULL;
}
return (error);
}
enum vtype vtype = psFileToRemove->sFSParams.vnfs_vtype;
if ((vtype == VLNK) || (vtype == VREG))
{
if ((cp->c_rsrc_vp) && (rvp == NULL))
{
rvp = cp->c_rsrc_vp;
hfs_chash_raise_OpenLookupCounter(cp);
hfs_unlock_truncate (cp, HFS_LOCK_DEFAULT);
hfs_unlockpair (dcp, cp);
goto relock;
}
}
if (dcp->c_flag & (C_DELETED | C_NOEXISTS))
{
error = ENOENT;
goto rm_done;
}
error = hfs_removefile(psParentDir, psFileToRemove, psCN, iFlags, 0, 0, 0);
rm_done:
psParentDir->sExtraData.sDirData.uDirVersion++;
hfs_unlockpair(dcp, cp);
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
if (rvp)
{
hfs_chash_lower_OpenLookupCounter(cp);
rvp = NULL;
}
return (error);
}
int
hfs_vnop_rmdir(struct vnode *dvp, struct vnode *vp, struct componentname* psCN)
{
int error = 0;
struct cnode *dcp = VTOC(dvp);
struct cnode *cp = VTOC(vp);
if (!S_ISDIR(cp->c_mode))
{
return (ENOTDIR);
}
if (dvp == vp)
{
return (EINVAL);
}
if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK)))
{
return (error);
}
if (dcp->c_flag & (C_DELETED | C_NOEXISTS))
{
hfs_unlockpair (dcp, cp);
return ENOENT;
}
error = hfs_removedir(dvp, vp, psCN, 0, 0);
hfs_unlockpair(dcp, cp);
return (error);
}
int
hfs_removedir(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, int skip_reserve, int only_unlink)
{
struct cnode *cp;
struct cnode *dcp;
struct hfsmount * hfsmp;
struct cat_desc desc;
int lockflags;
int error = 0, started_tr = 0;
cp = VTOC(vp);
dcp = VTOC(dvp);
hfsmp = VTOHFS(vp);
if (cp->c_flag & (C_NOEXISTS | C_DELETED)){
return (EINVAL);
}
if (cp->c_entries != 0){
return (ENOTEMPTY);
}
if (cp->c_flag & C_HARDLINK)
{
return hfs_unlink(hfsmp, dvp, vp, cnp, skip_reserve);
}
if (((hfsmp->hfs_attribute_vp != NULL) && ((cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0)) || (only_unlink != 0))
{
int ret = hfs_removefile(dvp, vp, cnp, 0, 0, 1, only_unlink);
return ret;
}
dcp->c_flag |= C_DIR_MODIFICATION;
if ((error = hfs_start_transaction(hfsmp)) != 0)
{
goto out;
}
started_tr = 1;
if ((dcp->c_bsdflags & (UF_APPEND | SF_APPEND)) || (cp->c_bsdflags & ((UF_IMMUTABLE | SF_IMMUTABLE | UF_APPEND | SF_APPEND))))
{
error = EPERM;
goto out;
}
desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
desc.cd_namelen = cnp->cn_namelen;
desc.cd_parentcnid = dcp->c_fileid;
desc.cd_cnid = cp->c_cnid;
desc.cd_flags = CD_ISDIR;
desc.cd_encoding = cp->c_encoding;
desc.cd_hint = 0;
if (!hfs_valid_cnode(hfsmp, dvp, cnp, cp->c_fileid, NULL, &error))
{
error = 0;
goto out;
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
if (!skip_reserve)
{
if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL)))
{
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
}
error = cat_delete(hfsmp, &desc, &cp->c_attr);
if (!error)
{
if (dcp->c_entries > 0)
dcp->c_entries--;
DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
dcp->c_dirchangecnt++;
hfs_incr_gencount(dcp);
dcp->c_touch_chgtime = TRUE;
dcp->c_touch_modtime = TRUE;
dcp->c_flag |= C_MODIFIED;
hfs_update(dcp->c_vp, 0);
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
goto out;
hfs_volupdate(hfsmp, VOL_RMDIR, (dcp->c_cnid == kHFSRootFolderID));
cp->c_flag |= C_NOEXISTS;
out:
dvp->sExtraData.sDirData.uDirVersion++;
dcp->c_flag &= ~C_DIR_MODIFICATION;
if (started_tr)
{
hfs_end_transaction(hfsmp);
}
return (error);
}
int hfs_vnop_setattr( vnode_t vp, const UVFSFileAttributes *attr )
{
int err = 0;
if ( attr->fa_validmask == 0 )
{
return 0;
}
if ( ( attr->fa_validmask & READ_ONLY_FA_FIELDS )
)
{
return EINVAL;
}
struct cnode *cp = NULL;
struct hfsmount *hfsmp = VTOHFS(vp);
if (hfs_is_journal_file(hfsmp, VTOC(vp))) {
return (EPERM);
}
if ( attr->fa_validmask & UVFS_FA_VALID_SIZE )
{
if (!vnode_isreg(vp))
{
if (vnode_isdir(vp) || vnode_islnk(vp))
{
return EPERM;
}
return EINVAL;
}
hfs_lock_truncate(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
err = hfs_truncate(vp, attr->fa_size, 0, 0);
hfs_unlock_truncate(VTOC(vp), HFS_LOCK_DEFAULT);
if (err)
return err;
}
if ((err = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
return (err);
cp = VTOC(vp);
if ( attr->fa_validmask & UVFS_FA_VALID_UID )
{
cp->c_flag |= C_MODIFIED;
cp->c_touch_chgtime = TRUE;
cp->c_uid = attr->fa_uid;
}
if ( attr->fa_validmask & UVFS_FA_VALID_GID )
{
cp->c_flag |= C_MODIFIED;
cp->c_touch_chgtime = TRUE;
cp->c_gid = attr->fa_gid;
}
if ( attr->fa_validmask & UVFS_FA_VALID_MODE )
{
mode_t new_mode = (cp->c_mode & ~ALLPERMS) | (attr->fa_mode & ALLPERMS);
if (new_mode != cp->c_mode) {
cp->c_mode = new_mode;
cp->c_flag |= C_MINOR_MOD;
}
}
if ( attr->fa_validmask & UVFS_FA_VALID_BSD_FLAGS )
{
cp->c_bsdflags = attr->fa_bsd_flags;
}
if ( attr->fa_validmask & UVFS_FA_VALID_ATIME )
{
cp->c_atime = attr->fa_atime.tv_sec;
cp->c_touch_acctime = FALSE;
}
if ( attr->fa_validmask & UVFS_FA_VALID_BIRTHTIME )
{
cp->c_ctime = attr->fa_birthtime.tv_sec;
}
if ( attr->fa_validmask & UVFS_FA_VALID_MTIME )
{
cp->c_mtime = attr->fa_mtime.tv_sec;
cp->c_touch_modtime = FALSE;
cp->c_touch_chgtime = TRUE;
hfs_clear_might_be_dirty_flag(cp);
}
err = hfs_update(vp, 0);
if ((cp->c_flag & C_HARDLINK) && (!IS_DIR(vp))){
hfs_relorigin(cp, 0);
}
hfs_unlock(cp);
return err;
}
int
hfs_update(struct vnode *vp, int options)
{
#pragma unused (options)
struct cnode *cp = VTOC(vp);
const struct cat_fork *dataforkp = NULL;
const struct cat_fork *rsrcforkp = NULL;
struct cat_fork datafork;
struct cat_fork rsrcfork;
struct hfsmount *hfsmp;
int lockflags;
int error = 0;
if (ISSET(cp->c_flag, C_NOEXISTS))
return 0;
hfsmp = VTOHFS(vp);
if (((vnode_issystem(vp) && (cp->c_cnid < kHFSFirstUserCatalogNodeID))) ||
hfsmp->hfs_catalog_vp == NULL){
return (0);
}
if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (cp->c_mode == 0)) {
CLR(cp->c_flag, C_MODIFIED | C_MINOR_MOD | C_NEEDS_DATEADDED);
cp->c_touch_acctime = 0;
cp->c_touch_chgtime = 0;
cp->c_touch_modtime = 0;
return (0);
}
hfs_touchtimes(hfsmp, cp);
if (!ISSET(cp->c_flag, C_MODIFIED | C_MINOR_MOD)
&& !hfs_should_save_atime(cp)) {
return 0;
}
bool check_txn = false;
if (!ISSET(options, HFS_UPDATE_FORCE) && !ISSET(cp->c_flag, C_MODIFIED)) {
if (hfsmp->jnl
&& journal_current_txn(hfsmp->jnl) == cp->c_update_txn) {
check_txn = true;
}
else
{
error = 0;
goto exit;
}
}
error = hfs_start_transaction(hfsmp);
if ( error != 0 )
{
goto exit;
}
if (check_txn
&& journal_current_txn(hfsmp->jnl) != cp->c_update_txn) {
hfs_end_transaction(hfsmp);
error = 0;
goto exit;
}
dataforkp = hfs_prepare_fork_for_update(cp->c_datafork, NULL, &datafork, hfsmp->blockSize);
rsrcforkp = hfs_prepare_fork_for_update(cp->c_rsrcfork, NULL, &rsrcfork, hfsmp->blockSize);
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
if (hfsmp->jnl)
cp->c_update_txn = journal_current_txn(hfsmp->jnl);
hfs_systemfile_unlock(hfsmp, lockflags);
CLR(cp->c_flag, C_MODIFIED | C_MINOR_MOD);
hfs_end_transaction(hfsmp);
exit:
return error;
}
const struct cat_fork *
hfs_prepare_fork_for_update(filefork_t *ff, const struct cat_fork *cf, struct cat_fork *cf_buf, uint32_t block_size)
{
if (!ff)
return NULL;
if (!cf)
cf = &ff->ff_data;
if (!cf_buf)
cf_buf = &ff->ff_data;
off_t max_size = ff->ff_size;
if (!ff->ff_unallocblocks && ff->ff_size <= max_size)
return cf;
if (ff->ff_blocks < ff->ff_unallocblocks) {
LFHFS_LOG(LEVEL_ERROR, "hfs_prepare_fork_for_update: ff_blocks %d is less than unalloc blocks %d\n",
ff->ff_blocks, ff->ff_unallocblocks);
hfs_assert(0);
}
struct cat_fork *out = cf_buf;
if (out != cf)
bcopy(cf, out, sizeof(*cf));
out->cf_blocks -= out->cf_vblocks;
off_t alloc_bytes = blk_to_bytes(out->cf_blocks, block_size);
if (out->cf_size > alloc_bytes)
out->cf_size = alloc_bytes;
if (out->cf_size > max_size)
out->cf_size = max_size;
return out;
}
int
hfs_vnop_readlink( struct vnode *vp, void* data, size_t dataSize, size_t *actuallyRead )
{
struct cnode *cp;
struct filefork *fp;
int error;
if (!vnode_islnk(vp))
{
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readlink: Received node is not a symlink\n");
return (EINVAL);
}
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
return (error);
cp = VTOC(vp);
fp = VTOF(vp);
if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readlink: Symlink is with invalid content length\n");
error = EINVAL;
goto exit;
}
if ( dataSize < (size_t)fp->ff_size+1 )
{
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_readlink: Received buffer size is too small\n");
error = ENOBUFS;
goto exit;
}
if (fp->ff_symlinkptr == NULL) {
GenericLFBufPtr bp = NULL;
fp->ff_symlinkptr = hfs_mallocz(fp->ff_size);
if ( fp->ff_symlinkptr == NULL )
{
error = ENOMEM;
goto exit;
}
bp = lf_hfs_generic_buf_allocate( vp, 0, roundup((int)fp->ff_size, VTOHFS(vp)->hfs_physical_block_size), 0);
error = lf_hfs_generic_buf_read(bp);
if (error) {
lf_hfs_generic_buf_release(bp);
if (fp->ff_symlinkptr) {
hfs_free(fp->ff_symlinkptr);
fp->ff_symlinkptr = NULL;
}
goto exit;
}
bcopy(bp->pvData, fp->ff_symlinkptr, (size_t)fp->ff_size);
lf_hfs_generic_buf_release(bp);
}
memcpy(data, fp->ff_symlinkptr, fp->ff_size);
((uint8_t*)data)[fp->ff_size] = 0;
*actuallyRead = fp->ff_size+1;
exit:
hfs_unlock(cp);
return (error);
}
int
hfs_vnop_mkdir(vnode_t a_dvp, vnode_t *a_vpp, struct componentname *a_cnp, UVFSFileAttributes* a_vap)
{
int iErr = 0;
a_cnp->cn_flags |= MAKEENTRY;
a_vap->fa_type = UVFS_FA_TYPE_DIR;
iErr = hfs_makenode(a_dvp, a_vpp, a_cnp, a_vap);
#if HFS_CRASH_TEST
CRASH_ABORT(CRASH_ABORT_MAKE_DIR, a_dvp->mount, NULL);
#endif
return(iErr);
}
int
hfs_vnop_create(vnode_t a_dvp, vnode_t *a_vpp, struct componentname *a_cnp, UVFSFileAttributes* a_vap)
{
a_vap->fa_type = UVFS_FA_TYPE_FILE;
return hfs_makenode(a_dvp, a_vpp, a_cnp, a_vap);
}
int
hfs_makenode(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, UVFSFileAttributes *psGivenAttr)
{
struct hfsmount *hfsmp = VTOHFS(dvp);
struct cnode *dcp = NULL;
struct cnode *cp = NULL;
struct vnode *tvp = NULL;
enum vtype vnodetype = UVFSTOV(psGivenAttr->fa_type);
mode_t mode = MAKEIMODE(vnodetype);
struct cat_attr attr = {0};
int lockflags;
int error, started_tr = 0;
int newvnode_flags = 0;
u_int32_t gnv_flags = 0;
int nocache = 0;
struct cat_desc out_desc = {0};
out_desc.cd_flags = 0;
out_desc.cd_nameptr = NULL;
if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
return (error);
dcp = VTOC(dvp);
if (dcp->c_flag & (C_DELETED | C_NOEXISTS))
{
error = ENOENT;
goto exit;
}
if ( !(psGivenAttr->fa_validmask & UVFS_FA_VALID_MODE) && (vnodetype != VDIR) )
{
LFHFS_LOG(LEVEL_ERROR, "hfs_makenode: Invalid mode or type[%#llx, %d]",
(unsigned long long)psGivenAttr->fa_validmask, psGivenAttr->fa_type);
error = EINVAL;
goto exit;
}
if ( ( psGivenAttr->fa_validmask & READ_ONLY_FA_FIELDS ) )
{
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);
error = EINVAL;
goto exit;
}
dcp->c_flag |= C_DIR_MODIFICATION;
*vpp = NULL;
if (hfs_freeblks(hfsmp, 1) == 0)
{
error = ENOSPC;
goto exit;
}
struct timeval tv;
microtime(&tv);
if ( psGivenAttr->fa_validmask & UVFS_FA_VALID_MODE )
{
mode = (mode & ~ALLPERMS) | (psGivenAttr->fa_mode & ALLPERMS);
}
attr.ca_mode = mode;
attr.ca_linkcount = 1;
attr.ca_itime = tv.tv_sec;
attr.ca_atime = attr.ca_ctime = attr.ca_mtime = attr.ca_itime;
attr.ca_atimeondisk = attr.ca_atime;
if (vnodetype == VDIR)
{
if (hfsmp->hfs_flags & HFS_FOLDERCOUNT)
{
attr.ca_recflags = kHFSHasFolderCountMask;
}
}
else
{
attr.ca_recflags = kHFSThreadExistsMask;
}
hfs_write_dateadded (&attr, attr.ca_atime);
hfs_write_gencount(&attr, (uint32_t)1);
if ( psGivenAttr->fa_validmask & UVFS_FA_VALID_UID )
{
attr.ca_uid = psGivenAttr->fa_uid;
}
if ( psGivenAttr->fa_validmask & UVFS_FA_VALID_GID )
{
attr.ca_gid = psGivenAttr->fa_gid;
}
if (vnodetype == VLNK)
{
struct FndrFileInfo *fip;
fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
fip->fdType = SWAP_BE32(kSymLinkFileType);
fip->fdCreator = SWAP_BE32(kSymLinkCreator);
}
struct cat_desc in_desc ={0};
in_desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
in_desc.cd_namelen = cnp->cn_namelen;
in_desc.cd_parentcnid = dcp->c_fileid;
in_desc.cd_flags = S_ISDIR(mode) ? CD_ISDIR : 0;
in_desc.cd_hint = dcp->c_childhint;
in_desc.cd_encoding = 0;
if ((error = hfs_start_transaction(hfsmp)) != 0)
{
goto exit;
}
started_tr = 1;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
cnid_t new_id = 0;
error = cat_preflight(hfsmp, CAT_CREATE, NULL);
if (error != 0)
{
hfs_systemfile_unlock(hfsmp, lockflags);
goto exit;
}
error = cat_acquire_cnid(hfsmp, &new_id);
if (error != 0)
{
hfs_systemfile_unlock (hfsmp, lockflags);
goto exit;
}
error = cat_create(hfsmp, new_id, &in_desc, &attr, &out_desc);
if (error == 0) {
dcp->c_childhint = out_desc.cd_hint;
dcp->c_entries++;
if (vnodetype == VDIR)
{
INC_FOLDERCOUNT(hfsmp, dcp->c_attr);
}
dcp->c_dirchangecnt++;
hfs_incr_gencount(dcp);
dcp->c_touch_chgtime = dcp->c_touch_modtime = true;
dcp->c_flag |= C_MODIFIED;
hfs_update(dcp->c_vp, 0);
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
goto exit;
uint32_t txn = hfsmp->jnl ? journal_current_txn(hfsmp->jnl) : 0;
hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE, (dcp->c_cnid == kHFSRootFolderID));
if (started_tr)
{
hfs_end_transaction(hfsmp);
started_tr = 0;
}
gnv_flags |= GNV_CREATE;
if (nocache)
{
gnv_flags |= GNV_NOCACHE;
}
error = hfs_getnewvnode(hfsmp, dvp, cnp, &out_desc, gnv_flags, &attr,
NULL, &tvp, &newvnode_flags);
if (error)
goto exit;
cp = VTOC(tvp);
cp->c_update_txn = txn;
*vpp = tvp;
exit:
cat_releasedesc(&out_desc);
dvp->sExtraData.sDirData.uDirVersion++;
if (dcp)
{
dcp->c_flag &= ~C_DIR_MODIFICATION;
hfs_unlock(dcp);
}
if (cp != NULL) {
hfs_unlock(cp);
}
if (started_tr) {
hfs_end_transaction(hfsmp);
}
return (error);
}
int
hfs_vnop_symlink(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, char* symlink_content, UVFSFileAttributes *attrp)
{
struct vnode *vp = NULL;
struct cnode *cp = NULL;
struct hfsmount *hfsmp;
struct filefork *fp;
GenericLFBufPtr bp = NULL;
char *datap;
int started_tr = 0;
uint64_t len;
int error;
hfsmp = VTOHFS(dvp);
len = strlen(symlink_content);
if (len > MAXPATHLEN)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_symlink: Received symlink content too long\n");
return (ENAMETOOLONG);
}
if (len == 0 )
{
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_symlink: Received zero length symlink content\n");
return (EINVAL);
}
if (((u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize) < len) {
return (ENOSPC);
}
attrp->fa_type = UVFS_FA_TYPE_SYMLINK;
attrp->fa_mode |= S_IFLNK;
attrp->fa_validmask |= UVFS_FA_VALID_MODE;
if ((error = hfs_makenode(dvp, vpp, cnp, attrp))) {
goto out;
}
vp = *vpp;
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
goto out;
}
cp = VTOC(vp);
fp = VTOF(vp);
if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
goto out;
}
#if QUOTA
(void)hfs_getinoquota(cp);
#endif
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto out;
}
started_tr = 1;
error = hfs_truncate(vp, len, IO_NOZEROFILL, 0);
if (error) {
hfs_end_transaction(hfsmp);
hfs_unlock(cp);
hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
if (hfs_start_transaction(hfsmp) != 0) {
started_tr = 0;
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
goto out;
}
(void) hfs_removefile(dvp, vp, cnp, 0, 0, 0, 0);
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
goto out;
}
bp = lf_hfs_generic_buf_allocate( vp, 0, roundup((int)fp->ff_size, hfsmp->hfs_physical_block_size), 0);
error = lf_hfs_generic_buf_read( bp );
if ( error != 0 )
{
goto out;
}
if (hfsmp->jnl)
{
journal_modify_block_start(hfsmp->jnl, bp);
}
datap = bp->pvData;
assert(bp->uDataSize >= len);
bzero(datap, bp->uDataSize);
bcopy(symlink_content, datap, len);
if (hfsmp->jnl)
{
journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
bp = NULL; }
else
{
error = lf_hfs_generic_buf_write(bp);
if ( error != 0 )
{
goto out;
}
}
out:
if (started_tr)
hfs_end_transaction(hfsmp);
if ((cp != NULL) && (vp != NULL)) {
hfs_unlock(cp);
}
if (error) {
if (vp) {
}
*vpp = NULL;
}
if ( bp ) {
lf_hfs_generic_buf_release(bp);
}
hfs_flush(hfsmp, HFS_FLUSH_FULL);
return (error);
}
int
hfs_vnop_renamex(struct vnode *fdvp,struct vnode *fvp, struct componentname *fcnp, struct vnode *tdvp, struct vnode *tvp, struct componentname *tcnp)
{
struct vnode *tvp_rsrc = NULL;
struct cnode *tcp = NULL;
struct cnode *error_cnode;
struct cat_desc from_desc;
struct hfsmount *hfsmp = VTOHFS(tdvp);
int tvp_deleted = 0;
int started_tr = 0, got_cookie = 0;
int took_trunc_lock = 0;
int lockflags;
int error;
int rename_exclusive = 0;
retry:
if (tvp && (vnode_isreg(tvp) || vnode_islnk(tvp))) {
hfs_lock_truncate(VTOC(tvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
took_trunc_lock = 1;
}
if (tvp && VTOC(tvp) == NULL)
return (EINVAL);
error = hfs_lockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL, HFS_EXCLUSIVE_LOCK, &error_cnode);
if (error)
{
if (took_trunc_lock)
{
hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
took_trunc_lock = 0;
}
if (tvp_rsrc && tcp)
{
hfs_chash_lower_OpenLookupCounter(tcp);
tvp_rsrc = NULL;
}
if ((error == ENOENT) && (tvp != NULL) && (error_cnode == VTOC(tvp))) {
tcp = NULL;
tvp = NULL;
goto retry;
}
return (error);
}
struct cnode* fdcp = VTOC(fdvp);
struct cnode* fcp = VTOC(fvp);
struct cnode* tdcp = VTOC(tdvp);
tcp = tvp ? VTOC(tvp) : NULL;
if (tcp && rename_exclusive)
{
error = EEXIST;
goto out;
}
if ((vnode_isreg(fvp)) || (vnode_islnk(fvp)))
{
if ((tvp) && (tcp->c_rsrc_vp) && (tvp_rsrc == NULL))
{
tvp_rsrc = tcp->c_rsrc_vp;
hfs_chash_raise_OpenLookupCounter(tcp);
if (took_trunc_lock)
{
hfs_unlock_truncate (VTOC(tvp), HFS_LOCK_DEFAULT);
took_trunc_lock = 0;
}
hfs_unlockfour(fdcp, fcp, tdcp, tcp);
goto retry;
}
}
if (fdcp->c_flag & (C_NOEXISTS | C_DELETED))
{
error = ENOENT;
goto out;
}
if (tdcp->c_flag & (C_NOEXISTS | C_DELETED))
{
error = ENOENT;
goto out;
}
if ((fcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, fdvp, fcnp, fcp->c_fileid, NULL, &error))
{
error = ENOENT;
goto out;
}
if (tcp && ((tcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, tdvp, tcnp, tcp->c_fileid, NULL, &error)))
{
if (took_trunc_lock)
{
hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
took_trunc_lock = 0;
}
error = 0;
hfs_unlockfour(fdcp, fcp, tdcp, tcp);
tcp = NULL;
tvp = NULL;
goto retry;
}
fdcp->c_flag |= C_DIR_MODIFICATION;
if (fdvp != tdvp)
{
tdcp->c_flag |= C_DIR_MODIFICATION;
}
if (vnode_isdir(fvp) && (fdvp != tdvp))
{
if (fcp->c_flag & C_HARDLINK) {
error = EPERM;
goto out;
}
if (fcp->c_attr.ca_recflags & kHFSHasChildLinkMask)
{
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (cat_check_link_ancestry(hfsmp, tdcp->c_fileid, 0))
{
error = EPERM;
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
hfs_systemfile_unlock(hfsmp, lockflags);
}
}
if (tdcp->c_parentcnid == fcp->c_fileid)
{
error = EINVAL;
goto out;
}
if (tvp && vnode_isdir(tvp) && (tcp->c_entries != 0) && fvp != tvp)
{
error = ENOTEMPTY;
goto out;
}
if (fdvp == fvp)
{
error = EINVAL;
goto out;
}
if ((fcp->c_bsdflags & (SF_IMMUTABLE | UF_IMMUTABLE | UF_APPEND | SF_APPEND)) || (fdcp->c_bsdflags & (UF_APPEND | SF_APPEND)))
{
error = EPERM;
goto out;
}
if (hfs_is_journal_file(hfsmp, fcp) || (tcp && hfs_is_journal_file(hfsmp, tcp)))
{
error = EPERM;
goto out;
}
struct cat_desc out_desc = {0};
from_desc.cd_nameptr = (const u_int8_t *)fcnp->cn_nameptr;
from_desc.cd_namelen = fcnp->cn_namelen;
from_desc.cd_parentcnid = fdcp->c_fileid;
from_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
from_desc.cd_cnid = fcp->c_cnid;
struct cat_desc to_desc = {0};
to_desc.cd_nameptr = (const u_int8_t *)tcnp->cn_nameptr;
to_desc.cd_namelen = tcnp->cn_namelen;
to_desc.cd_parentcnid = tdcp->c_fileid;
to_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
to_desc.cd_cnid = fcp->c_cnid;
if ((error = hfs_start_transaction(hfsmp)) != 0)
{
goto out;
}
started_tr = 1;
if (vnode_isdir(fvp) && (fdvp != tdvp))
{
if (fcp->c_attr.ca_recflags & kHFSHasChildLinkMask)
{
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (cat_check_link_ancestry(hfsmp, tdcp->c_fileid, 0)) {
error = EPERM;
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
hfs_systemfile_unlock(hfsmp, lockflags);
}
}
if (fcp->c_flag & C_HARDLINK)
{
struct cat_desc tmpdesc;
cnid_t real_cnid;
tmpdesc.cd_nameptr = (const u_int8_t *)fcnp->cn_nameptr;
tmpdesc.cd_namelen = fcnp->cn_namelen;
tmpdesc.cd_parentcnid = fdcp->c_fileid;
tmpdesc.cd_hint = fdcp->c_childhint;
tmpdesc.cd_flags = fcp->c_desc.cd_flags & CD_ISDIR;
tmpdesc.cd_encoding = 0;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (cat_lookup(hfsmp, &tmpdesc, 0, NULL, NULL, NULL, &real_cnid) != 0)
{
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
from_desc.cd_cnid = real_cnid;
hfs_systemfile_unlock(hfsmp, lockflags);
}
cat_cookie_t cookie;
if ((error = cat_preflight(hfsmp, CAT_RENAME + CAT_DELETE, &cookie)))
{
goto out;
}
got_cookie = 1;
if (tvp)
{
if (fvp == tvp)
{
if (!(fcp->c_flag & C_HARDLINK))
{
goto skip_rm;
}
else if ((fdvp != tdvp) || (hfsmp->hfs_flags & HFS_CASE_SENSITIVE))
{
goto out;
}
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)
{
goto skip_rm;
}
else
{
goto out;
}
}
if (vnode_isdir(tvp))
{
error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_SKIP_RESERVE, 0);
}
else
{
error = hfs_removefile(tdvp, tvp, tcnp, 0, HFSRM_SKIP_RESERVE, 0, 0);
if ((error == 0) && (tcp->c_flag & C_DELETED) && (tvp_rsrc))
{
hfs_chash_lower_OpenLookupCounter(tcp);
tvp_rsrc = NULL;
}
}
if (error)
{
goto out;
}
tvp_deleted = 1;
if ( ((VTOC(tvp)->c_flag & C_HARDLINK) == 0 ) || (VTOC(tvp)->c_linkcount == 0) )
{
INVALIDATE_NODE(tvp);
}
tcp->c_flag |= C_RENAMED;
}
skip_rm:
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
error = cat_rename(hfsmp, &from_desc, &tdcp->c_desc, &to_desc, &out_desc);
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
{
if (error == EEXIST)
{
error = ERECYCLE;
}
goto out;
}
replace_desc(fcp, &out_desc);
fcp->c_parentcnid = tdcp->c_fileid;
fcp->c_hint = 0;
if (fdvp != tdvp || !ISSET(fcp->c_attr.ca_recflags, kHFSHasDateAddedMask))
fcp->c_flag |= C_NEEDS_DATEADDED;
(void) hfs_update (fvp, 0);
hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_RMDIR : VOL_RMFILE, (fdcp->c_cnid == kHFSRootFolderID));
hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_MKDIR : VOL_MKFILE, (tdcp->c_cnid == kHFSRootFolderID));
if (fdvp != tdvp)
{
if (vnode_isdir(fvp))
{
if ((fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) && !(tdcp->c_attr.ca_recflags & kHFSHasChildLinkMask))
{
tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid);
if (error)
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_vnop_rename: error updating parent chain for %u\n", tdcp->c_cnid);
error = 0;
}
}
INC_FOLDERCOUNT(hfsmp, tdcp->c_attr);
DEC_FOLDERCOUNT(hfsmp, fdcp->c_attr);
}
tdcp->c_entries++;
tdcp->c_dirchangecnt++;
tdcp->c_flag |= C_MODIFIED;
hfs_incr_gencount(tdcp);
if (fdcp->c_entries > 0)
fdcp->c_entries--;
fdcp->c_dirchangecnt++;
fdcp->c_flag |= C_MODIFIED;
fdcp->c_touch_chgtime = TRUE;
fdcp->c_touch_modtime = TRUE;
if (ISSET(fcp->c_flag, C_HARDLINK))
{
hfs_relorigin(fcp, fdcp->c_fileid);
if (fdcp->c_fileid != fdcp->c_cnid)
hfs_relorigin(fcp, fdcp->c_cnid);
}
(void) hfs_update(fdvp, 0);
}
hfs_incr_gencount(fdcp);
tdcp->c_childhint = out_desc.cd_hint;
tdcp->c_touch_chgtime = TRUE;
tdcp->c_touch_modtime = TRUE;
(void) hfs_update(tdvp, 0);
vnode_update_identity(fvp, tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_hash, (VNODE_UPDATE_PARENT | VNODE_UPDATE_NAME));
if (fcp->c_rsrc_vp)
{
char* rsrc_path = NULL;
int len;
rsrc_path = hfs_malloc(MAXPATHLEN);
len = snprintf (rsrc_path, MAXPATHLEN, "%s%s", tcnp->cn_nameptr, _PATH_RSRCFORKSPEC);
len = MIN(len, MAXPATHLEN);
vnode_update_identity (fcp->c_rsrc_vp, fvp, rsrc_path, len, 0, (VNODE_UPDATE_NAME | VNODE_UPDATE_CACHE));
hfs_free(rsrc_path);
}
out:
if (got_cookie)
{
cat_postflight(hfsmp, &cookie);
}
if (started_tr)
{
hfs_end_transaction(hfsmp);
}
fdvp->sExtraData.sDirData.uDirVersion++;
fdcp->c_flag &= ~C_DIR_MODIFICATION;
if (fdvp != tdvp)
{
tdvp->sExtraData.sDirData.uDirVersion++;
tdcp->c_flag &= ~C_DIR_MODIFICATION;
}
if (tvp_rsrc)
{
hfs_chash_lower_OpenLookupCounter(tcp);
tvp_rsrc = NULL;
}
hfs_unlockfour(fdcp, fcp, tdcp, tcp);
if (took_trunc_lock)
{
hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
}
if (error && tvp_deleted)
error = EIO;
return (error);
}
int
hfs_vnop_link(vnode_t vp, vnode_t tdvp, struct componentname *cnp)
{
struct hfsmount *hfsmp = VTOHFS(vp);;
struct cnode *cp = VTOC(vp);;
struct cnode *tdcp;
struct cnode *fdcp = NULL;
struct cat_desc todesc;
cnid_t parentcnid;
int lockflags = 0;
int intrans = 0;
enum vtype v_type = vp->sFSParams.vnfs_vtype;
int error, ret;
if (v_type == VLNK || v_type == VDIR)
return (EPERM );
if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0) {
return (ENOTSUP);
}
if (hfs_freeblks(hfsmp, 0) == 0) {
return (ENOSPC);
}
if ((error = hfs_lockpair(VTOC(tdvp), VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
return (error);
}
tdcp = VTOC(tdvp);
parentcnid = hfs_currentparent(cp, true);
if (tdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
error = ENOENT;
goto out;
}
if (cp->c_linkcount >= HFS_LINK_MAX) {
error = EMLINK;
goto out;
}
if (cp->c_bsdflags & (UF_IMMUTABLE | SF_IMMUTABLE | UF_APPEND | SF_APPEND)) {
error = EPERM;
goto out;
}
if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
error = ENOENT;
goto out;
}
tdcp->c_flag |= C_DIR_MODIFICATION;
if (hfs_start_transaction(hfsmp) != 0) {
error = EINVAL;
goto out;
}
intrans = 1;
todesc.cd_flags = (v_type == VDIR) ? CD_ISDIR : 0;
todesc.cd_encoding = 0;
todesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
todesc.cd_namelen = cnp->cn_namelen;
todesc.cd_parentcnid = tdcp->c_fileid;
todesc.cd_hint = 0;
todesc.cd_cnid = 0;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (cat_lookup(hfsmp, &todesc, 0, NULL, NULL, NULL, NULL) == 0) {
error = EEXIST;
goto out;
}
if (cp->c_flag & C_HARDLINK) {
struct cat_attr cattr;
if ((cat_idlookup(hfsmp, cp->c_fileid, 0, 0, NULL, &cattr, NULL) != 0) ||
(cattr.ca_fileid != cp->c_fileid)) {
error = ENOENT;
goto out;
}
} else {
cnid_t fileid;
if ((cat_lookup(hfsmp, &cp->c_desc, 0, NULL, NULL, NULL, &fileid) != 0) ||
(fileid != cp->c_fileid)) {
error = ENOENT;
goto out;
}
}
if (v_type == VDIR) {
if ((parentcnid == tdcp->c_fileid) ||
(tdcp->c_fileid == kHFSRootFolderID) ||
(parentcnid == kHFSRootFolderID) ||
cat_check_link_ancestry(hfsmp, tdcp->c_fileid, cp->c_fileid)) {
error = EPERM;
goto out;
}
}
hfs_systemfile_unlock(hfsmp, lockflags);
lockflags = 0;
cp->c_linkcount++;
cp->c_flag |= C_MODIFIED;
cp->c_touch_chgtime = TRUE;
error = hfs_makelink(hfsmp, vp, cp, tdcp, cnp);
if (error) {
cp->c_linkcount--;
hfs_volupdate(hfsmp, VOL_UPDATE, 0);
} else {
tdcp->c_entries++;
if (v_type == VDIR) {
INC_FOLDERCOUNT(hfsmp, tdcp->c_attr);
tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid);
if (error) {
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_link: error updating destination parent chain for id=%u, vol=%s\n", tdcp->c_cnid, hfsmp->vcbVN);
}
}
tdcp->c_dirchangecnt++;
tdcp->c_flag |= C_MODIFIED;
hfs_incr_gencount(tdcp);
tdcp->c_touch_chgtime = TRUE;
tdcp->c_touch_modtime = TRUE;
error = hfs_update(tdvp, 0);
if (error) {
if (error != EIO && error != ENXIO) {
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_link: error %d updating tdvp %p\n", error, tdvp);
error = EIO;
}
hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
}
hfs_volupdate(hfsmp, VOL_MKFILE, (tdcp->c_cnid == kHFSRootFolderID));
}
if (error == 0 && (ret = hfs_update(vp, 0)) != 0) {
if (ret != EIO && ret != ENXIO)
LFHFS_LOG(LEVEL_ERROR, "hfs_vnop_link: error %d updating vp @ %p\n", ret, vp);
hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
}
out:
if (lockflags) {
hfs_systemfile_unlock(hfsmp, lockflags);
}
if (intrans) {
hfs_end_transaction(hfsmp);
}
tdcp->c_flag &= ~C_DIR_MODIFICATION;
if (fdcp) {
hfs_unlockfour(tdcp, cp, fdcp, NULL);
} else {
hfs_unlockpair(tdcp, cp);
}
return (error);
}
int hfs_removefile_callback(GenericLFBuf *psBuff, void *pvArgs) {
journal_kill_block(((struct hfsmount *)pvArgs)->jnl, psBuff);
return (0);
}
int
hfs_vgetrsrc( struct vnode *vp, struct vnode **rvpp)
{
struct hfsmount *hfsmp = VTOHFS(vp);
struct vnode *rvp = NULL;
struct cnode *cp = VTOC(vp);
int error = 0;
restart:
if ((rvp = cp->c_rsrc_vp)) {
hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
hfs_chash_raise_OpenLookupCounter(cp);
} else {
struct cat_fork rsrcfork;
struct cat_desc *descptr = NULL;
struct cat_desc to_desc;
int newvnode_flags = 0;
hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
if (cp->c_rsrc_vp) {
hfs_unlock(cp);
rvp = NULL;
goto restart;
}
if ((cp->c_flag & C_DELETED ) && (cp->c_desc.cd_namelen == 0)) {
char delname[32];
bzero (&to_desc, sizeof(to_desc));
bzero (delname, 32);
MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid);
to_desc.cd_nameptr = (const u_int8_t*) delname;
to_desc.cd_namelen = strlen(delname);
to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
to_desc.cd_flags = 0;
to_desc.cd_cnid = cp->c_cnid;
descptr = &to_desc;
}
else {
descptr = &cp->c_desc;
}
int lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
error = cat_idlookup (hfsmp, cp->c_fileid, 0, 1, NULL, NULL, &rsrcfork);
hfs_systemfile_unlock(hfsmp, lockflags);
if (error) {
LFHFS_LOG(LEVEL_ERROR, "hfs_vgetrsrc: cat_idlookup failed with error [%d]\n", error);
hfs_unlock(cp);
hfs_chash_lower_OpenLookupCounter(cp);
return (error);
}
struct componentname cn;
cn.cn_pnbuf = NULL;
if (descptr->cd_nameptr) {
void *buf = hfs_malloc(MAXPATHLEN);
cn = (struct componentname){
.cn_nameiop = LOOKUP,
.cn_flags = ISLASTCN,
.cn_pnlen = MAXPATHLEN,
.cn_pnbuf = buf,
.cn_nameptr = buf,
.cn_namelen = snprintf(buf, MAXPATHLEN,
"%s%s", descptr->cd_nameptr,
_PATH_RSRCFORKSPEC)
};
if (cn.cn_namelen >= MAXPATHLEN) {
hfs_free(buf);
LFHFS_LOG(LEVEL_ERROR, "hfs_vgetrsrc: cnode name too long [ENAMETOOLONG]\n");
hfs_unlock(cp);
hfs_chash_lower_OpenLookupCounter(cp);
return ENAMETOOLONG;
}
}
error = hfs_getnewvnode(hfsmp, NULL, cn.cn_pnbuf ? &cn : NULL,
descptr, (GNV_WANTRSRC | GNV_SKIPLOCK),
&cp->c_attr, &rsrcfork, &rvp, &newvnode_flags);
hfs_free(cn.cn_pnbuf);
if (error)
return (error);
}
*rvpp = rvp;
return (0);
}