_XkbSetMap(ClientPtr client, DeviceIntPtr dev, xkbSetMapReq *req, char *values)
{
    XkbEventCauseRec	cause;
    XkbChangesRec	change;
    Bool		sentNKN;
    XkbSrvInfoPtr       xkbi;
    XkbDescPtr          xkb;

    xkbi= dev->key->xkbInfo;
    xkb = xkbi->desc;

    XkbSetCauseXkbReq(&cause,X_kbSetMap,client);
    bzero(&change, sizeof(change));
    sentNKN = False;
    if ((xkb->min_key_code!=req->minKeyCode)||
        (xkb->max_key_code!=req->maxKeyCode)) {
	Status			status;
	xkbNewKeyboardNotify	nkn;
	nkn.deviceID = nkn.oldDeviceID = dev->id;
	nkn.oldMinKeyCode = xkb->min_key_code;
	nkn.oldMaxKeyCode = xkb->max_key_code;
	status= XkbChangeKeycodeRange(xkb, req->minKeyCode,
                                      req->maxKeyCode, &change);
	if (status != Success)
	    return status; /* oh-oh. what about the other keyboards? */
	nkn.minKeyCode = xkb->min_key_code;
	nkn.maxKeyCode = xkb->max_key_code;
	nkn.requestMajor = XkbReqCode;
	nkn.requestMinor = X_kbSetMap;
	nkn.changed = XkbNKN_KeycodesMask;
	XkbSendNewKeyboardNotify(dev,&nkn);
	sentNKN = True;
    }

    if (req->present&XkbKeyTypesMask) {
	values = SetKeyTypes(xkb,req,(xkbKeyTypeWireDesc *)values,&change);
	if (!values)	goto allocFailure;
    }
    if (req->present&XkbKeySymsMask) {
	values = SetKeySyms(client,xkb,req,(xkbSymMapWireDesc *)values,&change,dev);
	if (!values)	goto allocFailure;
    }
    if (req->present&XkbKeyActionsMask) {
	values = SetKeyActions(xkb,req,(CARD8 *)values,&change);
	if (!values)	goto allocFailure;
    }
    if (req->present&XkbKeyBehaviorsMask) {
	values= SetKeyBehaviors(xkbi,req,(xkbBehaviorWireDesc *)values,&change);
	if (!values)	goto allocFailure;
    }
    if (req->present&XkbVirtualModsMask)
	values= SetVirtualMods(xkbi,req,(CARD8 *)values,&change);
    if (req->present&XkbExplicitComponentsMask)
	values= SetKeyExplicit(xkbi,req,(CARD8 *)values,&change);
    if (req->present&XkbModifierMapMask)
	values= SetModifierMap(xkbi,req,(CARD8 *)values,&change);
    if (req->present&XkbVirtualModMapMask)
	values= SetVirtualModMap(xkbi,req,(xkbVModMapWireDesc *)values,&change);
    if (((values-((char *)req))/4)!=req->length) {
	ErrorF("[xkb] Internal error! Bad length in XkbSetMap (after set)\n");
	client->errorValue = values-((char *)&req[1]);
	return BadLength;
    }
    if (req->flags&XkbSetMapRecomputeActions) {
	KeyCode		first,last,firstMM,lastMM;
	if (change.map.num_key_syms>0) {
	    first= change.map.first_key_sym;
	    last= first+change.map.num_key_syms-1;
	}
	else first= last= 0;
	if (change.map.num_modmap_keys>0) {
	    firstMM= change.map.first_modmap_key;
	    lastMM= first+change.map.num_modmap_keys-1;
	}
	else firstMM= lastMM= 0;
	if ((last>0) && (lastMM>0)) {
	    if (firstMM<first)
		first= firstMM;
	    if (lastMM>last)
		last= lastMM;
	}
	else if (lastMM>0) {
	    first= firstMM;
	    last= lastMM;
	}
	if (last>0) {
	    unsigned check= 0;
	    XkbUpdateActions(dev,first,(last-first+1),&change,&check,&cause);
	    if (check)
		XkbCheckSecondaryEffects(xkbi,check,&change,&cause);
	}
    }
    if (!sentNKN)
	XkbSendNotification(dev,&change,&cause);

    XkbUpdateCoreDescription(dev,False);
    return Success;
allocFailure:
    return BadAlloc;
}


CheckSetDeviceIndicators(	char *		wire,
				DeviceIntPtr	dev,
				int		num,
				int *		status_rtrn,
				ClientPtr	client)
{
xkbDeviceLedsWireDesc *	ledWire;
int			i;
XkbSrvLedInfoPtr 	sli;

    ledWire= (xkbDeviceLedsWireDesc *)wire;
    for (i=0;i<num;i++) {
	if (client->swapped) {
	   register int n;
	   swaps(&ledWire->ledClass,n);
	   swaps(&ledWire->ledID,n);
	   swapl(&ledWire->namesPresent,n);
	   swapl(&ledWire->mapsPresent,n);
	   swapl(&ledWire->physIndicators,n);
	}

        sli= XkbFindSrvLedInfo(dev,ledWire->ledClass,ledWire->ledID,
							XkbXI_IndicatorsMask);
	if (sli!=NULL) {
	    register int n;
	    register unsigned bit;
	    int nMaps,nNames;
	    CARD32 *atomWire;
	    xkbIndicatorMapWireDesc *mapWire;

	    nMaps= nNames= 0;
	    for (n=0,bit=1;n<XkbNumIndicators;n++,bit<<=1) {
		if (ledWire->namesPresent&bit)
		    nNames++;
		if (ledWire->mapsPresent&bit)
		    nMaps++;
	    }
	    atomWire= (CARD32 *)&ledWire[1];
	    if (nNames>0) {
		for (n=0;n<nNames;n++) {
		    if (client->swapped) {
			register int t;
			swapl(atomWire,t);
		    }
		    CHK_ATOM_OR_NONE3(((Atom)(*atomWire)),client->errorValue,
							*status_rtrn,NULL);
		    atomWire++;
		}
	    }
	    mapWire= (xkbIndicatorMapWireDesc *)atomWire;
	    if (nMaps>0) {
		for (n=0;n<nMaps;n++) {
		    if (client->swapped) {
			register int t;
			swaps(&mapWire->virtualMods,t);
			swapl(&mapWire->ctrls,t);
		    }
		    CHK_MASK_LEGAL3(0x21,mapWire->whichGroups,
						XkbIM_UseAnyGroup,
						client->errorValue,
						*status_rtrn,NULL);
		    CHK_MASK_LEGAL3(0x22,mapWire->whichMods,XkbIM_UseAnyMods,
						client->errorValue,
						*status_rtrn,NULL);
		    mapWire++;
		}
	    }
	    ledWire= (xkbDeviceLedsWireDesc *)mapWire;
	}
	else {
	    /* SHOULD NEVER HAPPEN */
	    return (char *)ledWire;
	}
    }
    return (char *)ledWire;
}

xkb: ProcXkbSetDeviceInfo should work on all attached SDs.

If called with XkbUseCoreKbd, run through all attached SDs and replicate the
call. This way, we keep the SDs in sync with the MD as long as core clients
control the MDs.


ProcXkbGetIndicatorState(ClientPtr client)
{
    xkbGetIndicatorStateReply 	rep;
    XkbSrvLedInfoPtr		sli;
    DeviceIntPtr 		dev;
    register int 		i;

    REQUEST(xkbGetIndicatorStateReq);
    REQUEST_SIZE_MATCH(xkbGetIndicatorStateReq);

    if (!(client->xkbClientFlags&_XkbClientInitialized))
	return BadAccess;

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixReadAccess);

    sli= XkbFindSrvLedInfo(dev,XkbDfltXIClass,XkbDfltXIId,
						XkbXI_IndicatorStateMask);
    if (!sli)
	return BadAlloc;

    rep.type = X_Reply;
    rep.sequenceNumber = client->sequence;
    rep.length = 0;
    rep.deviceID = dev->id;
    rep.state = sli->effectiveState;

    if (client->swapped) {
	swaps(&rep.sequenceNumber,i);
	swapl(&rep.state,i);
    }
    WriteToClient(client, SIZEOF(xkbGetIndicatorStateReply), (char *)&rep);
    return client->noClientException;
}


ProcXkbSetNames(ClientPtr client)
{
    DeviceIntPtr	 dev;
    CARD32		*tmp;
    Atom                 bad;
    int                  rc;

    REQUEST(xkbSetNamesReq);
    REQUEST_AT_LEAST_SIZE(xkbSetNamesReq);

    if (!(client->xkbClientFlags&_XkbClientInitialized))
	return BadAccess;

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixManageAccess);
    CHK_MASK_LEGAL(0x01,stuff->which,XkbAllNamesMask);

    /* check device-independent stuff */
    tmp = (CARD32 *)&stuff[1];

    if (stuff->which&XkbKeycodesNameMask) {
	tmp= _XkbCheckAtoms(tmp,1,client->swapped,&bad);
	if (!tmp) {
	    client->errorValue = bad;
	    return BadAtom;
	}
    }
    if (stuff->which&XkbGeometryNameMask) {
	tmp= _XkbCheckAtoms(tmp,1,client->swapped,&bad);
	if (!tmp) {
	    client->errorValue = bad;
	    return BadAtom;
	}
    }
    if (stuff->which&XkbSymbolsNameMask) {
	tmp= _XkbCheckAtoms(tmp,1,client->swapped,&bad);
	if (!tmp) {
	    client->errorValue = bad;
	    return BadAtom;
	}
    }
    if (stuff->which&XkbPhysSymbolsNameMask) {
	tmp= _XkbCheckAtoms(tmp,1,client->swapped,&bad);
	if (!tmp) {
	    client->errorValue= bad;
	    return BadAtom;
	}
    }
    if (stuff->which&XkbTypesNameMask) {
	tmp= _XkbCheckAtoms(tmp,1,client->swapped,&bad);
	if (!tmp) {
	    client->errorValue = bad;
	    return BadAtom;
	}
    }
    if (stuff->which&XkbCompatNameMask) {
	tmp= _XkbCheckAtoms(tmp,1,client->swapped,&bad);
	if (!tmp) {
	    client->errorValue = bad;
	    return BadAtom;
	}
    }

    /* start of device-dependent tests */
    rc = _XkbSetNamesCheck(client, dev, stuff, tmp);
    if (rc != Success)
        return rc;

    if (stuff->deviceSpec == XkbUseCoreKbd)
    {
        DeviceIntPtr other;
        for (other = inputInfo.devices; other; other = other->next)
        {
            if ((other != dev) && other->key && !other->isMaster && (other->u.master == dev))
            {

                rc = XaceHook(XACE_DEVICE_ACCESS, client, other, DixManageAccess);
                if (rc == Success)
                {
                    rc = _XkbSetNamesCheck(client, other, stuff, tmp);
                    if (rc != Success)
                        return rc;
                }
            }
        }
    }

    /* everything is okay -- update names */

    rc = _XkbSetNames(client, dev, stuff);
    if (rc != Success)
        return rc;

    if (stuff->deviceSpec == XkbUseCoreKbd)
    {
        DeviceIntPtr other;
        for (other = inputInfo.devices; other; other = other->next)
        {
            if ((other != dev) && other->key && !other->isMaster && (other->u.master == dev))
            {

                rc = XaceHook(XACE_DEVICE_ACCESS, client, other, DixManageAccess);
                if (rc == Success)
                    _XkbSetNames(client, other, stuff);
            }
        }
    }

    /* everything is okay -- update names */

    return client->noClientException;
}


XkbWriteGeomShapes(char *wire,XkbGeometryPtr geom,Bool swap)
{
int			i;
XkbShapePtr		shape;
xkbShapeWireDesc *	shapeWire;

    for (i=0,shape=geom->shapes;i<geom->num_shapes;i++,shape++) {
	register int 		o;
	XkbOutlinePtr		ol;
	xkbOutlineWireDesc *	olWire;
	shapeWire= (xkbShapeWireDesc *)wire;
	shapeWire->name= shape->name;
	shapeWire->nOutlines= shape->num_outlines;
	if (shape->primary!=NULL)
	     shapeWire->primaryNdx= XkbOutlineIndex(shape,shape->primary);
	else shapeWire->primaryNdx= XkbNoShape;
	if (shape->approx!=NULL)
	     shapeWire->approxNdx= XkbOutlineIndex(shape,shape->approx);
	else shapeWire->approxNdx= XkbNoShape;
	if (swap) {
	    register int n;
	    swapl(&shapeWire->name,n);
	}
	wire= (char *)&shapeWire[1];
	for (o=0,ol=shape->outlines;o<shape->num_outlines;o++,ol++) {
	    register int	p;
	    XkbPointPtr		pt;
	    xkbPointWireDesc *	ptWire;
	    olWire= (xkbOutlineWireDesc *)wire;
	    olWire->nPoints= ol->num_points;
	    olWire->cornerRadius= ol->corner_radius;
	    wire= (char *)&olWire[1];
	    ptWire= (xkbPointWireDesc *)wire;
	    for (p=0,pt=ol->points;p<ol->num_points;p++,pt++) {
		ptWire[p].x= pt->x;
		ptWire[p].y= pt->y;
		if (swap) {
		    register int n;
		    swaps(&ptWire[p].x,n);
		    swaps(&ptWire[p].y,n);
		}
	    }
	    wire= (char *)&ptWire[ol->num_points];
	}
    }
    return wire;
}


XkbSizeModifierMap(XkbDescPtr xkb,xkbGetMapReply *rep)
{
    unsigned	i,len,nRtrn;

    if (((rep->present&XkbModifierMapMask)==0)||(rep->nModMapKeys<1)||
	(!xkb)||(!xkb->map)||(!xkb->map->modmap)) {
	rep->present&= ~XkbModifierMapMask;
	rep->firstModMapKey= rep->nModMapKeys= 0;
	rep->totalModMapKeys= 0;
	return 0;
    }
    for (nRtrn=i=0;i<rep->nModMapKeys;i++) {
	if (xkb->map->modmap[i+rep->firstModMapKey]!=0)
	    nRtrn++;
    }
    rep->totalModMapKeys= nRtrn;
    len= XkbPaddedSize(nRtrn*2); /* two bytes per non-zero modmap component */
    return len;
}


ProcXkbListComponents(ClientPtr client)
{
    DeviceIntPtr 		dev;
    xkbListComponentsReply 	rep;
    unsigned			len;
    int				status;
    unsigned char *		str;
    XkbSrvListInfoRec		list;

    REQUEST(xkbListComponentsReq);
    REQUEST_AT_LEAST_SIZE(xkbListComponentsReq);

    if (!(client->xkbClientFlags&_XkbClientInitialized))
	return BadAccess;

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixGetAttrAccess);

    status= Success;
    str= (unsigned char *)&stuff[1];
    bzero(&list,sizeof(XkbSrvListInfoRec));
    list.maxRtrn= stuff->maxNames;
    list.pattern[_XkbListKeymaps]= GetComponentSpec(&str,False,&status);
    list.pattern[_XkbListKeycodes]= GetComponentSpec(&str,False,&status);
    list.pattern[_XkbListTypes]= GetComponentSpec(&str,False,&status);
    list.pattern[_XkbListCompat]= GetComponentSpec(&str,False,&status);
    list.pattern[_XkbListSymbols]= GetComponentSpec(&str,False,&status);
    list.pattern[_XkbListGeometry]= GetComponentSpec(&str,False,&status);
    if (status!=Success)
	return status;
    len= str-((unsigned char *)stuff);
    if ((XkbPaddedSize(len)/4)!=stuff->length)
	return BadLength;
    if ((status=XkbDDXList(dev,&list,client))!=Success) {
	if (list.pool) {
	    _XkbFree(list.pool);
	    list.pool= NULL;
	}
	return status;
    }
    bzero(&rep,sizeof(xkbListComponentsReply));
    rep.type= X_Reply;
    rep.deviceID = dev->id;
    rep.sequenceNumber = client->sequence;
    rep.length = XkbPaddedSize(list.nPool)/4;
    rep.nKeymaps = list.nFound[_XkbListKeymaps];
    rep.nKeycodes = list.nFound[_XkbListKeycodes];
    rep.nTypes = list.nFound[_XkbListTypes];
    rep.nCompatMaps = list.nFound[_XkbListCompat];
    rep.nSymbols = list.nFound[_XkbListSymbols];
    rep.nGeometries = list.nFound[_XkbListGeometry];
    rep.extra=	0;
    if (list.nTotal>list.maxRtrn)
	rep.extra = (list.nTotal-list.maxRtrn);
    if (client->swapped) {
	register int n;
	swaps(&rep.sequenceNumber,n);
	swapl(&rep.length,n);
	swaps(&rep.nKeymaps,n);
	swaps(&rep.nKeycodes,n);
	swaps(&rep.nTypes,n);
	swaps(&rep.nCompatMaps,n);
	swaps(&rep.nSymbols,n);
	swaps(&rep.nGeometries,n);
	swaps(&rep.extra,n);
    }
    WriteToClient(client,SIZEOF(xkbListComponentsReply),(char *)&rep);
    if (list.nPool && list.pool) {
	WriteToClient(client,XkbPaddedSize(list.nPool), (char *)list.pool);
	_XkbFree(list.pool);
	list.pool= NULL;
    }
    return client->noClientException;
}


XkbComputeGetCompatMapReplySize(	XkbCompatMapPtr 	compat,
					xkbGetCompatMapReply *	rep)
{
unsigned	 size,nGroups;

    nGroups= 0;
    if (rep->groups!=0) {
	register int i,bit;
	for (i=0,bit=1;i<XkbNumKbdGroups;i++,bit<<=1) {
	    if (rep->groups&bit)
		nGroups++;
	}
    }
    size= nGroups*SIZEOF(xkbModsWireDesc);
    size+= (rep->nSI*SIZEOF(xkbSymInterpretWireDesc));
    rep->length= size/4;
    return Success;
}


XkbExtensionInit(void)
{
    ExtensionEntry *extEntry;

    if ((extEntry = AddExtension(XkbName, XkbNumberEvents, XkbNumberErrors,
				 ProcXkbDispatch, SProcXkbDispatch,
				 NULL, StandardMinorOpcode))) {
	XkbReqCode = (unsigned char)extEntry->base;
	XkbEventBase = (unsigned char)extEntry->eventBase;
	XkbErrorBase = (unsigned char)extEntry->errorBase;
	XkbKeyboardErrorCode = XkbErrorBase+XkbKeyboard;
	RT_XKBCLIENT = CreateNewResourceType(XkbClientGone);
    }
    return;
}


static ssize_t remove_slot_show(struct kobject *kobj,
				struct kobj_attribute *attr, char *buf)
{
	return sprintf(buf, "0\n");
}

PCI: rpadlpar: Fix potential drc_name corruption in store functions

Both add_slot_store() and remove_slot_store() try to fix up the
drc_name copied from the store buffer by placing a NUL terminator at
nbyte + 1 or in place of a '\n' if present. However, the static buffer
that we copy the drc_name data into is not zeroed and can contain
anything past the n-th byte.

This is problematic if a '\n' byte appears in that buffer after nbytes
and the string copied into the store buffer was not NUL terminated to
start with as the strchr() search for a '\n' byte will mark this
incorrectly as the end of the drc_name string resulting in a drc_name
string that contains garbage data after the n-th byte.

Additionally it will cause us to overwrite that '\n' byte on the stack
with NUL, potentially corrupting data on the stack.

The following debugging shows an example of the drmgr utility writing
"PHB 4543" to the add_slot sysfs attribute, but add_slot_store()
logging a corrupted string value.

  drmgr: drmgr: -c phb -a -s PHB 4543 -d 1
  add_slot_store: drc_name = PHB 4543°|<82>!, rc = -19

Fix this by using strscpy() instead of memcpy() to ensure the string
is NUL terminated when copied into the static drc_name buffer.
Further, since the string is now NUL terminated the code only needs to
change '\n' to '\0' when present.

Cc: stable@vger.kernel.org
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
[mpe: Reformat change log and add mention of possible stack corruption]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210315214821.452959-1-tyreld@linux.ibm.com


static ssize_t remove_slot_store(struct kobject *kobj,
				 struct kobj_attribute *attr,
				 const char *buf, size_t nbytes)
{
	char drc_name[MAX_DRC_NAME_LEN];
	int rc;
	char *end;

	if (nbytes >= MAX_DRC_NAME_LEN)
		return 0;

	strscpy(drc_name, buf, nbytes + 1);

	end = strchr(drc_name, '\n');
	if (end)
		*end = '\0';

	rc = dlpar_remove_slot(drc_name);
	if (rc)
		return rc;

	return nbytes;
}


static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr,
			      const char *buf, size_t nbytes)
{
	char drc_name[MAX_DRC_NAME_LEN];
	char *end;
	int rc;

	if (nbytes >= MAX_DRC_NAME_LEN)
		return 0;

	strscpy(drc_name, buf, nbytes + 1);

	end = strchr(drc_name, '\n');
	if (end)
		*end = '\0';

	rc = dlpar_add_slot(drc_name);
	if (rc)
		return rc;

	return nbytes;
}


void dlpar_sysfs_exit(void)
{
	sysfs_remove_group(dlpar_kobj, &dlpar_attr_group);
	kobject_put(dlpar_kobj);
}


int dlpar_sysfs_init(void)
{
	int error;

	dlpar_kobj = kobject_create_and_add(DLPAR_KOBJ_NAME,
					    &pci_slots_kset->kobj);
	if (!dlpar_kobj)
		return -EINVAL;

	error = sysfs_create_group(dlpar_kobj, &dlpar_attr_group);
	if (error)
		kobject_put(dlpar_kobj);
	return error;
}


static ssize_t add_slot_show(struct kobject *kobj,
			     struct kobj_attribute *attr, char *buf)
{
	return sprintf(buf, "0\n");
}


static bool validate_oplock_types(struct share_mode_lock *lck)
{
	struct share_mode_data *d = lck->data;
	bool batch = false;
	bool ex_or_batch = false;
	bool level2 = false;
	bool no_oplock = false;
	uint32_t num_non_stat_opens = 0;
	uint32_t i;

	for (i=0; i<d->num_share_modes; i++) {
		struct share_mode_entry *e = &d->share_modes[i];

		if (!is_valid_share_mode_entry(e)) {
			continue;
		}

		if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
			/* We ignore stat opens in the table - they
			   always have NO_OPLOCK and never get or
			   cause breaks. JRA. */
			continue;
		}

		num_non_stat_opens += 1;

		if (BATCH_OPLOCK_TYPE(e->op_type)) {
			/* batch - can only be one. */
			if (share_mode_stale_pid(d, i)) {
				DEBUG(10, ("Found stale batch oplock\n"));
				continue;
			}
			if (ex_or_batch || batch || level2 || no_oplock) {
				DEBUG(0, ("Bad batch oplock entry %u.",
					  (unsigned)i));
				return false;
			}
			batch = true;
		}

		if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
			if (share_mode_stale_pid(d, i)) {
				DEBUG(10, ("Found stale duplicate oplock\n"));
				continue;
			}
			/* Exclusive or batch - can only be one. */
			if (ex_or_batch || level2 || no_oplock) {
				DEBUG(0, ("Bad exclusive or batch oplock "
					  "entry %u.", (unsigned)i));
				return false;
			}
			ex_or_batch = true;
		}

		if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
			if (batch || ex_or_batch) {
				if (share_mode_stale_pid(d, i)) {
					DEBUG(10, ("Found stale LevelII "
						   "oplock\n"));
					continue;
				}
				DEBUG(0, ("Bad levelII oplock entry %u.",
					  (unsigned)i));
				return false;
			}
			level2 = true;
		}

		if (e->op_type == NO_OPLOCK) {
			if (batch || ex_or_batch) {
				if (share_mode_stale_pid(d, i)) {
					DEBUG(10, ("Found stale NO_OPLOCK "
						   "entry\n"));
					continue;
				}
				DEBUG(0, ("Bad no oplock entry %u.",
					  (unsigned)i));
				return false;
			}
			no_oplock = true;
		}
	}

	remove_stale_share_mode_entries(d);

	if ((batch || ex_or_batch) && (num_non_stat_opens != 1)) {
		DEBUG(1, ("got batch (%d) or ex (%d) non-exclusively (%d)\n",
			  (int)batch, (int)ex_or_batch,
			  (int)d->num_share_modes));
		return false;
	}

	return true;
}

Fix bug #10229 - No access check verification on stream files.

https://bugzilla.samba.org/show_bug.cgi?id=10229

We need to check if the requested access mask
could be used to open the underlying file (if
it existed), as we're passing in zero for the
access mask to the base filename.

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: David Disseldorp <ddiss@suse.de>


static void grant_fsp_oplock_type(files_struct *fsp,
				  struct share_mode_lock *lck,
				  int oplock_request)
{
	bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
		            lp_level2_oplocks(SNUM(fsp->conn));
	bool got_level2_oplock, got_a_none_oplock;
	uint32_t i;

	/* Start by granting what the client asked for,
	   but ensure no SAMBA_PRIVATE bits can be set. */
	fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);

	if (oplock_request & INTERNAL_OPEN_ONLY) {
		/* No oplocks on internal open. */
		fsp->oplock_type = NO_OPLOCK;
		DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
			fsp->oplock_type, fsp_str_dbg(fsp)));
		return;
	}

	if (lp_locking(fsp->conn->params) && file_has_brlocks(fsp)) {
		DEBUG(10,("grant_fsp_oplock_type: file %s has byte range locks\n",
			fsp_str_dbg(fsp)));
		fsp->oplock_type = NO_OPLOCK;
	}

	if (is_stat_open(fsp->access_mask)) {
		/* Leave the value already set. */
		DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
			fsp->oplock_type, fsp_str_dbg(fsp)));
		return;
	}

	got_level2_oplock = false;
	got_a_none_oplock = false;

	for (i=0; i<lck->data->num_share_modes; i++) {
		int op_type = lck->data->share_modes[i].op_type;

		if (LEVEL_II_OPLOCK_TYPE(op_type)) {
			got_level2_oplock = true;
		}
		if (op_type == NO_OPLOCK) {
			got_a_none_oplock = true;
		}
	}

	/*
	 * Match what was requested (fsp->oplock_type) with
 	 * what was found in the existing share modes.
 	 */

	if (got_level2_oplock || got_a_none_oplock) {
		if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
			fsp->oplock_type = LEVEL_II_OPLOCK;
		}
	}

	/*
	 * Don't grant level2 to clients that don't want them
	 * or if we've turned them off.
	 */
	if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
		fsp->oplock_type = NO_OPLOCK;
	}

	if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) {
		/*
		 * We're the first level2 oplock. Indicate that in brlock.tdb.
		 */
		struct byte_range_lock *brl;

		brl = brl_get_locks(talloc_tos(), fsp);
		if (brl != NULL) {
			brl_set_have_read_oplocks(brl, true);
			TALLOC_FREE(brl);
		}
	}

	DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
		  fsp->oplock_type, fsp_str_dbg(fsp)));
}


static NTSTATUS check_parent_access(struct connection_struct *conn,
				struct smb_filename *smb_fname,
				uint32_t access_mask)
{
	NTSTATUS status;
	char *parent_dir = NULL;
	struct security_descriptor *parent_sd = NULL;
	uint32_t access_granted = 0;

	if (!parent_dirname(talloc_tos(),
				smb_fname->base_name,
				&parent_dir,
				NULL)) {
		return NT_STATUS_NO_MEMORY;
	}

	if (get_current_uid(conn) == (uid_t)0) {
		/* I'm sorry sir, I didn't know you were root... */
		DEBUG(10,("check_parent_access: root override "
			"on %s. Granting 0x%x\n",
			smb_fname_str_dbg(smb_fname),
			(unsigned int)access_mask ));
		return NT_STATUS_OK;
	}

	status = SMB_VFS_GET_NT_ACL(conn,
				parent_dir,
				SECINFO_DACL,
				    talloc_tos(),
				&parent_sd);

	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(5,("check_parent_access: SMB_VFS_GET_NT_ACL failed for "
			"%s with error %s\n",
			parent_dir,
			nt_errstr(status)));
		return status;
	}

 	/*
	 * If we can access the path to this file, by
	 * default we have FILE_READ_ATTRIBUTES from the
	 * containing directory. See the section:
	 * "Algorithm to Check Access to an Existing File"
	 * in MS-FSA.pdf.
	 *
	 * se_file_access_check() also takes care of
	 * owner WRITE_DAC and READ_CONTROL.
	 */
	status = se_file_access_check(parent_sd,
				get_current_nttok(conn),
				false,
				(access_mask & ~FILE_READ_ATTRIBUTES),
				&access_granted);
	if(!NT_STATUS_IS_OK(status)) {
		DEBUG(5,("check_parent_access: access check "
			"on directory %s for "
			"path %s for mask 0x%x returned (0x%x) %s\n",
			parent_dir,
			smb_fname->base_name,
			access_mask,
			access_granted,
			nt_errstr(status) ));
		return status;
	}

	return NT_STATUS_OK;
}


static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
				   const struct share_mode_entry *exclusive,
				   uint16_t break_to)
{
	NTSTATUS status;
	char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];

	DEBUG(10, ("Sending break request to PID %s\n",
		   procid_str_static(&exclusive->pid)));

	/* Create the message. */
	share_mode_entry_to_message(msg, exclusive);

	/* Overload entry->op_type */
	SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to);

	status = messaging_send_buf(msg_ctx, exclusive->pid,
				    MSG_SMB_BREAK_REQUEST,
				    (uint8 *)msg, sizeof(msg));
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(3, ("Could not send oplock break message: %s\n",
			  nt_errstr(status)));
	}

	return status;
}


static bool share_conflict(struct share_mode_entry *entry,
			   uint32 access_mask,
			   uint32 share_access)
{
	DEBUG(10,("share_conflict: entry->access_mask = 0x%x, "
		  "entry->share_access = 0x%x, "
		  "entry->private_options = 0x%x\n",
		  (unsigned int)entry->access_mask,
		  (unsigned int)entry->share_access,
		  (unsigned int)entry->private_options));

	if (server_id_is_disconnected(&entry->pid)) {
		/*
		 * note: cleanup should have been done by
		 * delay_for_batch_oplocks()
		 */
		return false;
	}

	DEBUG(10,("share_conflict: access_mask = 0x%x, share_access = 0x%x\n",
		  (unsigned int)access_mask, (unsigned int)share_access));

	if ((entry->access_mask & (FILE_WRITE_DATA|
				   FILE_APPEND_DATA|
				   FILE_READ_DATA|
				   FILE_EXECUTE|
				   DELETE_ACCESS)) == 0) {
		DEBUG(10,("share_conflict: No conflict due to "
			  "entry->access_mask = 0x%x\n",
			  (unsigned int)entry->access_mask ));
		return False;
	}

	if ((access_mask & (FILE_WRITE_DATA|
			    FILE_APPEND_DATA|
			    FILE_READ_DATA|
			    FILE_EXECUTE|
			    DELETE_ACCESS)) == 0) {
		DEBUG(10,("share_conflict: No conflict due to "
			  "access_mask = 0x%x\n",
			  (unsigned int)access_mask ));
		return False;
	}

#if 1 /* JRA TEST - Superdebug. */
#define CHECK_MASK(num, am, right, sa, share) \
	DEBUG(10,("share_conflict: [%d] am (0x%x) & right (0x%x) = 0x%x\n", \
		(unsigned int)(num), (unsigned int)(am), \
		(unsigned int)(right), (unsigned int)(am)&(right) )); \
	DEBUG(10,("share_conflict: [%d] sa (0x%x) & share (0x%x) = 0x%x\n", \
		(unsigned int)(num), (unsigned int)(sa), \
		(unsigned int)(share), (unsigned int)(sa)&(share) )); \
	if (((am) & (right)) && !((sa) & (share))) { \
		DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \
sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \
			(unsigned int)(share) )); \
		return True; \
	}
#else
#define CHECK_MASK(num, am, right, sa, share) \
	if (((am) & (right)) && !((sa) & (share))) { \
		DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \
sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \
			(unsigned int)(share) )); \
		return True; \
	}
#endif

	CHECK_MASK(1, entry->access_mask, FILE_WRITE_DATA | FILE_APPEND_DATA,
		   share_access, FILE_SHARE_WRITE);
	CHECK_MASK(2, access_mask, FILE_WRITE_DATA | FILE_APPEND_DATA,
		   entry->share_access, FILE_SHARE_WRITE);

	CHECK_MASK(3, entry->access_mask, FILE_READ_DATA | FILE_EXECUTE,
		   share_access, FILE_SHARE_READ);
	CHECK_MASK(4, access_mask, FILE_READ_DATA | FILE_EXECUTE,
		   entry->share_access, FILE_SHARE_READ);

	CHECK_MASK(5, entry->access_mask, DELETE_ACCESS,
		   share_access, FILE_SHARE_DELETE);
	CHECK_MASK(6, access_mask, DELETE_ACCESS,
		   entry->share_access, FILE_SHARE_DELETE);

	DEBUG(10,("share_conflict: No conflict.\n"));
	return False;
}


static void defer_open(struct share_mode_lock *lck,
		       struct timeval request_time,
		       struct timeval timeout,
		       struct smb_request *req,
		       struct deferred_open_record *state)
{
	DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred "
		  "open entry for mid %llu\n",
		  (unsigned int)request_time.tv_sec,
		  (unsigned int)request_time.tv_usec,
		  (unsigned long long)req->mid));

	if (!push_deferred_open_message_smb(req, request_time, timeout,
				       state->id, (char *)state, sizeof(*state))) {
		TALLOC_FREE(lck);
		exit_server("push_deferred_open_message_smb failed");
	}
	if (lck) {
		struct defer_open_state *watch_state;
		struct tevent_req *watch_req;
		bool ret;

		watch_state = talloc(req->sconn, struct defer_open_state);
		if (watch_state == NULL) {
			exit_server("talloc failed");
		}
		watch_state->sconn = req->sconn;
		watch_state->mid = req->mid;

		DEBUG(10, ("defering mid %llu\n",
			   (unsigned long long)req->mid));

		watch_req = dbwrap_record_watch_send(
			watch_state, req->sconn->ev_ctx, lck->data->record,
			req->sconn->msg_ctx);
		if (watch_req == NULL) {
			exit_server("Could not watch share mode record");
		}
		tevent_req_set_callback(watch_req, defer_open_done,
					watch_state);

		ret = tevent_req_set_endtime(
			watch_req, req->sconn->ev_ctx,
			timeval_sum(&request_time, &timeout));
		SMB_ASSERT(ret);
	}
}


static NTSTATUS fcb_or_dos_open(struct smb_request *req,
				connection_struct *conn,
				files_struct *fsp_to_dup_into,
				const struct smb_filename *smb_fname,
				struct file_id id,
				uint16 file_pid,
				uint64_t vuid,
				uint32 access_mask,
				uint32 share_access,
				uint32 create_options)
{
	files_struct *fsp;

	DEBUG(5,("fcb_or_dos_open: attempting old open semantics for "
		 "file %s.\n", smb_fname_str_dbg(smb_fname)));

	for(fsp = file_find_di_first(conn->sconn, id); fsp;
	    fsp = file_find_di_next(fsp)) {

		DEBUG(10,("fcb_or_dos_open: checking file %s, fd = %d, "
			  "vuid = %llu, file_pid = %u, private_options = 0x%x "
			  "access_mask = 0x%x\n", fsp_str_dbg(fsp),
			  fsp->fh->fd, (unsigned long long)fsp->vuid,
			  (unsigned int)fsp->file_pid,
			  (unsigned int)fsp->fh->private_options,
			  (unsigned int)fsp->access_mask ));

		if (fsp != fsp_to_dup_into &&
		    fsp->fh->fd != -1 &&
		    fsp->vuid == vuid &&
		    fsp->file_pid == file_pid &&
		    (fsp->fh->private_options & (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
						 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
		    (fsp->access_mask & FILE_WRITE_DATA) &&
		    strequal(fsp->fsp_name->base_name, smb_fname->base_name) &&
		    strequal(fsp->fsp_name->stream_name,
			     smb_fname->stream_name)) {
			DEBUG(10,("fcb_or_dos_open: file match\n"));
			break;
		}
	}

	if (!fsp) {
		return NT_STATUS_NOT_FOUND;
	}

	/* quite an insane set of semantics ... */
	if (is_executable(smb_fname->base_name) &&
	    (fsp->fh->private_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
		DEBUG(10,("fcb_or_dos_open: file fail due to is_executable.\n"));
		return NT_STATUS_INVALID_PARAMETER;
	}

	/* We need to duplicate this fsp. */
	return dup_file_fsp(req, fsp, access_mask, share_access,
			    create_options, fsp_to_dup_into);
}


static NTSTATUS smbd_calculate_maximum_allowed_access(
	connection_struct *conn,
	const struct smb_filename *smb_fname,
	bool use_privs,
	uint32_t *p_access_mask)
{
	struct security_descriptor *sd;
	uint32_t access_granted;
	NTSTATUS status;

	if (!use_privs && (get_current_uid(conn) == (uid_t)0)) {
		*p_access_mask |= FILE_GENERIC_ALL;
		return NT_STATUS_OK;
	}

	status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name,
				    (SECINFO_OWNER |
				     SECINFO_GROUP |
				     SECINFO_DACL),
				    talloc_tos(), &sd);

	if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
		/*
		 * File did not exist
		 */
		*p_access_mask = FILE_GENERIC_ALL;
		return NT_STATUS_OK;
	}
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10,("Could not get acl on file %s: %s\n",
			  smb_fname_str_dbg(smb_fname),
			  nt_errstr(status)));
		return NT_STATUS_ACCESS_DENIED;
	}

	/*
	 * If we can access the path to this file, by
	 * default we have FILE_READ_ATTRIBUTES from the
	 * containing directory. See the section:
	 * "Algorithm to Check Access to an Existing File"
	 * in MS-FSA.pdf.
	 *
	 * se_file_access_check()
	 * also takes care of owner WRITE_DAC and READ_CONTROL.
	 */
	status = se_file_access_check(sd,
				 get_current_nttok(conn),
				 use_privs,
				 (*p_access_mask & ~FILE_READ_ATTRIBUTES),
				 &access_granted);

	TALLOC_FREE(sd);

	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("Access denied on file %s: "
			   "when calculating maximum access\n",
			   smb_fname_str_dbg(smb_fname)));
		return NT_STATUS_ACCESS_DENIED;
	}
	*p_access_mask = (access_granted | FILE_READ_ATTRIBUTES);

	if (!(access_granted & DELETE_ACCESS)) {
		if (can_delete_file_in_directory(conn, smb_fname)) {
			*p_access_mask |= DELETE_ACCESS;
		}
	}

	return NT_STATUS_OK;
}


NTSTATUS create_file_default(connection_struct *conn,
			     struct smb_request *req,
			     uint16_t root_dir_fid,
			     struct smb_filename *smb_fname,
			     uint32_t access_mask,
			     uint32_t share_access,
			     uint32_t create_disposition,
			     uint32_t create_options,
			     uint32_t file_attributes,
			     uint32_t oplock_request,
			     uint64_t allocation_size,
			     uint32_t private_flags,
			     struct security_descriptor *sd,
			     struct ea_list *ea_list,
			     files_struct **result,
			     int *pinfo)
{
	int info = FILE_WAS_OPENED;
	files_struct *fsp = NULL;
	NTSTATUS status;
	bool stream_name = false;

	DEBUG(10,("create_file: access_mask = 0x%x "
		  "file_attributes = 0x%x, share_access = 0x%x, "
		  "create_disposition = 0x%x create_options = 0x%x "
		  "oplock_request = 0x%x "
		  "private_flags = 0x%x "
		  "root_dir_fid = 0x%x, ea_list = 0x%p, sd = 0x%p, "
		  "fname = %s\n",
		  (unsigned int)access_mask,
		  (unsigned int)file_attributes,
		  (unsigned int)share_access,
		  (unsigned int)create_disposition,
		  (unsigned int)create_options,
		  (unsigned int)oplock_request,
		  (unsigned int)private_flags,
		  (unsigned int)root_dir_fid,
		  ea_list, sd, smb_fname_str_dbg(smb_fname)));

	/*
	 * Calculate the filename from the root_dir_if if necessary.
	 */

	if (root_dir_fid != 0) {
		struct smb_filename *smb_fname_out = NULL;
		status = get_relative_fid_filename(conn, req, root_dir_fid,
						   smb_fname, &smb_fname_out);
		if (!NT_STATUS_IS_OK(status)) {
			goto fail;
		}
		smb_fname = smb_fname_out;
	}

	/*
	 * Check to see if this is a mac fork of some kind.
	 */

	stream_name = is_ntfs_stream_smb_fname(smb_fname);
	if (stream_name) {
		enum FAKE_FILE_TYPE fake_file_type;

		fake_file_type = is_fake_file(smb_fname);

		if (fake_file_type != FAKE_FILE_TYPE_NONE) {

			/*
			 * Here we go! support for changing the disk quotas
			 * --metze
			 *
			 * We need to fake up to open this MAGIC QUOTA file
			 * and return a valid FID.
			 *
			 * w2k close this file directly after openening xp
			 * also tries a QUERY_FILE_INFO on the file and then
			 * close it
			 */
			status = open_fake_file(req, conn, req->vuid,
						fake_file_type, smb_fname,
						access_mask, &fsp);
			if (!NT_STATUS_IS_OK(status)) {
				goto fail;
			}

			ZERO_STRUCT(smb_fname->st);
			goto done;
		}

		if (!(conn->fs_capabilities & FILE_NAMED_STREAMS)) {
			status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
			goto fail;
		}
	}

	if (is_ntfs_default_stream_smb_fname(smb_fname)) {
		int ret;
		smb_fname->stream_name = NULL;
		/* We have to handle this error here. */
		if (create_options & FILE_DIRECTORY_FILE) {
			status = NT_STATUS_NOT_A_DIRECTORY;
			goto fail;
		}
		if (lp_posix_pathnames()) {
			ret = SMB_VFS_LSTAT(conn, smb_fname);
		} else {
			ret = SMB_VFS_STAT(conn, smb_fname);
		}

		if (ret == 0 && VALID_STAT_OF_DIR(smb_fname->st)) {
			status = NT_STATUS_FILE_IS_A_DIRECTORY;
			goto fail;
		}
	}

	status = create_file_unixpath(
		conn, req, smb_fname, access_mask, share_access,
		create_disposition, create_options, file_attributes,
		oplock_request, allocation_size, private_flags,
		sd, ea_list,
		&fsp, &info);

	if (!NT_STATUS_IS_OK(status)) {
		goto fail;
	}

 done:
	DEBUG(10, ("create_file: info=%d\n", info));

	*result = fsp;
	if (pinfo != NULL) {
		*pinfo = info;
	}
	return NT_STATUS_OK;

 fail:
	DEBUG(10, ("create_file: %s\n", nt_errstr(status)));

	if (fsp != NULL) {
		close_file(req, fsp, ERROR_CLOSE);
		fsp = NULL;
	}
	return status;
}


static bool open_match_attributes(connection_struct *conn,
				  uint32 old_dos_attr,
				  uint32 new_dos_attr,
				  mode_t existing_unx_mode,
				  mode_t new_unx_mode,
				  mode_t *returned_unx_mode)
{
	uint32 noarch_old_dos_attr, noarch_new_dos_attr;

	noarch_old_dos_attr = (old_dos_attr & ~FILE_ATTRIBUTE_ARCHIVE);
	noarch_new_dos_attr = (new_dos_attr & ~FILE_ATTRIBUTE_ARCHIVE);

	if((noarch_old_dos_attr == 0 && noarch_new_dos_attr != 0) ||
	   (noarch_old_dos_attr != 0 && ((noarch_old_dos_attr & noarch_new_dos_attr) == noarch_old_dos_attr))) {
		*returned_unx_mode = new_unx_mode;
	} else {
		*returned_unx_mode = (mode_t)0;
	}

	DEBUG(10,("open_match_attributes: old_dos_attr = 0x%x, "
		  "existing_unx_mode = 0%o, new_dos_attr = 0x%x "
		  "returned_unx_mode = 0%o\n",
		  (unsigned int)old_dos_attr,
		  (unsigned int)existing_unx_mode,
		  (unsigned int)new_dos_attr,
		  (unsigned int)*returned_unx_mode ));

	/* If we're mapping SYSTEM and HIDDEN ensure they match. */
	if (lp_map_system(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) {
		if ((old_dos_attr & FILE_ATTRIBUTE_SYSTEM) &&
		    !(new_dos_attr & FILE_ATTRIBUTE_SYSTEM)) {
			return False;
		}
	}
	if (lp_map_hidden(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) {
		if ((old_dos_attr & FILE_ATTRIBUTE_HIDDEN) &&
		    !(new_dos_attr & FILE_ATTRIBUTE_HIDDEN)) {
			return False;
		}
	}
	return True;
}


void change_file_owner_to_parent(connection_struct *conn,
					const char *inherit_from_dir,
					files_struct *fsp)
{
	struct smb_filename *smb_fname_parent;
	int ret;

	smb_fname_parent = synthetic_smb_fname(talloc_tos(), inherit_from_dir,
					       NULL, NULL);
	if (smb_fname_parent == NULL) {
		return;
	}

	ret = SMB_VFS_STAT(conn, smb_fname_parent);
	if (ret == -1) {
		DEBUG(0,("change_file_owner_to_parent: failed to stat parent "
			 "directory %s. Error was %s\n",
			 smb_fname_str_dbg(smb_fname_parent),
			 strerror(errno)));
		TALLOC_FREE(smb_fname_parent);
		return;
	}

	if (smb_fname_parent->st.st_ex_uid == fsp->fsp_name->st.st_ex_uid) {
		/* Already this uid - no need to change. */
		DEBUG(10,("change_file_owner_to_parent: file %s "
			"is already owned by uid %d\n",
			fsp_str_dbg(fsp),
			(int)fsp->fsp_name->st.st_ex_uid ));
		TALLOC_FREE(smb_fname_parent);
		return;
	}

	become_root();
	ret = SMB_VFS_FCHOWN(fsp, smb_fname_parent->st.st_ex_uid, (gid_t)-1);
	unbecome_root();
	if (ret == -1) {
		DEBUG(0,("change_file_owner_to_parent: failed to fchown "
			 "file %s to parent directory uid %u. Error "
			 "was %s\n", fsp_str_dbg(fsp),
			 (unsigned int)smb_fname_parent->st.st_ex_uid,
			 strerror(errno) ));
	} else {
		DEBUG(10,("change_file_owner_to_parent: changed new file %s to "
			"parent directory uid %u.\n", fsp_str_dbg(fsp),
			(unsigned int)smb_fname_parent->st.st_ex_uid));
		/* Ensure the uid entry is updated. */
		fsp->fsp_name->st.st_ex_uid = smb_fname_parent->st.st_ex_uid;
	}

	TALLOC_FREE(smb_fname_parent);
}


static NTSTATUS create_file_unixpath(connection_struct *conn,
				     struct smb_request *req,
				     struct smb_filename *smb_fname,
				     uint32_t access_mask,
				     uint32_t share_access,
				     uint32_t create_disposition,
				     uint32_t create_options,
				     uint32_t file_attributes,
				     uint32_t oplock_request,
				     uint64_t allocation_size,
				     uint32_t private_flags,
				     struct security_descriptor *sd,
				     struct ea_list *ea_list,

				     files_struct **result,
				     int *pinfo)
{
	int info = FILE_WAS_OPENED;
	files_struct *base_fsp = NULL;
	files_struct *fsp = NULL;
	NTSTATUS status;

	DEBUG(10,("create_file_unixpath: access_mask = 0x%x "
		  "file_attributes = 0x%x, share_access = 0x%x, "
		  "create_disposition = 0x%x create_options = 0x%x "
		  "oplock_request = 0x%x private_flags = 0x%x "
		  "ea_list = 0x%p, sd = 0x%p, "
		  "fname = %s\n",
		  (unsigned int)access_mask,
		  (unsigned int)file_attributes,
		  (unsigned int)share_access,
		  (unsigned int)create_disposition,
		  (unsigned int)create_options,
		  (unsigned int)oplock_request,
		  (unsigned int)private_flags,
		  ea_list, sd, smb_fname_str_dbg(smb_fname)));

	if (create_options & FILE_OPEN_BY_FILE_ID) {
		status = NT_STATUS_NOT_SUPPORTED;
		goto fail;
	}

	if (create_options & NTCREATEX_OPTIONS_INVALID_PARAM_MASK) {
		status = NT_STATUS_INVALID_PARAMETER;
		goto fail;
	}

	if (req == NULL) {
		oplock_request |= INTERNAL_OPEN_ONLY;
	}

	if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
	    && (access_mask & DELETE_ACCESS)
	    && !is_ntfs_stream_smb_fname(smb_fname)) {
		/*
		 * We can't open a file with DELETE access if any of the
		 * streams is open without FILE_SHARE_DELETE
		 */
		status = open_streams_for_delete(conn, smb_fname->base_name);

		if (!NT_STATUS_IS_OK(status)) {
			goto fail;
		}
	}

	if ((access_mask & SEC_FLAG_SYSTEM_SECURITY) &&
			!security_token_has_privilege(get_current_nttok(conn),
					SEC_PRIV_SECURITY)) {
		DEBUG(10, ("create_file_unixpath: open on %s "
			"failed - SEC_FLAG_SYSTEM_SECURITY denied.\n",
			smb_fname_str_dbg(smb_fname)));
		status = NT_STATUS_PRIVILEGE_NOT_HELD;
		goto fail;
	}

	if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
	    && is_ntfs_stream_smb_fname(smb_fname)
	    && (!(private_flags & NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE))) {
		uint32 base_create_disposition;
		struct smb_filename *smb_fname_base = NULL;

		if (create_options & FILE_DIRECTORY_FILE) {
			status = NT_STATUS_NOT_A_DIRECTORY;
			goto fail;
		}

		switch (create_disposition) {
		case FILE_OPEN:
			base_create_disposition = FILE_OPEN;
			break;
		default:
			base_create_disposition = FILE_OPEN_IF;
			break;
		}

		/* Create an smb_filename with stream_name == NULL. */
		smb_fname_base = synthetic_smb_fname(talloc_tos(),
						     smb_fname->base_name,
						     NULL, NULL);
		if (smb_fname_base == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto fail;
		}

		if (SMB_VFS_STAT(conn, smb_fname_base) == -1) {
			DEBUG(10, ("Unable to stat stream: %s\n",
				   smb_fname_str_dbg(smb_fname_base)));
		} else {
			/*
			 * https://bugzilla.samba.org/show_bug.cgi?id=10229
			 * We need to check if the requested access mask
			 * could be used to open the underlying file (if
			 * it existed), as we're passing in zero for the
			 * access mask to the base filename.
			 */
			status = check_base_file_access(conn,
							smb_fname_base,
							access_mask);

			if (!NT_STATUS_IS_OK(status)) {
				DEBUG(10, ("Permission check "
					"for base %s failed: "
					"%s\n", smb_fname->base_name,
					nt_errstr(status)));
				goto fail;
			}
		}

		/* Open the base file. */
		status = create_file_unixpath(conn, NULL, smb_fname_base, 0,
					      FILE_SHARE_READ
					      | FILE_SHARE_WRITE
					      | FILE_SHARE_DELETE,
					      base_create_disposition,
					      0, 0, 0, 0, 0, NULL, NULL,
					      &base_fsp, NULL);
		TALLOC_FREE(smb_fname_base);

		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(10, ("create_file_unixpath for base %s failed: "
				   "%s\n", smb_fname->base_name,
				   nt_errstr(status)));
			goto fail;
		}
		/* we don't need to low level fd */
		fd_close(base_fsp);
	}

	/*
	 * If it's a request for a directory open, deal with it separately.
	 */

	if (create_options & FILE_DIRECTORY_FILE) {

		if (create_options & FILE_NON_DIRECTORY_FILE) {
			status = NT_STATUS_INVALID_PARAMETER;
			goto fail;
		}

		/* Can't open a temp directory. IFS kit test. */
		if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS) &&
		     (file_attributes & FILE_ATTRIBUTE_TEMPORARY)) {
			status = NT_STATUS_INVALID_PARAMETER;
			goto fail;
		}

		/*
		 * We will get a create directory here if the Win32
		 * app specified a security descriptor in the
		 * CreateDirectory() call.
		 */

		oplock_request = 0;
		status = open_directory(
			conn, req, smb_fname, access_mask, share_access,
			create_disposition, create_options, file_attributes,
			&info, &fsp);
	} else {

		/*
		 * Ordinary file case.
		 */

		status = file_new(req, conn, &fsp);
		if(!NT_STATUS_IS_OK(status)) {
			goto fail;
		}

		status = fsp_set_smb_fname(fsp, smb_fname);
		if (!NT_STATUS_IS_OK(status)) {
			goto fail;
		}

		if (base_fsp) {
			/*
			 * We're opening the stream element of a
			 * base_fsp we already opened. Set up the
			 * base_fsp pointer.
			 */
			fsp->base_fsp = base_fsp;
		}

		if (allocation_size) {
			fsp->initial_allocation_size = smb_roundup(fsp->conn,
							allocation_size);
		}

		status = open_file_ntcreate(conn,
					    req,
					    access_mask,
					    share_access,
					    create_disposition,
					    create_options,
					    file_attributes,
					    oplock_request,
					    private_flags,
					    &info,
					    fsp);

		if(!NT_STATUS_IS_OK(status)) {
			file_free(req, fsp);
			fsp = NULL;
		}

		if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {

			/* A stream open never opens a directory */

			if (base_fsp) {
				status = NT_STATUS_FILE_IS_A_DIRECTORY;
				goto fail;
			}

			/*
			 * Fail the open if it was explicitly a non-directory
			 * file.
			 */

			if (create_options & FILE_NON_DIRECTORY_FILE) {
				status = NT_STATUS_FILE_IS_A_DIRECTORY;
				goto fail;
			}

			oplock_request = 0;
			status = open_directory(
				conn, req, smb_fname, access_mask,
				share_access, create_disposition,
				create_options,	file_attributes,
				&info, &fsp);
		}
	}

	if (!NT_STATUS_IS_OK(status)) {
		goto fail;
	}

	fsp->base_fsp = base_fsp;

	if ((ea_list != NULL) &&
	    ((info == FILE_WAS_CREATED) || (info == FILE_WAS_OVERWRITTEN))) {
		status = set_ea(conn, fsp, fsp->fsp_name, ea_list);
		if (!NT_STATUS_IS_OK(status)) {
			goto fail;
		}
	}

	if (!fsp->is_directory && S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
		status = NT_STATUS_ACCESS_DENIED;
		goto fail;
	}

	/* Save the requested allocation size. */
	if ((info == FILE_WAS_CREATED) || (info == FILE_WAS_OVERWRITTEN)) {
		if (allocation_size
		    && (allocation_size > fsp->fsp_name->st.st_ex_size)) {
			fsp->initial_allocation_size = smb_roundup(
				fsp->conn, allocation_size);
			if (fsp->is_directory) {
				/* Can't set allocation size on a directory. */
				status = NT_STATUS_ACCESS_DENIED;
				goto fail;
			}
			if (vfs_allocate_file_space(
				    fsp, fsp->initial_allocation_size) == -1) {
				status = NT_STATUS_DISK_FULL;
				goto fail;
			}
		} else {
			fsp->initial_allocation_size = smb_roundup(
				fsp->conn, (uint64_t)fsp->fsp_name->st.st_ex_size);
		}
	} else {
		fsp->initial_allocation_size = 0;
	}

	if ((info == FILE_WAS_CREATED) && lp_nt_acl_support(SNUM(conn)) &&
				fsp->base_fsp == NULL) {
		if (sd != NULL) {
			/*
			 * According to the MS documentation, the only time the security
			 * descriptor is applied to the opened file is iff we *created* the
			 * file; an existing file stays the same.
			 *
			 * Also, it seems (from observation) that you can open the file with
			 * any access mask but you can still write the sd. We need to override
			 * the granted access before we call set_sd
			 * Patch for bug #2242 from Tom Lackemann <cessnatomny@yahoo.com>.
			 */

			uint32_t sec_info_sent;
			uint32_t saved_access_mask = fsp->access_mask;

			sec_info_sent = get_sec_info(sd);

			fsp->access_mask = FILE_GENERIC_ALL;

			if (sec_info_sent & (SECINFO_OWNER|
						SECINFO_GROUP|
						SECINFO_DACL|
						SECINFO_SACL)) {
				status = set_sd(fsp, sd, sec_info_sent);
			}

			fsp->access_mask = saved_access_mask;

			if (!NT_STATUS_IS_OK(status)) {
				goto fail;
			}
		} else if (lp_inherit_acls(SNUM(conn))) {
			/* Inherit from parent. Errors here are not fatal. */
			status = inherit_new_acl(fsp);
			if (!NT_STATUS_IS_OK(status)) {
				DEBUG(10,("inherit_new_acl: failed for %s with %s\n",
					fsp_str_dbg(fsp),
					nt_errstr(status) ));
			}
		}
	}

	DEBUG(10, ("create_file_unixpath: info=%d\n", info));

	*result = fsp;
	if (pinfo != NULL) {
		*pinfo = info;
	}

	smb_fname->st = fsp->fsp_name->st;

	return NT_STATUS_OK;

 fail:
	DEBUG(10, ("create_file_unixpath: %s\n", nt_errstr(status)));

	if (fsp != NULL) {
		if (base_fsp && fsp->base_fsp == base_fsp) {
			/*
			 * The close_file below will close
			 * fsp->base_fsp.
			 */
			base_fsp = NULL;
		}
		close_file(req, fsp, ERROR_CLOSE);
		fsp = NULL;
	}
	if (base_fsp != NULL) {
		close_file(req, base_fsp, ERROR_CLOSE);
		base_fsp = NULL;
	}
	return status;
}


static void schedule_defer_open(struct share_mode_lock *lck,
				struct timeval request_time,
				struct smb_request *req)
{
	struct deferred_open_record state;

	/* This is a relative time, added to the absolute
	   request_time value to get the absolute timeout time.
	   Note that if this is the second or greater time we enter
	   this codepath for this particular request mid then
	   request_time is left as the absolute time of the *first*
	   time this request mid was processed. This is what allows
	   the request to eventually time out. */

	struct timeval timeout;

	/* Normally the smbd we asked should respond within
	 * OPLOCK_BREAK_TIMEOUT seconds regardless of whether
	 * the client did, give twice the timeout as a safety
	 * measure here in case the other smbd is stuck
	 * somewhere else. */

	timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0);

	/* Nothing actually uses state.delayed_for_oplocks
	   but it's handy to differentiate in debug messages
	   between a 30 second delay due to oplock break, and
	   a 1 second delay for share mode conflicts. */

	state.delayed_for_oplocks = True;
	state.async_open = false;
	state.id = lck->data->id;

	if (!request_timed_out(request_time, timeout)) {
		defer_open(lck, request_time, timeout, req, &state);
	}
}


static void schedule_async_open(struct timeval request_time,
				struct smb_request *req)
{
	struct deferred_open_record state;
	struct timeval timeout;

	timeout = timeval_set(20, 0);

	ZERO_STRUCT(state);
	state.delayed_for_oplocks = false;
	state.async_open = true;

	if (!request_timed_out(request_time, timeout)) {
		defer_open(NULL, request_time, timeout, req, &state);
	}
}


NTSTATUS get_relative_fid_filename(connection_struct *conn,
				   struct smb_request *req,
				   uint16_t root_dir_fid,
				   const struct smb_filename *smb_fname,
				   struct smb_filename **smb_fname_out)
{
	files_struct *dir_fsp;
	char *parent_fname = NULL;
	char *new_base_name = NULL;
	NTSTATUS status;

	if (root_dir_fid == 0 || !smb_fname) {
		status = NT_STATUS_INTERNAL_ERROR;
		goto out;
	}

	dir_fsp = file_fsp(req, root_dir_fid);

	if (dir_fsp == NULL) {
		status = NT_STATUS_INVALID_HANDLE;
		goto out;
	}

	if (is_ntfs_stream_smb_fname(dir_fsp->fsp_name)) {
		status = NT_STATUS_INVALID_HANDLE;
		goto out;
	}

	if (!dir_fsp->is_directory) {

		/*
		 * Check to see if this is a mac fork of some kind.
		 */

		if ((conn->fs_capabilities & FILE_NAMED_STREAMS) &&
		    is_ntfs_stream_smb_fname(smb_fname)) {
			status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
			goto out;
		}

		/*
		  we need to handle the case when we get a
		  relative open relative to a file and the
		  pathname is blank - this is a reopen!
		  (hint from demyn plantenberg)
		*/

		status = NT_STATUS_INVALID_HANDLE;
		goto out;
	}

	if (ISDOT(dir_fsp->fsp_name->base_name)) {
		/*
		 * We're at the toplevel dir, the final file name
		 * must not contain ./, as this is filtered out
		 * normally by srvstr_get_path and unix_convert
		 * explicitly rejects paths containing ./.
		 */
		parent_fname = talloc_strdup(talloc_tos(), "");
		if (parent_fname == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto out;
		}
	} else {
		size_t dir_name_len = strlen(dir_fsp->fsp_name->base_name);

		/*
		 * Copy in the base directory name.
		 */

		parent_fname = talloc_array(talloc_tos(), char,
		    dir_name_len+2);
		if (parent_fname == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto out;
		}
		memcpy(parent_fname, dir_fsp->fsp_name->base_name,
		    dir_name_len+1);

		/*
		 * Ensure it ends in a '/'.
		 * We used TALLOC_SIZE +2 to add space for the '/'.
		 */

		if(dir_name_len
		    && (parent_fname[dir_name_len-1] != '\\')
		    && (parent_fname[dir_name_len-1] != '/')) {
			parent_fname[dir_name_len] = '/';
			parent_fname[dir_name_len+1] = '\0';
		}
	}

	new_base_name = talloc_asprintf(talloc_tos(), "%s%s", parent_fname,
					smb_fname->base_name);
	if (new_base_name == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	status = filename_convert(req,
				conn,
				req->flags2 & FLAGS2_DFS_PATHNAMES,
				new_base_name,
				0,
				NULL,
				smb_fname_out);
	if (!NT_STATUS_IS_OK(status)) {
		goto out;
	}

 out:
	TALLOC_FREE(parent_fname);
	TALLOC_FREE(new_base_name);
	return status;
}


static bool delay_for_oplock(files_struct *fsp,
			     int oplock_request,
			     struct share_mode_lock *lck,
			     bool have_sharing_violation,
			     uint32_t create_disposition)
{
	struct share_mode_data *d = lck->data;
	struct share_mode_entry *entry;
	uint32_t num_non_stat_opens = 0;
	uint32_t i;
	uint16_t break_to;

	if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
		return false;
	}
	for (i=0; i<d->num_share_modes; i++) {
		struct share_mode_entry *e = &d->share_modes[i];
		if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
			continue;
		}
		num_non_stat_opens += 1;

		/*
		 * We found the a non-stat open, which in the exclusive/batch
		 * case will be inspected further down.
		 */
		entry = e;
	}
	if (num_non_stat_opens == 0) {
		/*
		 * Nothing to wait for around
		 */
		return false;
	}
	if (num_non_stat_opens != 1) {
		/*
		 * More than one open around. There can't be any exclusive or
		 * batch left, this is all level2.
		 */
		return false;
	}

	if (server_id_is_disconnected(&entry->pid)) {
		/*
		 * TODO: clean up.
		 * This could be achieved by sending a break message
		 * to ourselves. Special considerations for files
		 * with delete_on_close flag set!
		 *
		 * For now we keep it simple and do not
		 * allow delete on close for durable handles.
		 */
		return false;
	}

	switch (create_disposition) {
	case FILE_SUPERSEDE:
	case FILE_OVERWRITE_IF:
		break_to = NO_OPLOCK;
		break;
	default:
		break_to = LEVEL_II_OPLOCK;
		break;
	}

	if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
		if (share_mode_stale_pid(d, 0)) {
			return false;
		}
		send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
		return true;
	}
	if (have_sharing_violation) {
		/*
		 * Non-batch exclusive is not broken if we have a sharing
		 * violation
		 */
		return false;
	}
	if (LEVEL_II_OPLOCK_TYPE(entry->op_type) &&
	    (break_to == NO_OPLOCK)) {
		if (share_mode_stale_pid(d, 0)) {
			return false;
		}
		DEBUG(10, ("Asynchronously breaking level2 oplock for "
			   "create_disposition=%u\n",
			   (unsigned)create_disposition));
		send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
		return false;
	}
	if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) {
		/*
		 * No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks
		 */
		return false;
	}
	if (share_mode_stale_pid(d, 0)) {
		return false;
	}

	send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
	return true;
}


NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
				       const char *inherit_from_dir,
				       const char *fname,
				       SMB_STRUCT_STAT *psbuf)
{
	struct smb_filename *smb_fname_parent;
	struct smb_filename *smb_fname_cwd = NULL;
	char *saved_dir = NULL;
	TALLOC_CTX *ctx = talloc_tos();
	NTSTATUS status = NT_STATUS_OK;
	int ret;

	smb_fname_parent = synthetic_smb_fname(ctx, inherit_from_dir,
					       NULL, NULL);
	if (smb_fname_parent == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	ret = SMB_VFS_STAT(conn, smb_fname_parent);
	if (ret == -1) {
		status = map_nt_error_from_unix(errno);
		DEBUG(0,("change_dir_owner_to_parent: failed to stat parent "
			 "directory %s. Error was %s\n",
			 smb_fname_str_dbg(smb_fname_parent),
			 strerror(errno)));
		goto out;
	}

	/* We've already done an lstat into psbuf, and we know it's a
	   directory. If we can cd into the directory and the dev/ino
	   are the same then we can safely chown without races as
	   we're locking the directory in place by being in it.  This
	   should work on any UNIX (thanks tridge :-). JRA.
	*/

	saved_dir = vfs_GetWd(ctx,conn);
	if (!saved_dir) {
		status = map_nt_error_from_unix(errno);
		DEBUG(0,("change_dir_owner_to_parent: failed to get "
			 "current working directory. Error was %s\n",
			 strerror(errno)));
		goto out;
	}

	/* Chdir into the new path. */
	if (vfs_ChDir(conn, fname) == -1) {
		status = map_nt_error_from_unix(errno);
		DEBUG(0,("change_dir_owner_to_parent: failed to change "
			 "current working directory to %s. Error "
			 "was %s\n", fname, strerror(errno) ));
		goto chdir;
	}

	smb_fname_cwd = synthetic_smb_fname(ctx, ".", NULL, NULL);
	if (smb_fname_cwd == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto chdir;
	}

	ret = SMB_VFS_STAT(conn, smb_fname_cwd);
	if (ret == -1) {
		status = map_nt_error_from_unix(errno);
		DEBUG(0,("change_dir_owner_to_parent: failed to stat "
			 "directory '.' (%s) Error was %s\n",
			 fname, strerror(errno)));
		goto chdir;
	}

	/* Ensure we're pointing at the same place. */
	if (smb_fname_cwd->st.st_ex_dev != psbuf->st_ex_dev ||
	    smb_fname_cwd->st.st_ex_ino != psbuf->st_ex_ino) {
		DEBUG(0,("change_dir_owner_to_parent: "
			 "device/inode on directory %s changed. "
			 "Refusing to chown !\n", fname ));
		status = NT_STATUS_ACCESS_DENIED;
		goto chdir;
	}

	if (smb_fname_parent->st.st_ex_uid == smb_fname_cwd->st.st_ex_uid) {
		/* Already this uid - no need to change. */
		DEBUG(10,("change_dir_owner_to_parent: directory %s "
			"is already owned by uid %d\n",
			fname,
			(int)smb_fname_cwd->st.st_ex_uid ));
		status = NT_STATUS_OK;
		goto chdir;
	}

	become_root();
	ret = SMB_VFS_LCHOWN(conn, ".", smb_fname_parent->st.st_ex_uid,
			    (gid_t)-1);
	unbecome_root();
	if (ret == -1) {
		status = map_nt_error_from_unix(errno);
		DEBUG(10,("change_dir_owner_to_parent: failed to chown "
			  "directory %s to parent directory uid %u. "
			  "Error was %s\n", fname,
			  (unsigned int)smb_fname_parent->st.st_ex_uid,
			  strerror(errno) ));
	} else {
		DEBUG(10,("change_dir_owner_to_parent: changed ownership of new "
			"directory %s to parent directory uid %u.\n",
			fname, (unsigned int)smb_fname_parent->st.st_ex_uid ));
		/* Ensure the uid entry is updated. */
		psbuf->st_ex_uid = smb_fname_parent->st.st_ex_uid;
	}

 chdir:
	vfs_ChDir(conn,saved_dir);
 out:
	TALLOC_FREE(smb_fname_parent);
	TALLOC_FREE(smb_fname_cwd);
	return status;
}


static NTSTATUS open_file(files_struct *fsp,
			  connection_struct *conn,
			  struct smb_request *req,
			  const char *parent_dir,
			  int flags,
			  mode_t unx_mode,
			  uint32 access_mask, /* client requested access mask. */
			  uint32 open_access_mask, /* what we're actually using in the open. */
			  bool *p_file_created)
{
	struct smb_filename *smb_fname = fsp->fsp_name;
	NTSTATUS status = NT_STATUS_OK;
	int accmode = (flags & O_ACCMODE);
	int local_flags = flags;
	bool file_existed = VALID_STAT(fsp->fsp_name->st);

	fsp->fh->fd = -1;
	errno = EPERM;

	/* Check permissions */

	/*
	 * This code was changed after seeing a client open request
	 * containing the open mode of (DENY_WRITE/read-only) with
	 * the 'create if not exist' bit set. The previous code
	 * would fail to open the file read only on a read-only share
	 * as it was checking the flags parameter  directly against O_RDONLY,
	 * this was failing as the flags parameter was set to O_RDONLY|O_CREAT.
	 * JRA.
	 */

	if (!CAN_WRITE(conn)) {
		/* It's a read-only share - fail if we wanted to write. */
		if(accmode != O_RDONLY || (flags & O_TRUNC) || (flags & O_APPEND)) {
			DEBUG(3,("Permission denied opening %s\n",
				 smb_fname_str_dbg(smb_fname)));
			return NT_STATUS_ACCESS_DENIED;
		}
		if (flags & O_CREAT) {
			/* We don't want to write - but we must make sure that
			   O_CREAT doesn't create the file if we have write
			   access into the directory.
			*/
			flags &= ~(O_CREAT|O_EXCL);
			local_flags &= ~(O_CREAT|O_EXCL);
		}
	}

	/*
	 * This little piece of insanity is inspired by the
	 * fact that an NT client can open a file for O_RDONLY,
	 * but set the create disposition to FILE_EXISTS_TRUNCATE.
	 * If the client *can* write to the file, then it expects to
	 * truncate the file, even though it is opening for readonly.
	 * Quicken uses this stupid trick in backup file creation...
	 * Thanks *greatly* to "David W. Chapman Jr." <dwcjr@inethouston.net>
	 * for helping track this one down. It didn't bite us in 2.0.x
	 * as we always opened files read-write in that release. JRA.
	 */

	if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC)) {
		DEBUG(10,("open_file: truncate requested on read-only open "
			  "for file %s\n", smb_fname_str_dbg(smb_fname)));
		local_flags = (flags & ~O_ACCMODE)|O_RDWR;
	}

	if ((open_access_mask & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ||
	    (!file_existed && (local_flags & O_CREAT)) ||
	    ((local_flags & O_TRUNC) == O_TRUNC) ) {
		const char *wild;
		int ret;

#if defined(O_NONBLOCK) && defined(S_ISFIFO)
		/*
		 * We would block on opening a FIFO with no one else on the
		 * other end. Do what we used to do and add O_NONBLOCK to the
		 * open flags. JRA.
		 */

		if (file_existed && S_ISFIFO(smb_fname->st.st_ex_mode)) {
			local_flags &= ~O_TRUNC; /* Can't truncate a FIFO. */
			local_flags |= O_NONBLOCK;
		}
#endif

		/* Don't create files with Microsoft wildcard characters. */
		if (fsp->base_fsp) {
			/*
			 * wildcard characters are allowed in stream names
			 * only test the basefilename
			 */
			wild = fsp->base_fsp->fsp_name->base_name;
		} else {
			wild = smb_fname->base_name;
		}
		if ((local_flags & O_CREAT) && !file_existed &&
		    ms_has_wild(wild))  {
			return NT_STATUS_OBJECT_NAME_INVALID;
		}

		/* Can we access this file ? */
		if (!fsp->base_fsp) {
			/* Only do this check on non-stream open. */
			if (file_existed) {
				status = smbd_check_access_rights(conn,
						smb_fname,
						false,
						access_mask);
			} else if (local_flags & O_CREAT){
				status = check_parent_access(conn,
						smb_fname,
						SEC_DIR_ADD_FILE);
			} else {
				/* File didn't exist and no O_CREAT. */
				return NT_STATUS_OBJECT_NAME_NOT_FOUND;
			}
			if (!NT_STATUS_IS_OK(status)) {
				DEBUG(10,("open_file: "
					"%s on file "
					"%s returned %s\n",
					file_existed ?
						"smbd_check_access_rights" :
						"check_parent_access",
					smb_fname_str_dbg(smb_fname),
					nt_errstr(status) ));
				return status;
			}
		}

		/* Actually do the open */
		status = fd_open_atomic(conn, fsp, local_flags,
				unx_mode, p_file_created);
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
				 "(flags=%d)\n", smb_fname_str_dbg(smb_fname),
				 nt_errstr(status),local_flags,flags));
			return status;
		}

		ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
		if (ret == -1) {
			/* If we have an fd, this stat should succeed. */
			DEBUG(0,("Error doing fstat on open file %s "
				"(%s)\n",
				smb_fname_str_dbg(smb_fname),
				strerror(errno) ));
			status = map_nt_error_from_unix(errno);
			fd_close(fsp);
			return status;
		}

		if (*p_file_created) {
			/* We created this file. */

			bool need_re_stat = false;
			/* Do all inheritance work after we've
			   done a successful fstat call and filled
			   in the stat struct in fsp->fsp_name. */

			/* Inherit the ACL if required */
			if (lp_inherit_perms(SNUM(conn))) {
				inherit_access_posix_acl(conn, parent_dir,
							 smb_fname->base_name,
							 unx_mode);
				need_re_stat = true;
			}

			/* Change the owner if required. */
			if (lp_inherit_owner(SNUM(conn))) {
				change_file_owner_to_parent(conn, parent_dir,
							    fsp);
				need_re_stat = true;
			}

			if (need_re_stat) {
				ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
				/* If we have an fd, this stat should succeed. */
				if (ret == -1) {
					DEBUG(0,("Error doing fstat on open file %s "
						 "(%s)\n",
						 smb_fname_str_dbg(smb_fname),
						 strerror(errno) ));
				}
			}

			notify_fname(conn, NOTIFY_ACTION_ADDED,
				     FILE_NOTIFY_CHANGE_FILE_NAME,
				     smb_fname->base_name);
		}
	} else {
		fsp->fh->fd = -1; /* What we used to call a stat open. */
		if (!file_existed) {
			/* File must exist for a stat open. */
			return NT_STATUS_OBJECT_NAME_NOT_FOUND;
		}

		status = smbd_check_access_rights(conn,
				smb_fname,
				false,
				access_mask);

		if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
				fsp->posix_open &&
				S_ISLNK(smb_fname->st.st_ex_mode)) {
			/* This is a POSIX stat open for delete
			 * or rename on a symlink that points
			 * nowhere. Allow. */
			DEBUG(10,("open_file: allowing POSIX "
				  "open on bad symlink %s\n",
				  smb_fname_str_dbg(smb_fname)));
			status = NT_STATUS_OK;
		}

		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(10,("open_file: "
				"smbd_check_access_rights on file "
				"%s returned %s\n",
				smb_fname_str_dbg(smb_fname),
				nt_errstr(status) ));
			return status;
		}
	}

	/*
	 * POSIX allows read-only opens of directories. We don't
	 * want to do this (we use a different code path for this)
	 * so catch a directory open and return an EISDIR. JRA.
	 */

	if(S_ISDIR(smb_fname->st.st_ex_mode)) {
		fd_close(fsp);
		errno = EISDIR;
		return NT_STATUS_FILE_IS_A_DIRECTORY;
	}

	fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
	fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
	fsp->file_pid = req ? req->smbpid : 0;
	fsp->can_lock = True;
	fsp->can_read = ((access_mask & FILE_READ_DATA) != 0);
	fsp->can_write =
		CAN_WRITE(conn) &&
		((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0);
	fsp->print_file = NULL;
	fsp->modified = False;
	fsp->sent_oplock_break = NO_BREAK_SENT;
	fsp->is_directory = False;
	if (conn->aio_write_behind_list &&
	    is_in_path(smb_fname->base_name, conn->aio_write_behind_list,
		       conn->case_sensitive)) {
		fsp->aio_write_behind = True;
	}

	fsp->wcp = NULL; /* Write cache pointer. */

	DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
		 conn->session_info->unix_info->unix_name,
		 smb_fname_str_dbg(smb_fname),
		 BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
		 conn->num_files_open));

	errno = 0;
	return NT_STATUS_OK;
}


NTSTATUS fd_open(struct connection_struct *conn,
		 files_struct *fsp,
		 int flags,
		 mode_t mode)
{
	struct smb_filename *smb_fname = fsp->fsp_name;
	NTSTATUS status = NT_STATUS_OK;

#ifdef O_NOFOLLOW
	/*
	 * Never follow symlinks on a POSIX client. The
	 * client should be doing this.
	 */

	if (fsp->posix_open || !lp_symlinks(SNUM(conn))) {
		flags |= O_NOFOLLOW;
	}
#endif

	fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
	if (fsp->fh->fd == -1) {
		int posix_errno = errno;
#ifdef O_NOFOLLOW
#if defined(ENOTSUP) && defined(OSF1)
		/* handle special Tru64 errno */
		if (errno == ENOTSUP) {
			posix_errno = ELOOP;
		}
#endif /* ENOTSUP */
#ifdef EFTYPE
		/* fix broken NetBSD errno */
		if (errno == EFTYPE) {
			posix_errno = ELOOP;
		}
#endif /* EFTYPE */
		/* fix broken FreeBSD errno */
		if (errno == EMLINK) {
			posix_errno = ELOOP;
		}
#endif /* O_NOFOLLOW */
		status = map_nt_error_from_unix(posix_errno);
		if (errno == EMFILE) {
			static time_t last_warned = 0L;

			if (time((time_t *) NULL) > last_warned) {
				DEBUG(0,("Too many open files, unable "
					"to open more!  smbd's max "
					"open files = %d\n",
					lp_max_open_files()));
				last_warned = time((time_t *) NULL);
			}
		}

	}

	DEBUG(10,("fd_open: name %s, flags = 0%o mode = 0%o, fd = %d. %s\n",
		  smb_fname_str_dbg(smb_fname), flags, (int)mode, fsp->fh->fd,
		(fsp->fh->fd == -1) ? strerror(errno) : "" ));

	return status;
}


bool is_stat_open(uint32 access_mask)
{
	const uint32_t stat_open_bits =
		(SYNCHRONIZE_ACCESS|
		 FILE_READ_ATTRIBUTES|
		 FILE_WRITE_ATTRIBUTES);

	return (((access_mask &  stat_open_bits) != 0) &&
		((access_mask & ~stat_open_bits) == 0));
}


static bool file_has_brlocks(files_struct *fsp)
{
	struct byte_range_lock *br_lck;

	br_lck = brl_get_locks_readonly(fsp);
	if (!br_lck)
		return false;

	return (brl_num_locks(br_lck) > 0);
}


static NTSTATUS open_file_ntcreate(connection_struct *conn,
			    struct smb_request *req,
			    uint32 access_mask,		/* access bits (FILE_READ_DATA etc.) */
			    uint32 share_access,	/* share constants (FILE_SHARE_READ etc) */
			    uint32 create_disposition,	/* FILE_OPEN_IF etc. */
			    uint32 create_options,	/* options such as delete on close. */
			    uint32 new_dos_attributes,	/* attributes used for new file. */
			    int oplock_request, 	/* internal Samba oplock codes. */
				 			/* Information (FILE_EXISTS etc.) */
			    uint32_t private_flags,     /* Samba specific flags. */
			    int *pinfo,
			    files_struct *fsp)
{
	struct smb_filename *smb_fname = fsp->fsp_name;
	int flags=0;
	int flags2=0;
	bool file_existed = VALID_STAT(smb_fname->st);
	bool def_acl = False;
	bool posix_open = False;
	bool new_file_created = False;
	bool first_open_attempt = true;
	NTSTATUS fsp_open = NT_STATUS_ACCESS_DENIED;
	mode_t new_unx_mode = (mode_t)0;
	mode_t unx_mode = (mode_t)0;
	int info;
	uint32 existing_dos_attributes = 0;
	struct timeval request_time = timeval_zero();
	struct share_mode_lock *lck = NULL;
	uint32 open_access_mask = access_mask;
	NTSTATUS status;
	char *parent_dir;
	SMB_STRUCT_STAT saved_stat = smb_fname->st;
	struct timespec old_write_time;
	struct file_id id;

	if (conn->printer) {
		/*
		 * Printers are handled completely differently.
		 * Most of the passed parameters are ignored.
		 */

		if (pinfo) {
			*pinfo = FILE_WAS_CREATED;
		}

		DEBUG(10, ("open_file_ntcreate: printer open fname=%s\n",
			   smb_fname_str_dbg(smb_fname)));

		if (!req) {
			DEBUG(0,("open_file_ntcreate: printer open without "
				"an SMB request!\n"));
			return NT_STATUS_INTERNAL_ERROR;
		}

		return print_spool_open(fsp, smb_fname->base_name,
					req->vuid);
	}

	if (!parent_dirname(talloc_tos(), smb_fname->base_name, &parent_dir,
			    NULL)) {
		return NT_STATUS_NO_MEMORY;
	}

	if (new_dos_attributes & FILE_FLAG_POSIX_SEMANTICS) {
		posix_open = True;
		unx_mode = (mode_t)(new_dos_attributes & ~FILE_FLAG_POSIX_SEMANTICS);
		new_dos_attributes = 0;
	} else {
		/* Windows allows a new file to be created and
		   silently removes a FILE_ATTRIBUTE_DIRECTORY
		   sent by the client. Do the same. */

		new_dos_attributes &= ~FILE_ATTRIBUTE_DIRECTORY;

		/* We add FILE_ATTRIBUTE_ARCHIVE to this as this mode is only used if the file is
		 * created new. */
		unx_mode = unix_mode(conn, new_dos_attributes | FILE_ATTRIBUTE_ARCHIVE,
				     smb_fname, parent_dir);
	}

	DEBUG(10, ("open_file_ntcreate: fname=%s, dos_attrs=0x%x "
		   "access_mask=0x%x share_access=0x%x "
		   "create_disposition = 0x%x create_options=0x%x "
		   "unix mode=0%o oplock_request=%d private_flags = 0x%x\n",
		   smb_fname_str_dbg(smb_fname), new_dos_attributes,
		   access_mask, share_access, create_disposition,
		   create_options, (unsigned int)unx_mode, oplock_request,
		   (unsigned int)private_flags));

	if ((req == NULL) && ((oplock_request & INTERNAL_OPEN_ONLY) == 0)) {
		DEBUG(0, ("No smb request but not an internal only open!\n"));
		return NT_STATUS_INTERNAL_ERROR;
	}

	/*
	 * Only non-internal opens can be deferred at all
	 */

	if (req) {
		void *ptr;
		if (get_deferred_open_message_state(req,
				&request_time,
				&ptr)) {
			/* Remember the absolute time of the original
			   request with this mid. We'll use it later to
			   see if this has timed out. */

			/* If it was an async create retry, the file
			   didn't exist. */

			if (is_deferred_open_async(ptr)) {
				SET_STAT_INVALID(smb_fname->st);
				file_existed = false;
			}

			/* Ensure we don't reprocess this message. */
			remove_deferred_open_message_smb(req->sconn, req->mid);

			first_open_attempt = false;
		}
	}

	if (!posix_open) {
		new_dos_attributes &= SAMBA_ATTRIBUTES_MASK;
		if (file_existed) {
			existing_dos_attributes = dos_mode(conn, smb_fname);
		}
	}

	/* ignore any oplock requests if oplocks are disabled */
	if (!lp_oplocks(SNUM(conn)) ||
	    IS_VETO_OPLOCK_PATH(conn, smb_fname->base_name)) {
		/* Mask off everything except the private Samba bits. */
		oplock_request &= SAMBA_PRIVATE_OPLOCK_MASK;
	}

	/* this is for OS/2 long file names - say we don't support them */
	if (!lp_posix_pathnames() && strstr(smb_fname->base_name,".+,;=[].")) {
		/* OS/2 Workplace shell fix may be main code stream in a later
		 * release. */
		DEBUG(5,("open_file_ntcreate: OS/2 long filenames are not "
			 "supported.\n"));
		if (use_nt_status()) {
			return NT_STATUS_OBJECT_NAME_NOT_FOUND;
		}
		return NT_STATUS_DOS(ERRDOS, ERRcannotopen);
	}

	switch( create_disposition ) {
		case FILE_OPEN:
			/* If file exists open. If file doesn't exist error. */
			if (!file_existed) {
				DEBUG(5,("open_file_ntcreate: FILE_OPEN "
					 "requested for file %s and file "
					 "doesn't exist.\n",
					 smb_fname_str_dbg(smb_fname)));
				errno = ENOENT;
				return NT_STATUS_OBJECT_NAME_NOT_FOUND;
			}
			break;

		case FILE_OVERWRITE:
			/* If file exists overwrite. If file doesn't exist
			 * error. */
			if (!file_existed) {
				DEBUG(5,("open_file_ntcreate: FILE_OVERWRITE "
					 "requested for file %s and file "
					 "doesn't exist.\n",
					 smb_fname_str_dbg(smb_fname) ));
				errno = ENOENT;
				return NT_STATUS_OBJECT_NAME_NOT_FOUND;
			}
			break;

		case FILE_CREATE:
			/* If file exists error. If file doesn't exist
			 * create. */
			if (file_existed) {
				DEBUG(5,("open_file_ntcreate: FILE_CREATE "
					 "requested for file %s and file "
					 "already exists.\n",
					 smb_fname_str_dbg(smb_fname)));
				if (S_ISDIR(smb_fname->st.st_ex_mode)) {
					errno = EISDIR;
				} else {
					errno = EEXIST;
				}
				return map_nt_error_from_unix(errno);
			}
			break;

		case FILE_SUPERSEDE:
		case FILE_OVERWRITE_IF:
		case FILE_OPEN_IF:
			break;
		default:
			return NT_STATUS_INVALID_PARAMETER;
	}

	flags2 = disposition_to_open_flags(create_disposition);

	/* We only care about matching attributes on file exists and
	 * overwrite. */

	if (!posix_open && file_existed &&
	    ((create_disposition == FILE_OVERWRITE) ||
	     (create_disposition == FILE_OVERWRITE_IF))) {
		if (!open_match_attributes(conn, existing_dos_attributes,
					   new_dos_attributes,
					   smb_fname->st.st_ex_mode,
					   unx_mode, &new_unx_mode)) {
			DEBUG(5,("open_file_ntcreate: attributes missmatch "
				 "for file %s (%x %x) (0%o, 0%o)\n",
				 smb_fname_str_dbg(smb_fname),
				 existing_dos_attributes,
				 new_dos_attributes,
				 (unsigned int)smb_fname->st.st_ex_mode,
				 (unsigned int)unx_mode ));
			errno = EACCES;
			return NT_STATUS_ACCESS_DENIED;
		}
	}

	status = smbd_calculate_access_mask(conn, smb_fname,
					false,
					access_mask,
					&access_mask);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("open_file_ntcreate: smbd_calculate_access_mask "
			"on file %s returned %s\n",
			smb_fname_str_dbg(smb_fname), nt_errstr(status)));
		return status;
	}

	open_access_mask = access_mask;

	if (flags2 & O_TRUNC) {
		open_access_mask |= FILE_WRITE_DATA; /* This will cause oplock breaks. */
	}

	DEBUG(10, ("open_file_ntcreate: fname=%s, after mapping "
		   "access_mask=0x%x\n", smb_fname_str_dbg(smb_fname),
		    access_mask));

	/*
	 * Note that we ignore the append flag as append does not
	 * mean the same thing under DOS and Unix.
	 */

	flags = calculate_open_access_flags(access_mask, oplock_request,
					    private_flags);

	/*
	 * Currently we only look at FILE_WRITE_THROUGH for create options.
	 */

#if defined(O_SYNC)
	if ((create_options & FILE_WRITE_THROUGH) && lp_strict_sync(SNUM(conn))) {
		flags2 |= O_SYNC;
	}
#endif /* O_SYNC */

	if (posix_open && (access_mask & FILE_APPEND_DATA)) {
		flags2 |= O_APPEND;
	}

	if (!posix_open && !CAN_WRITE(conn)) {
		/*
		 * We should really return a permission denied error if either
		 * O_CREAT or O_TRUNC are set, but for compatibility with
		 * older versions of Samba we just AND them out.
		 */
		flags2 &= ~(O_CREAT|O_TRUNC);
	}

	if (first_open_attempt && lp_kernel_oplocks(SNUM(conn))) {
		/*
		 * With kernel oplocks the open breaking an oplock
		 * blocks until the oplock holder has given up the
		 * oplock or closed the file. We prevent this by first
		 * trying to open the file with O_NONBLOCK (see "man
		 * fcntl" on Linux). For the second try, triggered by
		 * an oplock break response, we do not need this
		 * anymore.
		 *
		 * This is true under the assumption that only Samba
		 * requests kernel oplocks. Once someone else like
		 * NFSv4 starts to use that API, we will have to
		 * modify this by communicating with the NFSv4 server.
		 */
		flags2 |= O_NONBLOCK;
	}

	/*
	 * Ensure we can't write on a read-only share or file.
	 */

	if (flags != O_RDONLY && file_existed &&
	    (!CAN_WRITE(conn) || IS_DOS_READONLY(existing_dos_attributes))) {
		DEBUG(5,("open_file_ntcreate: write access requested for "
			 "file %s on read only %s\n",
			 smb_fname_str_dbg(smb_fname),
			 !CAN_WRITE(conn) ? "share" : "file" ));
		errno = EACCES;
		return NT_STATUS_ACCESS_DENIED;
	}

	fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
	fsp->share_access = share_access;
	fsp->fh->private_options = private_flags;
	fsp->access_mask = open_access_mask; /* We change this to the
					      * requested access_mask after
					      * the open is done. */
	fsp->posix_open = posix_open;

	/* Ensure no SAMBA_PRIVATE bits can be set. */
	fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);

	if (timeval_is_zero(&request_time)) {
		request_time = fsp->open_time;
	}

	/*
	 * Ensure we pay attention to default ACLs on directories if required.
	 */

        if ((flags2 & O_CREAT) && lp_inherit_acls(SNUM(conn)) &&
	    (def_acl = directory_has_default_acl(conn, parent_dir))) {
		unx_mode = (0777 & lp_create_mask(SNUM(conn)));
	}

	DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o, "
		"access_mask = 0x%x, open_access_mask = 0x%x\n",
		 (unsigned int)flags, (unsigned int)flags2,
		 (unsigned int)unx_mode, (unsigned int)access_mask,
		 (unsigned int)open_access_mask));

	fsp_open = open_file(fsp, conn, req, parent_dir,
			     flags|flags2, unx_mode, access_mask,
			     open_access_mask, &new_file_created);

	if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_NETWORK_BUSY)) {
		struct deferred_open_record state;

		/*
		 * EWOULDBLOCK/EAGAIN maps to NETWORK_BUSY.
		 */
		if (file_existed && S_ISFIFO(fsp->fsp_name->st.st_ex_mode)) {
			DEBUG(10, ("FIFO busy\n"));
			return NT_STATUS_NETWORK_BUSY;
		}
		if (req == NULL) {
			DEBUG(10, ("Internal open busy\n"));
			return NT_STATUS_NETWORK_BUSY;
		}

		/*
		 * From here on we assume this is an oplock break triggered
		 */

		lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
		if (lck == NULL) {
			state.delayed_for_oplocks = false;
			state.async_open = false;
			state.id = fsp->file_id;
			defer_open(NULL, request_time, timeval_set(0, 0),
				   req, &state);
			DEBUG(10, ("No share mode lock found after "
				   "EWOULDBLOCK, retrying sync\n"));
			return NT_STATUS_SHARING_VIOLATION;
		}

		if (!validate_oplock_types(lck)) {
			smb_panic("validate_oplock_types failed");
		}

		if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) {
			schedule_defer_open(lck, request_time, req);
			TALLOC_FREE(lck);
			DEBUG(10, ("Sent oplock break request to kernel "
				   "oplock holder\n"));
			return NT_STATUS_SHARING_VIOLATION;
		}

		/*
		 * No oplock from Samba around. Immediately retry with
		 * a blocking open.
		 */
		state.delayed_for_oplocks = false;
		state.async_open = false;
		state.id = lck->data->id;
		defer_open(lck, request_time, timeval_set(0, 0), req, &state);
		TALLOC_FREE(lck);
		DEBUG(10, ("No Samba oplock around after EWOULDBLOCK. "
			   "Retrying sync\n"));
		return NT_STATUS_SHARING_VIOLATION;
	}

	if (!NT_STATUS_IS_OK(fsp_open)) {
		if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {
			schedule_async_open(request_time, req);
		}
		return fsp_open;
	}

	if (file_existed && !check_same_dev_ino(&saved_stat, &smb_fname->st)) {
		/*
		 * The file did exist, but some other (local or NFS)
		 * process either renamed/unlinked and re-created the
		 * file with different dev/ino after we walked the path,
		 * but before we did the open. We could retry the
		 * open but it's a rare enough case it's easier to
		 * just fail the open to prevent creating any problems
		 * in the open file db having the wrong dev/ino key.
		 */
		fd_close(fsp);
		DEBUG(1,("open_file_ntcreate: file %s - dev/ino mismatch. "
			"Old (dev=0x%llu, ino =0x%llu). "
			"New (dev=0x%llu, ino=0x%llu). Failing open "
			" with NT_STATUS_ACCESS_DENIED.\n",
			 smb_fname_str_dbg(smb_fname),
			 (unsigned long long)saved_stat.st_ex_dev,
			 (unsigned long long)saved_stat.st_ex_ino,
			 (unsigned long long)smb_fname->st.st_ex_dev,
			 (unsigned long long)smb_fname->st.st_ex_ino));
		return NT_STATUS_ACCESS_DENIED;
	}

	old_write_time = smb_fname->st.st_ex_mtime;

	/*
	 * Deal with the race condition where two smbd's detect the
	 * file doesn't exist and do the create at the same time. One
	 * of them will win and set a share mode, the other (ie. this
	 * one) should check if the requested share mode for this
	 * create is allowed.
	 */

	/*
	 * Now the file exists and fsp is successfully opened,
	 * fsp->dev and fsp->inode are valid and should replace the
	 * dev=0,inode=0 from a non existent file. Spotted by
	 * Nadav Danieli <nadavd@exanet.com>. JRA.
	 */

	id = fsp->file_id;

	lck = get_share_mode_lock(talloc_tos(), id,
				  conn->connectpath,
				  smb_fname, &old_write_time);

	if (lck == NULL) {
		DEBUG(0, ("open_file_ntcreate: Could not get share "
			  "mode lock for %s\n",
			  smb_fname_str_dbg(smb_fname)));
		fd_close(fsp);
		return NT_STATUS_SHARING_VIOLATION;
	}

	/* Get the types we need to examine. */
	if (!validate_oplock_types(lck)) {
		smb_panic("validate_oplock_types failed");
	}

	if (has_delete_on_close(lck, fsp->name_hash)) {
		TALLOC_FREE(lck);
		fd_close(fsp);
		return NT_STATUS_DELETE_PENDING;
	}

	status = open_mode_check(conn, lck,
				 access_mask, share_access);

	if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
	    (lck->data->num_share_modes > 0)) {
		/*
		 * This comes from ancient times out of open_mode_check. I
		 * have no clue whether this is still necessary. I can't think
		 * of a case where this would actually matter further down in
		 * this function. I leave it here for further investigation
		 * :-)
		 */
		file_existed = true;
	}

	if ((req != NULL) &&
	    delay_for_oplock(
		    fsp, oplock_request, lck,
		    NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
		    create_disposition)) {
		schedule_defer_open(lck, request_time, req);
		TALLOC_FREE(lck);
		fd_close(fsp);
		return NT_STATUS_SHARING_VIOLATION;
	}

	if (!NT_STATUS_IS_OK(status)) {
		uint32 can_access_mask;
		bool can_access = True;

		SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION));

		/* Check if this can be done with the deny_dos and fcb
		 * calls. */
		if (private_flags &
		    (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS|
		     NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
			if (req == NULL) {
				DEBUG(0, ("DOS open without an SMB "
					  "request!\n"));
				TALLOC_FREE(lck);
				fd_close(fsp);
				return NT_STATUS_INTERNAL_ERROR;
			}

			/* Use the client requested access mask here,
			 * not the one we open with. */
			status = fcb_or_dos_open(req,
						 conn,
						 fsp,
						 smb_fname,
						 id,
						 req->smbpid,
						 req->vuid,
						 access_mask,
						 share_access,
						 create_options);

			if (NT_STATUS_IS_OK(status)) {
				TALLOC_FREE(lck);
				if (pinfo) {
					*pinfo = FILE_WAS_OPENED;
				}
				return NT_STATUS_OK;
			}
		}

		/*
		 * This next line is a subtlety we need for
		 * MS-Access. If a file open will fail due to share
		 * permissions and also for security (access) reasons,
		 * we need to return the access failed error, not the
		 * share error. We can't open the file due to kernel
		 * oplock deadlock (it's possible we failed above on
		 * the open_mode_check()) so use a userspace check.
		 */

		if (flags & O_RDWR) {
			can_access_mask = FILE_READ_DATA|FILE_WRITE_DATA;
		} else if (flags & O_WRONLY) {
			can_access_mask = FILE_WRITE_DATA;
		} else {
			can_access_mask = FILE_READ_DATA;
		}

		if (((can_access_mask & FILE_WRITE_DATA) &&
		     !CAN_WRITE(conn)) ||
		    !NT_STATUS_IS_OK(smbd_check_access_rights(conn,
							      smb_fname,
							      false,
							      can_access_mask))) {
			can_access = False;
		}

		/*
		 * If we're returning a share violation, ensure we
		 * cope with the braindead 1 second delay (SMB1 only).
		 */

		if (!(oplock_request & INTERNAL_OPEN_ONLY) &&
		    !conn->sconn->using_smb2 &&
		    lp_defer_sharing_violations()) {
			struct timeval timeout;
			struct deferred_open_record state;
			int timeout_usecs;

			/* this is a hack to speed up torture tests
			   in 'make test' */
			timeout_usecs = lp_parm_int(SNUM(conn),
						    "smbd","sharedelay",
						    SHARING_VIOLATION_USEC_WAIT);

			/* This is a relative time, added to the absolute
			   request_time value to get the absolute timeout time.
			   Note that if this is the second or greater time we enter
			   this codepath for this particular request mid then
			   request_time is left as the absolute time of the *first*
			   time this request mid was processed. This is what allows
			   the request to eventually time out. */

			timeout = timeval_set(0, timeout_usecs);

			/* Nothing actually uses state.delayed_for_oplocks
			   but it's handy to differentiate in debug messages
			   between a 30 second delay due to oplock break, and
			   a 1 second delay for share mode conflicts. */

			state.delayed_for_oplocks = False;
			state.async_open = false;
			state.id = id;

			if ((req != NULL)
			    && !request_timed_out(request_time,
						  timeout)) {
				defer_open(lck, request_time, timeout,
					   req, &state);
			}
		}

		TALLOC_FREE(lck);
		fd_close(fsp);
		if (can_access) {
			/*
			 * We have detected a sharing violation here
			 * so return the correct error code
			 */
			status = NT_STATUS_SHARING_VIOLATION;
		} else {
			status = NT_STATUS_ACCESS_DENIED;
		}
		return status;
	}

	grant_fsp_oplock_type(fsp, lck, oplock_request);

	/*
	 * We have the share entry *locked*.....
	 */

	/* Delete streams if create_disposition requires it */
	if (!new_file_created && clear_ads(create_disposition) &&
	    !is_ntfs_stream_smb_fname(smb_fname)) {
		status = delete_all_streams(conn, smb_fname->base_name);
		if (!NT_STATUS_IS_OK(status)) {
			TALLOC_FREE(lck);
			fd_close(fsp);
			return status;
		}
	}

	/* note that we ignore failure for the following. It is
           basically a hack for NFS, and NFS will never set one of
           these only read them. Nobody but Samba can ever set a deny
           mode and we have already checked our more authoritative
           locking database for permission to set this deny mode. If
           the kernel refuses the operations then the kernel is wrong.
	   note that GPFS supports it as well - jmcd */

	if (fsp->fh->fd != -1 && lp_kernel_share_modes(SNUM(conn))) {
		int ret_flock;
		ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, share_access, access_mask);
		if(ret_flock == -1 ){

			TALLOC_FREE(lck);
			fd_close(fsp);

			return NT_STATUS_SHARING_VIOLATION;
		}
	}

	/*
	 * At this point onwards, we can guarantee that the share entry
	 * is locked, whether we created the file or not, and that the
	 * deny mode is compatible with all current opens.
	 */

	/*
	 * According to Samba4, SEC_FILE_READ_ATTRIBUTE is always granted,
	 * but we don't have to store this - just ignore it on access check.
	 */
	if (conn->sconn->using_smb2) {
		/*
		 * SMB2 doesn't return it (according to Microsoft tests).
		 * Test Case: TestSuite_ScenarioNo009GrantedAccessTestS0
		 * File created with access = 0x7 (Read, Write, Delete)
		 * Query Info on file returns 0x87 (Read, Write, Delete, Read Attributes)
		 */
		fsp->access_mask = access_mask;
	} else {
		/* But SMB1 does. */
		fsp->access_mask = access_mask | FILE_READ_ATTRIBUTES;
	}

	if (file_existed) {
		/* stat opens on existing files don't get oplocks. */
		if (is_stat_open(open_access_mask)) {
			fsp->oplock_type = NO_OPLOCK;
		}
	}

	if (new_file_created) {
		info = FILE_WAS_CREATED;
	} else {
		if (flags2 & O_TRUNC) {
			info = FILE_WAS_OVERWRITTEN;
		} else {
			info = FILE_WAS_OPENED;
		}
	}

	if (pinfo) {
		*pinfo = info;
	}

	/*
	 * Setup the oplock info in both the shared memory and
	 * file structs.
	 */

	status = set_file_oplock(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		/*
		 * Could not get the kernel oplock
		 */
		fsp->oplock_type = NO_OPLOCK;
	}

	if (!set_share_mode(lck, fsp, get_current_uid(conn),
			    req ? req->mid : 0,
			    fsp->oplock_type)) {
		TALLOC_FREE(lck);
		fd_close(fsp);
		return NT_STATUS_NO_MEMORY;
	}

	/* Handle strange delete on close create semantics. */
	if (create_options & FILE_DELETE_ON_CLOSE) {

		status = can_set_delete_on_close(fsp, new_dos_attributes);

		if (!NT_STATUS_IS_OK(status)) {
			/* Remember to delete the mode we just added. */
			del_share_mode(lck, fsp);
			TALLOC_FREE(lck);
			fd_close(fsp);
			return status;
		}
		/* Note that here we set the *inital* delete on close flag,
		   not the regular one. The magic gets handled in close. */
		fsp->initial_delete_on_close = True;
	}

	if (info != FILE_WAS_OPENED) {
		/* Files should be initially set as archive */
		if (lp_map_archive(SNUM(conn)) ||
		    lp_store_dos_attributes(SNUM(conn))) {
			if (!posix_open) {
				if (file_set_dosmode(conn, smb_fname,
					    new_dos_attributes | FILE_ATTRIBUTE_ARCHIVE,
					    parent_dir, true) == 0) {
					unx_mode = smb_fname->st.st_ex_mode;
				}
			}
		}
	}

	/* Determine sparse flag. */
	if (posix_open) {
		/* POSIX opens are sparse by default. */
		fsp->is_sparse = true;
	} else {
		fsp->is_sparse = (file_existed &&
			(existing_dos_attributes & FILE_ATTRIBUTE_SPARSE));
	}

	/*
	 * Take care of inherited ACLs on created files - if default ACL not
	 * selected.
	 */

	if (!posix_open && new_file_created && !def_acl) {

		int saved_errno = errno; /* We might get ENOSYS in the next
					  * call.. */

		if (SMB_VFS_FCHMOD_ACL(fsp, unx_mode) == -1 &&
		    errno == ENOSYS) {
			errno = saved_errno; /* Ignore ENOSYS */
		}

	} else if (new_unx_mode) {

		int ret = -1;

		/* Attributes need changing. File already existed. */

		{
			int saved_errno = errno; /* We might get ENOSYS in the
						  * next call.. */
			ret = SMB_VFS_FCHMOD_ACL(fsp, new_unx_mode);

			if (ret == -1 && errno == ENOSYS) {
				errno = saved_errno; /* Ignore ENOSYS */
			} else {
				DEBUG(5, ("open_file_ntcreate: reset "
					  "attributes of file %s to 0%o\n",
					  smb_fname_str_dbg(smb_fname),
					  (unsigned int)new_unx_mode));
				ret = 0; /* Don't do the fchmod below. */
			}
		}

		if ((ret == -1) &&
		    (SMB_VFS_FCHMOD(fsp, new_unx_mode) == -1))
			DEBUG(5, ("open_file_ntcreate: failed to reset "
				  "attributes of file %s to 0%o\n",
				  smb_fname_str_dbg(smb_fname),
				  (unsigned int)new_unx_mode));
	}

	TALLOC_FREE(lck);

	return NT_STATUS_OK;
}


static bool request_timed_out(struct timeval request_time,
			      struct timeval timeout)
{
	struct timeval now, end_time;
	GetTimeOfDay(&now);
	end_time = timeval_sum(&request_time, &timeout);
	return (timeval_compare(&end_time, &now) < 0);
}


static NTSTATUS check_base_file_access(struct connection_struct *conn,
				struct smb_filename *smb_fname,
				uint32_t access_mask)
{
	NTSTATUS status;

	status = smbd_calculate_access_mask(conn, smb_fname,
					false,
					access_mask,
					&access_mask);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("smbd_calculate_access_mask "
			"on file %s returned %s\n",
			smb_fname_str_dbg(smb_fname),
			nt_errstr(status)));
		return status;
	}

	if (access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) {
		uint32_t dosattrs;
		if (!CAN_WRITE(conn)) {
			return NT_STATUS_ACCESS_DENIED;
		}
		dosattrs = dos_mode(conn, smb_fname);
		if (IS_DOS_READONLY(dosattrs)) {
			return NT_STATUS_ACCESS_DENIED;
		}
	}

	return smbd_check_access_rights(conn,
					smb_fname,
					false,
					access_mask);
}


static bool parent_override_delete(connection_struct *conn,
					const struct smb_filename *smb_fname,
					uint32_t access_mask,
					uint32_t rejected_mask)
{
	if ((access_mask & DELETE_ACCESS) &&
		    (rejected_mask & DELETE_ACCESS) &&
		    can_delete_file_in_directory(conn, smb_fname)) {
		return true;
	}
	return false;
}


NTSTATUS create_directory(connection_struct *conn, struct smb_request *req,
			  struct smb_filename *smb_dname)
{
	NTSTATUS status;
	files_struct *fsp;

	status = SMB_VFS_CREATE_FILE(
		conn,					/* conn */
		req,					/* req */
		0,					/* root_dir_fid */
		smb_dname,				/* fname */
		FILE_READ_ATTRIBUTES,			/* access_mask */
		FILE_SHARE_NONE,			/* share_access */
		FILE_CREATE,				/* create_disposition*/
		FILE_DIRECTORY_FILE,			/* create_options */
		FILE_ATTRIBUTE_DIRECTORY,		/* file_attributes */
		0,					/* oplock_request */
		0,					/* allocation_size */
		0,					/* private_flags */
		NULL,					/* sd */
		NULL,					/* ea_list */
		&fsp,					/* result */
		NULL);					/* pinfo */

	if (NT_STATUS_IS_OK(status)) {
		close_file(req, fsp, NORMAL_CLOSE);
	}

	return status;
}


NTSTATUS open_file_fchmod(connection_struct *conn,
			  struct smb_filename *smb_fname,
			  files_struct **result)
{
	if (!VALID_STAT(smb_fname->st)) {
		return NT_STATUS_INVALID_PARAMETER;
	}

        return SMB_VFS_CREATE_FILE(
		conn,					/* conn */
		NULL,					/* req */
		0,					/* root_dir_fid */
		smb_fname,				/* fname */
		FILE_WRITE_DATA,			/* access_mask */
		(FILE_SHARE_READ | FILE_SHARE_WRITE |	/* share_access */
		    FILE_SHARE_DELETE),
		FILE_OPEN,				/* create_disposition*/
		0,					/* create_options */
		0,					/* file_attributes */
		INTERNAL_OPEN_ONLY,			/* oplock_request */
		0,					/* allocation_size */
		0,					/* private_flags */
		NULL,					/* sd */
		NULL,					/* ea_list */
		result,					/* result */
		NULL);					/* pinfo */
}


static bool has_delete_on_close(struct share_mode_lock *lck,
				uint32_t name_hash)
{
	struct share_mode_data *d = lck->data;
	uint32_t i;

	if (d->num_share_modes == 0) {
		return false;
	}
	if (!is_delete_on_close_set(lck, name_hash)) {
		return false;
	}
	for (i=0; i<d->num_share_modes; i++) {
		if (!share_mode_stale_pid(d, i)) {
			return true;
		}
	}
	return false;
}


static bool clear_ads(uint32_t create_disposition)
{
	bool ret = false;

	switch (create_disposition) {
	case FILE_SUPERSEDE:
	case FILE_OVERWRITE_IF:
	case FILE_OVERWRITE:
		ret = true;
		break;
	default:
		break;
	}
	return ret;
}


static NTSTATUS mkdir_internal(connection_struct *conn,
			       struct smb_filename *smb_dname,
			       uint32 file_attributes)
{
	mode_t mode;
	char *parent_dir = NULL;
	NTSTATUS status;
	bool posix_open = false;
	bool need_re_stat = false;
	uint32_t access_mask = SEC_DIR_ADD_SUBDIR;

	if (!CAN_WRITE(conn) || (access_mask & ~(conn->share_access))) {
		DEBUG(5,("mkdir_internal: failing share access "
			 "%s\n", lp_servicename(talloc_tos(), SNUM(conn))));
		return NT_STATUS_ACCESS_DENIED;
	}

	if (!parent_dirname(talloc_tos(), smb_dname->base_name, &parent_dir,
			    NULL)) {
		return NT_STATUS_NO_MEMORY;
	}

	if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) {
		posix_open = true;
		mode = (mode_t)(file_attributes & ~FILE_FLAG_POSIX_SEMANTICS);
	} else {
		mode = unix_mode(conn, FILE_ATTRIBUTE_DIRECTORY, smb_dname, parent_dir);
	}

	status = check_parent_access(conn,
					smb_dname,
					access_mask);
	if(!NT_STATUS_IS_OK(status)) {
		DEBUG(5,("mkdir_internal: check_parent_access "
			"on directory %s for path %s returned %s\n",
			parent_dir,
			smb_dname->base_name,
			nt_errstr(status) ));
		return status;
	}

	if (SMB_VFS_MKDIR(conn, smb_dname->base_name, mode) != 0) {
		return map_nt_error_from_unix(errno);
	}

	/* Ensure we're checking for a symlink here.... */
	/* We don't want to get caught by a symlink racer. */

	if (SMB_VFS_LSTAT(conn, smb_dname) == -1) {
		DEBUG(2, ("Could not stat directory '%s' just created: %s\n",
			  smb_fname_str_dbg(smb_dname), strerror(errno)));
		return map_nt_error_from_unix(errno);
	}

	if (!S_ISDIR(smb_dname->st.st_ex_mode)) {
		DEBUG(0, ("Directory '%s' just created is not a directory !\n",
			  smb_fname_str_dbg(smb_dname)));
		return NT_STATUS_NOT_A_DIRECTORY;
	}

	if (lp_store_dos_attributes(SNUM(conn))) {
		if (!posix_open) {
			file_set_dosmode(conn, smb_dname,
					 file_attributes | FILE_ATTRIBUTE_DIRECTORY,
					 parent_dir, true);
		}
	}

	if (lp_inherit_perms(SNUM(conn))) {
		inherit_access_posix_acl(conn, parent_dir,
					 smb_dname->base_name, mode);
		need_re_stat = true;
	}

	if (!posix_open) {
		/*
		 * Check if high bits should have been set,
		 * then (if bits are missing): add them.
		 * Consider bits automagically set by UNIX, i.e. SGID bit from parent
		 * dir.
		 */
		if ((mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) &&
		    (mode & ~smb_dname->st.st_ex_mode)) {
			SMB_VFS_CHMOD(conn, smb_dname->base_name,
				      (smb_dname->st.st_ex_mode |
					  (mode & ~smb_dname->st.st_ex_mode)));
			need_re_stat = true;
		}
	}

	/* Change the owner if required. */
	if (lp_inherit_owner(SNUM(conn))) {
		change_dir_owner_to_parent(conn, parent_dir,
					   smb_dname->base_name,
					   &smb_dname->st);
		need_re_stat = true;
	}

	if (need_re_stat) {
		if (SMB_VFS_LSTAT(conn, smb_dname) == -1) {
			DEBUG(2, ("Could not stat directory '%s' just created: %s\n",
			  smb_fname_str_dbg(smb_dname), strerror(errno)));
			return map_nt_error_from_unix(errno);
		}
	}

	notify_fname(conn, NOTIFY_ACTION_ADDED, FILE_NOTIFY_CHANGE_DIR_NAME,
		     smb_dname->base_name);

	return NT_STATUS_OK;
}


static void validate_my_share_entries(struct smbd_server_connection *sconn,
				      int num,
				      struct share_mode_entry *share_entry)
{
	struct server_id self = messaging_server_id(sconn->msg_ctx);
	files_struct *fsp;

	if (!serverid_equal(&self, &share_entry->pid)) {
		return;
	}

	if (!is_valid_share_mode_entry(share_entry)) {
		return;
	}

	fsp = file_find_dif(sconn, share_entry->id,
			    share_entry->share_file_id);
	if (!fsp) {
		DEBUG(0,("validate_my_share_entries: PANIC : %s\n",
			 share_mode_str(talloc_tos(), num, share_entry) ));
		smb_panic("validate_my_share_entries: Cannot match a "
			  "share entry with an open file\n");
	}

	if (((uint16)fsp->oplock_type) != share_entry->op_type) {
		goto panic;
	}

	return;

 panic:
	{
		char *str;
		DEBUG(0,("validate_my_share_entries: PANIC : %s\n",
			 share_mode_str(talloc_tos(), num, share_entry) ));
		str = talloc_asprintf(talloc_tos(),
			"validate_my_share_entries: "
			"file %s, oplock_type = 0x%x, op_type = 0x%x\n",
			 fsp->fsp_name->base_name,
			 (unsigned int)fsp->oplock_type,
			 (unsigned int)share_entry->op_type );
		smb_panic(str);
	}
}