/*
- * Copyright (c) 1999-2012 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2015 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <sys/file.h>
#include <sys/mount.h>
#include <sys/vnode.h>
+#include <sys/vnode_internal.h>
#include <sys/malloc.h>
#include <sys/kdebug.h>
#include <sys/kauth.h>
* When should we lock parent_hp in here ??
*/
static int
-hfs_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int *cnode_locked)
+hfs_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int *cnode_locked, int force_casesensitive_lookup)
{
struct cnode *dcp; /* cnode for directory being searched */
struct vnode *tvp; /* target vnode */
cnp->cn_flags &= ~MAKEENTRY;
goto found; /* We always know who we are */
} else {
- if (hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK) != 0) {
+ if (hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
retval = ENOENT; /* The parent no longer exists ? */
goto exit;
}
goto retry;
}
- /* No need to go to catalog if there are no children */
- if (dcp->c_entries == 0) {
- goto notfound;
- }
+
+ /*
+ * We shouldn't need to go to the catalog if there are no children.
+ * However, in the face of a minor disk corruption where the valence of
+ * the directory is off, we could infinite loop here if we return ENOENT
+ * even though there are actually items in the directory. (create will
+ * see the ENOENT, try to create something, which will return with
+ * EEXIST over and over again). As a result, always check the catalog.
+ */
bzero(&cndesc, sizeof(cndesc));
cndesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
- retval = cat_lookup(hfsmp, &cndesc, 0, &desc, &attr, &fork, NULL);
+ retval = cat_lookup(hfsmp, &cndesc, 0, force_casesensitive_lookup, &desc, &attr, &fork, NULL);
hfs_systemfile_unlock(hfsmp, lockflags);
goto found;
}
-notfound:
+
/*
* ENAMETOOLONG supersedes other errors
*
retval = ENAMETOOLONG;
} else if (retval == 0) {
retval = ENOENT;
+ } else if (retval == ERESERVEDNAME) {
+ /*
+ * We found the name in the catalog, but it is unavailable
+ * to us. The exact error to return to our caller depends
+ * on the operation, and whether we've already reached the
+ * last path component. In all cases, avoid a negative
+ * cache entry, since someone else may be able to access
+ * the name if their lookup is configured differently.
+ */
+
+ cnp->cn_flags &= ~MAKEENTRY;
+
+ if (((flags & ISLASTCN) == 0) || ((nameiop == LOOKUP) || (nameiop == DELETE))) {
+ /* A reserved name for a pure lookup is the same as the path not being present */
+ retval = ENOENT;
+ } else {
+ /* A reserved name with intent to create must be rejected as impossible */
+ retval = EEXIST;
+ }
}
if (retval != ENOENT)
goto exit;
* directory has not been removed, then can consider
* allowing file to be created.
*/
- if ((nameiop == CREATE || nameiop == RENAME ||
- (nameiop == DELETE &&
- (cnp->cn_flags & DOWHITEOUT) &&
- (cnp->cn_flags & ISWHITEOUT))) &&
+ if ((nameiop == CREATE || nameiop == RENAME) &&
(flags & ISLASTCN) &&
!(ISSET(dcp->c_flag, C_DELETED | C_NOEXISTS))) {
retval = EJUSTRETURN;
* Directory hard links can have multiple parents so
* find the appropriate parent for the current thread.
*/
- if ((retval = hfs_vget(hfsmp, hfs_currentparent(VTOC(dvp)), &tvp, 0, 0))) {
+ if ((retval = hfs_vget(hfsmp, hfs_currentparent(VTOC(dvp),
+ /* have_lock: */ false), &tvp, 0, 0))) {
goto exit;
}
*cnode_locked = 1;
* Save the origin info for file and directory hardlinks. Directory hardlinks
* need the origin for '..' lookups, and file hardlinks need it to ensure that
* competing lookups do not cause us to vend different hardlinks than the ones requested.
- * We want to restrict saving the cache entries to LOOKUP namei operations, since
- * we're really doing this to protect getattr.
*/
- if ((nameiop == LOOKUP) && (VTOC(tvp)->c_flag & C_HARDLINK)) {
+ if (ISSET(VTOC(tvp)->c_flag, C_HARDLINK))
hfs_savelinkorigin(VTOC(tvp), VTOC(dvp)->c_fileid);
- }
*cnode_locked = 1;
*vpp = tvp;
}
int error;
struct vnode **vpp = ap->a_vpp;
struct componentname *cnp = ap->a_cnp;
+ struct proc *p = vfs_context_proc(ap->a_context);
int flags = cnp->cn_flags;
+ int force_casesensitive_lookup = proc_is_forcing_hfs_case_sensitivity(p);
int cnode_locked;
+ int fastdev_candidate = 0;
+ int auto_candidate = 0;
*vpp = NULL;
dcp = VTOC(dvp);
-
hfsmp = VTOHFS(dvp);
+ if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && (vnode_isfastdevicecandidate(dvp) || (dcp->c_attr.ca_recflags & kHFSFastDevCandidateMask)) ){
+ fastdev_candidate = 1;
+ auto_candidate = (vnode_isautocandidate(dvp) || (dcp->c_attr.ca_recflags & kHFSAutoCandidateMask));
+ }
+
+
/*
* Lookup an entry in the cache
*
goto exit;
}
-
+ if (cp->c_attr.ca_recflags & kHFSDoNotFastDevPinMask) {
+ fastdev_candidate = 0;
+ }
+
/*
* If this is a hard-link vnode then we need to update
* the name (of the link), the parent ID, the cnid, the
* getattrlist calls to return the correct link info.
*/
- if ((flags & ISLASTCN) && (cp->c_flag & C_HARDLINK)) {
+ /*
+ * Alternatively, if we are forcing a case-sensitive lookup
+ * on a case-insensitive volume, the namecache entry
+ * may have been for an incorrect case. Since we cannot
+ * determine case vs. normalization, redrive the catalog
+ * lookup based on any byte mismatch.
+ */
+ if (((flags & ISLASTCN) && (cp->c_flag & C_HARDLINK))
+ || (force_casesensitive_lookup && !(hfsmp->hfs_flags & HFS_CASE_SENSITIVE))) {
int stale_link = 0;
- hfs_lock(cp, HFS_FORCE_LOCK);
+ hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
if ((cp->c_parentcnid != dcp->c_cnid) ||
+ (cnp->cn_namelen != cp->c_desc.cd_namelen) ||
(bcmp(cnp->cn_nameptr, cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen) != 0)) {
struct cat_desc desc;
struct cat_attr lookup_attr;
int lockflags;
+
+ if (force_casesensitive_lookup && !(hfsmp->hfs_flags & HFS_CASE_SENSITIVE)) {
+ /*
+ * Since the name in the cnode doesn't match our lookup
+ * string exactly, do a full lookup.
+ */
+ hfs_unlock (cp);
+
+ vnode_put(vp);
+ goto lookup;
+ }
+
/*
* Get an updated descriptor
*/
lockflags = hfs_systemfile_lock(VTOHFS(dvp), SFL_CATALOG, HFS_SHARED_LOCK);
- error = cat_lookup(VTOHFS(vp), &desc, 0, &desc, &lookup_attr, NULL, NULL);
+ error = cat_lookup(VTOHFS(vp), &desc, 0, 0, &desc, &lookup_attr, NULL, NULL);
hfs_systemfile_unlock(VTOHFS(dvp), lockflags);
* Save the origin info for file and directory hardlinks. Directory hardlinks
* need the origin for '..' lookups, and file hardlinks need it to ensure that
* competing lookups do not cause us to vend different hardlinks than the ones requested.
- * We want to restrict saving the cache entries to LOOKUP namei operations, since
- * we're really doing this to protect getattr.
*/
- if (cnp->cn_nameiop == LOOKUP) {
- hfs_savelinkorigin(cp, dcp->c_fileid);
- }
+ hfs_savelinkorigin(cp, dcp->c_fileid);
}
else {
/* If the fileID does not match then do NOT replace the descriptor! */
*/
cnode_locked = 0;
- error = hfs_lookup(dvp, vpp, cnp, &cnode_locked);
+ error = hfs_lookup(dvp, vpp, cnp, &cnode_locked, force_casesensitive_lookup);
+
+ if (*vpp && (VTOC(*vpp)->c_attr.ca_recflags & kHFSDoNotFastDevPinMask)) {
+ fastdev_candidate = 0;
+ }
+
+ if (*vpp && (VTOC(*vpp)->c_attr.ca_recflags & kHFSAutoCandidateMask)) {
+ //printf("vp %s / %d is an auto-candidate\n", (*vpp)->v_name ? (*vpp)->v_name : "no-name", VTOC(*vpp)->c_fileid);
+ auto_candidate = 1;
+ }
if (cnode_locked)
hfs_unlock(VTOC(*vpp));
exit:
+ if (*vpp && fastdev_candidate && (*vpp)->v_parent == dvp && !(vnode_isfastdevicecandidate(*vpp))) {
+ vnode_setfastdevicecandidate(*vpp);
+ if (auto_candidate) {
+ vnode_setautocandidate(*vpp);
+ }
+ }
+
{
uthread_t ut = (struct uthread *)get_bsdthread_info(current_thread());
* completed and throttling at the systemcall return
*/
if (__improbable(ut->uu_lowpri_window)) {
- throttle_lowpri_io(TRUE);
+ throttle_lowpri_io(1);
}
}