ProcXkbUseExtension(ClientPtr client)
{
    REQUEST(xkbUseExtensionReq);
    xkbUseExtensionReply	rep;
    register int n;
    int	supported;

    REQUEST_SIZE_MATCH(xkbUseExtensionReq);
    if (stuff->wantedMajor != XkbMajorVersion) {
	/* pre-release version 0.65 is compatible with 1.00 */
	supported= ((XkbMajorVersion==1)&&
		    (stuff->wantedMajor==0)&&(stuff->wantedMinor==65));
    }
    else supported = 1;

    if ((supported) && (!(client->xkbClientFlags&_XkbClientInitialized))) {
	client->xkbClientFlags= _XkbClientInitialized;
	client->vMajor= stuff->wantedMajor;
	client->vMinor= stuff->wantedMinor;
    }
    else if (xkbDebugFlags&0x1) {
	ErrorF("[xkb] Rejecting client %d (0x%lx) (wants %d.%02d, have %d.%02d)\n",
					client->index,
					(long)client->clientAsMask,
					stuff->wantedMajor,stuff->wantedMinor,
					XkbMajorVersion,XkbMinorVersion);
    }
    rep.type = X_Reply;
    rep.supported = supported;
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    rep.serverMajor = XkbMajorVersion;
    rep.serverMinor = XkbMinorVersion;
    if ( client->swapped ) {
	swaps(&rep.sequenceNumber, n);
	swaps(&rep.serverMajor, n);
	swaps(&rep.serverMinor, n);
    }
    WriteToClient(client,SIZEOF(xkbUseExtensionReply), (char *)&rep);
    return client->noClientException;
}


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.

XkbComputeGetIndicatorMapReplySize(
    XkbIndicatorPtr		indicators,
    xkbGetIndicatorMapReply	*rep)
{
register int 	i,bit;
int		nIndicators;

    rep->realIndicators = indicators->phys_indicators;
    for (i=nIndicators=0,bit=1;i<XkbNumIndicators;i++,bit<<=1) {
	if (rep->which&bit)
	    nIndicators++;
    }
    rep->length = (nIndicators*SIZEOF(xkbIndicatorMapWireDesc))/4;
    return Success;
}

A flaw was found in the Xorg-x11-server. The specific flaw exists within the handling of ProcXkbSetDeviceInfo requests. The issue results from the lack of proper validation of user-supplied data, which can result in a memory access past the end of an allocated buffer. This flaw allows an attacker to escalate privileges and execute arbitrary code in the context of root.


ProcXkbSetControls(ClientPtr client)
{
    DeviceIntPtr 	dev, tmpd;
    XkbSrvInfoPtr	xkbi;
    XkbControlsPtr	ctrl;
    XkbControlsRec	new,old;
    xkbControlsNotify	cn;
    XkbEventCauseRec	cause;
    XkbSrvLedInfoPtr	sli;

    REQUEST(xkbSetControlsReq);
    REQUEST_SIZE_MATCH(xkbSetControlsReq);

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

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixManageAccess);
    CHK_MASK_LEGAL(0x01, stuff->changeCtrls, XkbAllControlsMask);

    for (tmpd = inputInfo.keyboard; tmpd; tmpd = tmpd->next) {
        if ((dev == inputInfo.keyboard && tmpd->key && tmpd->coreEvents) ||
            tmpd == dev) {

            xkbi = tmpd->key->xkbInfo;
            ctrl = xkbi->desc->ctrls;
            new = *ctrl;
            XkbSetCauseXkbReq(&cause, X_kbSetControls, client);

            if (stuff->changeCtrls & XkbInternalModsMask) {
                CHK_MASK_MATCH(0x02, stuff->affectInternalMods,
                               stuff->internalMods);
                CHK_MASK_MATCH(0x03, stuff->affectInternalVMods,
                               stuff->internalVMods);

                new.internal.real_mods &= ~(stuff->affectInternalMods);
                new.internal.real_mods |= (stuff->affectInternalMods &
                                           stuff->internalMods);
                new.internal.vmods &= ~(stuff->affectInternalVMods);
                new.internal.vmods |= (stuff->affectInternalVMods &
                                       stuff->internalVMods);
                new.internal.mask = new.internal.real_mods |
                                    XkbMaskForVMask(xkbi->desc,
                                                    new.internal.vmods);
            }

            if (stuff->changeCtrls & XkbIgnoreLockModsMask) {
                CHK_MASK_MATCH(0x4, stuff->affectIgnoreLockMods,
                               stuff->ignoreLockMods);
                CHK_MASK_MATCH(0x5, stuff->affectIgnoreLockVMods,
                               stuff->ignoreLockVMods);

                new.ignore_lock.real_mods &= ~(stuff->affectIgnoreLockMods);
                new.ignore_lock.real_mods |= (stuff->affectIgnoreLockMods &
                                              stuff->ignoreLockMods);
                new.ignore_lock.vmods &= ~(stuff->affectIgnoreLockVMods);
                new.ignore_lock.vmods |= (stuff->affectIgnoreLockVMods &
                                          stuff->ignoreLockVMods);
                new.ignore_lock.mask = new.ignore_lock.real_mods |
                                       XkbMaskForVMask(xkbi->desc,
                                                       new.ignore_lock.vmods);
            }

            CHK_MASK_MATCH(0x06, stuff->affectEnabledCtrls,
                           stuff->enabledCtrls);
            if (stuff->affectEnabledCtrls) {
                CHK_MASK_LEGAL(0x07, stuff->affectEnabledCtrls,
                               XkbAllBooleanCtrlsMask);

                new.enabled_ctrls &= ~(stuff->affectEnabledCtrls);
                new.enabled_ctrls |= (stuff->affectEnabledCtrls &
                                      stuff->enabledCtrls);
            }

            if (stuff->changeCtrls & XkbRepeatKeysMask) {
                if (stuff->repeatDelay < 1 || stuff->repeatInterval < 1) {
                    client->errorValue = _XkbErrCode3(0x08, stuff->repeatDelay,
                                                      stuff->repeatInterval);
                    return BadValue;
                }

                new.repeat_delay = stuff->repeatDelay;
                new.repeat_interval = stuff->repeatInterval;
            }

            if (stuff->changeCtrls & XkbSlowKeysMask) {
                if (stuff->slowKeysDelay < 1) {
                    client->errorValue = _XkbErrCode2(0x09,
                                                      stuff->slowKeysDelay);
                    return BadValue;
                }

                new.slow_keys_delay = stuff->slowKeysDelay;
            }

            if (stuff->changeCtrls & XkbBounceKeysMask) {
                if (stuff->debounceDelay < 1) {
                    client->errorValue = _XkbErrCode2(0x0A,
                                                      stuff->debounceDelay);
                    return BadValue;
                }

                new.debounce_delay = stuff->debounceDelay;
            }

            if (stuff->changeCtrls & XkbMouseKeysMask) {
                if (stuff->mkDfltBtn > XkbMaxMouseKeysBtn) {
                    client->errorValue = _XkbErrCode2(0x0B, stuff->mkDfltBtn);
                    return BadValue;
                }

                new.mk_dflt_btn = stuff->mkDfltBtn;
            }

            if (stuff->changeCtrls & XkbMouseKeysAccelMask) {
                if (stuff->mkDelay < 1 || stuff->mkInterval < 1 ||
                    stuff->mkTimeToMax < 1 || stuff->mkMaxSpeed < 1 ||
                    stuff->mkCurve < -1000) {
                    client->errorValue = _XkbErrCode2(0x0C,0);
                    return BadValue;
                }

                new.mk_delay = stuff->mkDelay;
                new.mk_interval = stuff->mkInterval;
                new.mk_time_to_max = stuff->mkTimeToMax;
                new.mk_max_speed = stuff->mkMaxSpeed;
                new.mk_curve = stuff->mkCurve;
                AccessXComputeCurveFactor(xkbi, &new);
            }

            if (stuff->changeCtrls & XkbGroupsWrapMask) {
                unsigned act, num;

                act = XkbOutOfRangeGroupAction(stuff->groupsWrap);
                switch (act) {
                case XkbRedirectIntoRange:
                    num = XkbOutOfRangeGroupNumber(stuff->groupsWrap);
                    if (num >= new.num_groups) {
                        client->errorValue = _XkbErrCode3(0x0D, new.num_groups,
                                                          num);
                        return BadValue;
                    }
                case XkbWrapIntoRange:
                case XkbClampIntoRange:
                    break;
                default:
                    client->errorValue = _XkbErrCode2(0x0E, act);
                    return BadValue;
                }

                new.groups_wrap= stuff->groupsWrap;
            }

            CHK_MASK_LEGAL(0x0F, stuff->axOptions, XkbAX_AllOptionsMask);
            if (stuff->changeCtrls & XkbAccessXKeysMask) {
                new.ax_options = stuff->axOptions & XkbAX_AllOptionsMask;
            }
            else {
                if (stuff->changeCtrls & XkbStickyKeysMask) {
                    new.ax_options &= ~(XkbAX_SKOptionsMask);
                    new.ax_options |= (stuff->axOptions & XkbAX_SKOptionsMask);
                }

                if (stuff->changeCtrls & XkbAccessXFeedbackMask) {
                    new.ax_options &= ~(XkbAX_FBOptionsMask);
                    new.ax_options |= (stuff->axOptions & XkbAX_FBOptionsMask);
                }
            }

            if (stuff->changeCtrls & XkbAccessXTimeoutMask) {
                if (stuff->axTimeout < 1) {
                    client->errorValue = _XkbErrCode2(0x10, stuff->axTimeout);
                    return BadValue;
                }
                CHK_MASK_MATCH(0x11, stuff->axtCtrlsMask,
                               stuff->axtCtrlsValues);
                CHK_MASK_LEGAL(0x12, stuff->axtCtrlsMask,
                               XkbAllBooleanCtrlsMask);
                CHK_MASK_MATCH(0x13, stuff->axtOptsMask, stuff->axtOptsValues);
                CHK_MASK_LEGAL(0x14, stuff->axtOptsMask, XkbAX_AllOptionsMask);
                new.ax_timeout = stuff->axTimeout;
                new.axt_ctrls_mask = stuff->axtCtrlsMask;
                new.axt_ctrls_values = (stuff->axtCtrlsValues &
                                        stuff->axtCtrlsMask);
                new.axt_opts_mask = stuff->axtOptsMask;
                new.axt_opts_values = (stuff->axtOptsValues &
                                       stuff->axtOptsMask);
            }

            if (stuff->changeCtrls & XkbPerKeyRepeatMask)
                memcpy(new.per_key_repeat, stuff->perKeyRepeat,
                       XkbPerKeyBitArraySize);

            old= *ctrl;
            *ctrl= new;
            XkbDDXChangeControls(tmpd, &old, ctrl);

            if (XkbComputeControlsNotify(tmpd, &old, ctrl, &cn, False)) {
                cn.keycode = 0;
                cn.eventType = 0;
                cn.requestMajor = XkbReqCode;
                cn.requestMinor = X_kbSetControls;
                XkbSendControlsNotify(tmpd, &cn);
            }

            sli = XkbFindSrvLedInfo(tmpd, XkbDfltXIClass, XkbDfltXIId, 0);
            if (sli)
                XkbUpdateIndicators(tmpd, sli->usesControls, True, NULL,
                                    &cause);

            /* If sticky keys were disabled, clear all locks and latches */
            if ((old.enabled_ctrls & XkbStickyKeysMask) &&
                !(ctrl->enabled_ctrls & XkbStickyKeysMask))
                XkbClearAllLatchesAndLocks(tmpd, xkbi, True, &cause);
        }
    }

    return client->noClientException;
}


XkbClientGone(pointer data,XID id)
{
    DevicePtr	pXDev = (DevicePtr)data;

    if (!XkbRemoveResourceClient(pXDev,id)) {
	ErrorF("[xkb] Internal Error! bad RemoveResourceClient in XkbClientGone\n");
    }
    return 1;
}


_CheckSetOverlay(	char **		wire_inout,
			XkbGeometryPtr	geom,
			XkbSectionPtr	section,
			ClientPtr	client)
{
register int		r;
char *			wire;
XkbOverlayPtr		ol;
xkbOverlayWireDesc *	olWire;
xkbOverlayRowWireDesc *	rWire;

    wire= *wire_inout;
    olWire= (xkbOverlayWireDesc *)wire;
    if (client->swapped) {
	register int n;
	swapl(&olWire->name,n);
    }
    CHK_ATOM_ONLY(olWire->name);
    ol= XkbAddGeomOverlay(section,olWire->name,olWire->nRows);
    rWire= (xkbOverlayRowWireDesc *)&olWire[1];
    for (r=0;r<olWire->nRows;r++) {
	register int		k;
	xkbOverlayKeyWireDesc *	kWire;
	XkbOverlayRowPtr	row;

	if (rWire->rowUnder>section->num_rows) {
	    client->errorValue= _XkbErrCode4(0x20,r,section->num_rows,
							rWire->rowUnder);
	    return BadMatch;
	}
	row= XkbAddGeomOverlayRow(ol,rWire->rowUnder,rWire->nKeys);
	kWire= (xkbOverlayKeyWireDesc *)&rWire[1];
	for (k=0;k<rWire->nKeys;k++,kWire++) {
	    if (XkbAddGeomOverlayKey(ol,row,
	    		(char *)kWire->over,(char *)kWire->under)==NULL) {
		client->errorValue= _XkbErrCode3(0x21,r,k);
		return BadMatch;
	    }
	}
	rWire= (xkbOverlayRowWireDesc *)kWire;
    }
    olWire= (xkbOverlayWireDesc *)rWire;
    wire= (char *)olWire;
    *wire_inout= wire;
    return Success;
}


ProcXkbGetDeviceInfo(ClientPtr client)
{
DeviceIntPtr		dev;
xkbGetDeviceInfoReply	rep;
int			status,nDeviceLedFBs;
unsigned		length,nameLen;
CARD16			ledClass,ledID;
unsigned		wanted,supported;
char *			str;

    REQUEST(xkbGetDeviceInfoReq);
    REQUEST_SIZE_MATCH(xkbGetDeviceInfoReq);

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

    wanted= stuff->wanted;

    CHK_ANY_DEVICE(dev, stuff->deviceSpec, client, DixGetAttrAccess);
    CHK_MASK_LEGAL(0x01,wanted,XkbXI_AllDeviceFeaturesMask);

    if ((!dev->button)||((stuff->nBtns<1)&&(!stuff->allBtns)))
	wanted&= ~XkbXI_ButtonActionsMask;
    if ((!dev->kbdfeed)&&(!dev->leds))
	wanted&= ~XkbXI_IndicatorsMask;

    nameLen= XkbSizeCountedString(dev->name);
    bzero((char *)&rep,SIZEOF(xkbGetDeviceInfoReply));
    rep.type = X_Reply;
    rep.deviceID= dev->id;
    rep.sequenceNumber = client->sequence;
    rep.length = nameLen/4;
    rep.present = wanted;
    rep.supported = XkbXI_AllDeviceFeaturesMask;
    rep.unsupported = 0;
    rep.firstBtnWanted = rep.nBtnsWanted = 0;
    rep.firstBtnRtrn = rep.nBtnsRtrn = 0;
    if (dev->button)
	 rep.totalBtns= dev->button->numButtons;
    else rep.totalBtns= 0;
    rep.devType=	dev->type;
    rep.hasOwnState=	(dev->key && dev->key->xkbInfo);
    rep.nDeviceLedFBs = 0;
    if (dev->kbdfeed)	rep.dfltKbdFB= dev->kbdfeed->ctrl.id;
    else		rep.dfltKbdFB= XkbXINone;
    if (dev->leds)	rep.dfltLedFB= dev->leds->ctrl.id;
    else		rep.dfltLedFB= XkbXINone;

    ledClass= stuff->ledClass;
    ledID= stuff->ledID;

    rep.firstBtnWanted= rep.nBtnsWanted= 0;
    rep.firstBtnRtrn= rep.nBtnsRtrn= 0;
    if (wanted&XkbXI_ButtonActionsMask) {
	if (stuff->allBtns) {
	    stuff->firstBtn= 0;
	    stuff->nBtns= dev->button->numButtons;
	}

	if ((stuff->firstBtn+stuff->nBtns)>dev->button->numButtons) {
	    client->errorValue = _XkbErrCode4(0x02,dev->button->numButtons,
							stuff->firstBtn,
							stuff->nBtns);
	    return BadValue;
	}
	else {
	    rep.firstBtnWanted= stuff->firstBtn;
	    rep.nBtnsWanted= stuff->nBtns;
	    if (dev->button->xkb_acts!=NULL) {
		XkbAction *act;
		register int i;

		rep.firstBtnRtrn= stuff->firstBtn;
		rep.nBtnsRtrn= stuff->nBtns;
		act= &dev->button->xkb_acts[rep.firstBtnWanted];
		for (i=0;i<rep.nBtnsRtrn;i++,act++) {
		    if (act->type!=XkbSA_NoAction)
			break;
		}
		rep.firstBtnRtrn+=	i;
		rep.nBtnsRtrn-=		i;
		act= &dev->button->xkb_acts[rep.firstBtnRtrn+rep.nBtnsRtrn-1];
		for (i=0;i<rep.nBtnsRtrn;i++,act--) {
		    if (act->type!=XkbSA_NoAction)
			break;
		}
		rep.nBtnsRtrn-=		i;
	    }
	    rep.length+= (rep.nBtnsRtrn*SIZEOF(xkbActionWireDesc))/4;
	}
    }

    if (wanted&XkbXI_IndicatorsMask) {
	status= CheckDeviceLedFBs(dev,ledClass,ledID,&rep,client);
	if (status!=Success)
	    return status;
    }
    length= rep.length*4;
    supported= rep.supported;
    nDeviceLedFBs = rep.nDeviceLedFBs;
    if (client->swapped) {
	register int n;
	swaps(&rep.sequenceNumber,n);
	swapl(&rep.length,n);
	swaps(&rep.present,n);
	swaps(&rep.supported,n);
	swaps(&rep.unsupported,n);
	swaps(&rep.nDeviceLedFBs,n);
	swapl(&rep.type,n);
    }
    WriteToClient(client,SIZEOF(xkbGetDeviceInfoReply), (char *)&rep);

    str= (char*) xalloc(nameLen);
    if (!str)
	return BadAlloc;
    XkbWriteCountedString(str,dev->name,client->swapped);
    WriteToClient(client,nameLen,str);
    xfree(str);
    length-= nameLen;

    if (rep.nBtnsRtrn>0) {
	int			sz;
	xkbActionWireDesc *	awire;
	sz= rep.nBtnsRtrn*SIZEOF(xkbActionWireDesc);
	awire= (xkbActionWireDesc *)&dev->button->xkb_acts[rep.firstBtnRtrn];
	WriteToClient(client,sz,(char *)awire);
	length-= sz;
    }
    if (nDeviceLedFBs>0) {
	status= SendDeviceLedFBs(dev,ledClass,ledID,length,client);
	if (status!=Success)
	    return status;
    }
    else if (length!=0)  {
	ErrorF("[xkb] Internal Error!  BadLength in ProcXkbGetDeviceInfo\n");
	ErrorF("[xkb]                  Wrote %d fewer bytes than expected\n",length);
	return BadLength;
    }
    if (stuff->wanted&(~supported)) {
	xkbExtensionDeviceNotify ed;
	bzero((char *)&ed,SIZEOF(xkbExtensionDeviceNotify));
	ed.ledClass=		ledClass;
	ed.ledID=		ledID;
	ed.ledsDefined= 	0;
	ed.ledState=		0;
	ed.firstBtn= ed.nBtns=	0;
	ed.reason=		XkbXI_UnsupportedFeatureMask;
	ed.supported=		supported;
	ed.unsupported=		stuff->wanted&(~supported);
	XkbSendExtensionDeviceNotify(dev,client,&ed);
    }
    return client->noClientException;
}


_CheckSetSections( 	XkbGeometryPtr		geom,
			xkbSetGeometryReq *	req,
			char **			wire_inout,
			ClientPtr		client)
{
Status			status;
register int		s;
char *			wire;
xkbSectionWireDesc *	sWire;
XkbSectionPtr		section;

    wire= *wire_inout;
    if (req->nSections<1)
	return Success;
    sWire= (xkbSectionWireDesc *)wire;
    for (s=0;s<req->nSections;s++) {
	register int		r;
	xkbRowWireDesc *	rWire;
	if (client->swapped) {
	    register int n;
	    swapl(&sWire->name,n);
	    swaps(&sWire->top,n);
	    swaps(&sWire->left,n);
	    swaps(&sWire->width,n);
	    swaps(&sWire->height,n);
	    swaps(&sWire->angle,n);
	}
	CHK_ATOM_ONLY(sWire->name);
	section= XkbAddGeomSection(geom,sWire->name,sWire->nRows,
					sWire->nDoodads,sWire->nOverlays);
	if (!section)
	    return BadAlloc;
	section->priority=	sWire->priority;
	section->top=		sWire->top;
	section->left=		sWire->left;
	section->width=		sWire->width;
	section->height=	sWire->height;
	section->angle=		sWire->angle;
	rWire= (xkbRowWireDesc *)&sWire[1];
	for (r=0;r<sWire->nRows;r++) {
	    register int	k;
	    XkbRowPtr		row;
	    xkbKeyWireDesc *	kWire;
	    if (client->swapped) {
		register int n;
		swaps(&rWire->top,n);
		swaps(&rWire->left,n);
	    }
	    row= XkbAddGeomRow(section,rWire->nKeys);
	    if (!row)
		return BadAlloc;
	    row->top= rWire->top;
	    row->left= rWire->left;
	    row->vertical= rWire->vertical;
	    kWire= (xkbKeyWireDesc *)&rWire[1];
	    for (k=0;k<rWire->nKeys;k++) {
		XkbKeyPtr	key;
		key= XkbAddGeomKey(row);
		if (!key)
		    return BadAlloc;
		memcpy(key->name.name,kWire[k].name,XkbKeyNameLength);
		key->gap= kWire[k].gap;
		key->shape_ndx= kWire[k].shapeNdx;
		key->color_ndx= kWire[k].colorNdx;
		if (key->shape_ndx>=geom->num_shapes) {
		    client->errorValue= _XkbErrCode3(0x10,key->shape_ndx,
							  geom->num_shapes);
		    return BadMatch;
		}
		if (key->color_ndx>=geom->num_colors) {
		    client->errorValue= _XkbErrCode3(0x11,key->color_ndx,
							  geom->num_colors);
		    return BadMatch;
		}
	    }
	    rWire= (xkbRowWireDesc *)&kWire[rWire->nKeys];
	}
	wire= (char *)rWire;
	if (sWire->nDoodads>0) {
	    register int d;
	    for (d=0;d<sWire->nDoodads;d++) {
		status=_CheckSetDoodad(&wire,geom,section,client);
		if (status!=Success)
		    return status;
	    }
	}
	if (sWire->nOverlays>0) {
	    register int o;
	    for (o=0;o<sWire->nOverlays;o++) {
		status= _CheckSetOverlay(&wire,geom,section,client);
		if (status!=Success)
		    return status;
	    }
	}
	sWire= (xkbSectionWireDesc *)wire;
    }
    wire= (char *)sWire;
    *wire_inout= wire;
    return Success;
}


CheckKeyTypes(	ClientPtr	client,
		XkbDescPtr	xkb,
		xkbSetMapReq *	req,
		xkbKeyTypeWireDesc **wireRtrn,
		int	 *	nMapsRtrn,
		CARD8 *		mapWidthRtrn)
{
unsigned		nMaps;
register unsigned	i,n;
register CARD8 *	map;
register xkbKeyTypeWireDesc	*wire = *wireRtrn;

    if (req->firstType>((unsigned)xkb->map->num_types)) {
	*nMapsRtrn = _XkbErrCode3(0x01,req->firstType,xkb->map->num_types);
	return 0;
    }
    if (req->flags&XkbSetMapResizeTypes) {
	nMaps = req->firstType+req->nTypes;
	if (nMaps<XkbNumRequiredTypes) {  /* canonical types must be there */
	    *nMapsRtrn= _XkbErrCode4(0x02,req->firstType,req->nTypes,4);
	    return 0;
	}
    }
    else if (req->present&XkbKeyTypesMask) {
	nMaps = xkb->map->num_types;
	if ((req->firstType+req->nTypes)>nMaps) {
	    *nMapsRtrn = req->firstType+req->nTypes;
	    return 0;
	}
    }
    else {
	*nMapsRtrn = xkb->map->num_types;
	for (i=0;i<xkb->map->num_types;i++) {
	    mapWidthRtrn[i] = xkb->map->types[i].num_levels;
	}
	return 1;
    }

    for (i=0;i<req->firstType;i++) {
	mapWidthRtrn[i] = xkb->map->types[i].num_levels;
    }
    for (i=0;i<req->nTypes;i++) {
	unsigned	width;
	if (client->swapped) {
	    register int s;
	    swaps(&wire->virtualMods,s);
	}
	n= i+req->firstType;
	width= wire->numLevels;
	if (width<1) {
	    *nMapsRtrn= _XkbErrCode3(0x04,n,width);
	    return 0;
	}
	else if ((n==XkbOneLevelIndex)&&(width!=1)) { /* must be width 1 */
	    *nMapsRtrn= _XkbErrCode3(0x05,n,width);
	    return 0;
	}
	else if ((width!=2)&&
		 ((n==XkbTwoLevelIndex)||(n==XkbKeypadIndex)||
		  (n==XkbAlphabeticIndex))) {
	    /* TWO_LEVEL, ALPHABETIC and KEYPAD must be width 2 */
	    *nMapsRtrn= _XkbErrCode3(0x05,n,width);
	    return 0;
	}
	if (wire->nMapEntries>0) {
	    xkbKTSetMapEntryWireDesc *	mapWire;
	    xkbModsWireDesc *		preWire;
	    mapWire= (xkbKTSetMapEntryWireDesc *)&wire[1];
	    preWire= (xkbModsWireDesc *)&mapWire[wire->nMapEntries];
	    for (n=0;n<wire->nMapEntries;n++) {
		if (client->swapped) {
		    register int s;
		    swaps(&mapWire[n].virtualMods,s);
		}
		if (mapWire[n].realMods&(~wire->realMods)) {
		    *nMapsRtrn= _XkbErrCode4(0x06,n,mapWire[n].realMods,
						 wire->realMods);
		    return 0;
		}
		if (mapWire[n].virtualMods&(~wire->virtualMods)) {
		    *nMapsRtrn= _XkbErrCode3(0x07,n,mapWire[n].virtualMods);
		    return 0;
		}
		if (mapWire[n].level>=wire->numLevels) {
		    *nMapsRtrn= _XkbErrCode4(0x08,n,wire->numLevels,
						 mapWire[n].level);
		    return 0;
		}
		if (wire->preserve) {
		    if (client->swapped) {
			register int s;
			swaps(&preWire[n].virtualMods,s);
		    }
		    if (preWire[n].realMods&(~mapWire[n].realMods)) {
			*nMapsRtrn= _XkbErrCode4(0x09,n,preWire[n].realMods,
							mapWire[n].realMods);
			return 0;
		    }
		    if (preWire[n].virtualMods&(~mapWire[n].virtualMods)) {
			*nMapsRtrn=_XkbErrCode3(0x0a,n,preWire[n].virtualMods);
			return 0;
		    }
		}
	    }
	    if (wire->preserve)
		 map= (CARD8 *)&preWire[wire->nMapEntries];
	    else map= (CARD8 *)&mapWire[wire->nMapEntries];
	}
	else map= (CARD8 *)&wire[1];
	mapWidthRtrn[i+req->firstType] = wire->numLevels;
	wire= (xkbKeyTypeWireDesc *)map;
    }
    for (i=req->firstType+req->nTypes;i<nMaps;i++) {
	mapWidthRtrn[i] = xkb->map->types[i].num_levels;
    }
    *nMapsRtrn = nMaps;
    *wireRtrn = wire;
    return 1;
}


_XkbSetDeviceInfoCheck(ClientPtr client, DeviceIntPtr dev,
                       xkbSetDeviceInfoReq *stuff)
{
    unsigned                    change;
    char                       *wire;
    xkbExtensionDeviceNotify    ed;

    bzero((char *)&ed,SIZEOF(xkbExtensionDeviceNotify));
    ed.deviceID=	dev->id;
    wire= (char *)&stuff[1];
    if (change&XkbXI_ButtonActionsMask) {
	int			nBtns,sz,i;
	XkbAction *		acts;
	DeviceIntPtr		kbd;

	nBtns= dev->button->numButtons;
	acts= dev->button->xkb_acts;
	if (acts==NULL) {
	    acts= _XkbTypedCalloc(nBtns,XkbAction);
	    if (!acts)
		return BadAlloc;
	    dev->button->xkb_acts= acts;
	}
	sz= stuff->nBtns*SIZEOF(xkbActionWireDesc);
	memcpy((char *)&acts[stuff->firstBtn],(char *)wire,sz);
	wire+= sz;
	ed.reason|=	XkbXI_ButtonActionsMask;
	ed.firstBtn=	stuff->firstBtn;
	ed.nBtns=	stuff->nBtns;

	if (dev->key)	kbd= dev;
	else		kbd= inputInfo.keyboard;
	acts= &dev->button->xkb_acts[stuff->firstBtn];
	for (i=0;i<stuff->nBtns;i++,acts++) {
	    if (acts->type!=XkbSA_NoAction)
		XkbSetActionKeyMods(kbd->key->xkbInfo->desc,acts,0);
	}
    }
    if (stuff->change&XkbXI_IndicatorsMask) {
	int status= Success;
	wire= SetDeviceIndicators(wire,dev,change,stuff->nDeviceLedFBs,
							&status,client,&ed);
	if (status!=Success)
	    return status;
    }
    if ((stuff->change)&&(ed.reason))
	XkbSendExtensionDeviceNotify(dev,client,&ed);
    return Success;
}


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

    if (((rep->present&XkbVirtualModMapMask)==0)||(rep->nVModMapKeys<1)||
	(!xkb)||(!xkb->server)||(!xkb->server->vmodmap)) {
	rep->present&= ~XkbVirtualModMapMask;
	rep->firstVModMapKey= rep->nVModMapKeys= 0;
	rep->totalVModMapKeys= 0;
	return 0;
    }
    for (nRtrn=i=0;i<rep->nVModMapKeys-1;i++) {
	if (xkb->server->vmodmap[i+rep->firstVModMapKey]!=0)
	    nRtrn++;
    }
    rep->totalVModMapKeys= nRtrn;
    len= nRtrn*SIZEOF(xkbVModMapWireDesc);
    return len;
}


ProcXkbGetState(ClientPtr client)
{
    REQUEST(xkbGetStateReq);
    DeviceIntPtr	dev;
    xkbGetStateReply	 rep;
    XkbStateRec		*xkb;

    REQUEST_SIZE_MATCH(xkbGetStateReq);

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

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

    xkb= &dev->key->xkbInfo->state;
    bzero(&rep,sizeof(xkbGetStateReply));
    rep.type= X_Reply;
    rep.sequenceNumber= client->sequence;
    rep.length = 0;
    rep.deviceID = dev->id;
    rep.mods = dev->key->state&0xff;
    rep.baseMods = xkb->base_mods;
    rep.lockedMods = xkb->locked_mods;
    rep.latchedMods = xkb->latched_mods;
    rep.group = xkb->group;
    rep.baseGroup = xkb->base_group;
    rep.latchedGroup = xkb->latched_group;
    rep.lockedGroup = xkb->locked_group;
    rep.compatState = xkb->compat_state;
    rep.ptrBtnState = xkb->ptr_buttons;
    if (client->swapped) {
	register int n;
	swaps(&rep.sequenceNumber,n);
	swaps(&rep.ptrBtnState,n);
    }
    WriteToClient(client, SIZEOF(xkbGetStateReply), (char *)&rep);
    return client->noClientException;
}


_XkbWriteAtoms(char *wire,Atom *atoms,int maxAtoms,int swap)
{
register unsigned int i;
Atom *atm;

    atm = (Atom *)wire;
    for (i=0;i<maxAtoms;i++) {
	if (atoms[i]!=None) {
	    *atm= atoms[i];
	    if (swap) {
		register int n;
		swapl(atm,n);
	    }
	    atm++;
	}
    }
    return (char *)atm;
}


ProcXkbSetGeometry(ClientPtr client)
{
    DeviceIntPtr        dev;
    int                 rc;

    REQUEST(xkbSetGeometryReq);
    REQUEST_AT_LEAST_SIZE(xkbSetGeometryReq);

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

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixManageAccess);
    CHK_ATOM_OR_NONE(stuff->name);

    rc = _XkbSetGeometry(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)
                    _XkbSetGeometry(client, other, stuff);
            }
        }
    }

    return Success;
}


ProcXkbSetMap(ClientPtr client)
{
    DeviceIntPtr	dev;
    char *		tmp;
    int                 rc;

    REQUEST(xkbSetMapReq);
    REQUEST_AT_LEAST_SIZE(xkbSetMapReq);

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

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixManageAccess);
    CHK_MASK_LEGAL(0x01,stuff->present,XkbAllMapComponentsMask);

    tmp = (char *)&stuff[1];

    /* Check if we can to the SetMap on the requested device. If this
       succeeds, do the same thing for all extension devices (if needed).
       If any of them fails, fail.  */
    rc = _XkbSetMapChecks(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 = _XkbSetMapChecks(client, other, stuff, tmp);
                    if (rc != Success)
                        return rc;
                }
            }
        }
    }

    /* We know now that we will succed with the SetMap. In theory anyway. */
    rc = _XkbSetMap(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)
                    _XkbSetMap(client, other, stuff, tmp);
                /* ignore rc. if the SetMap failed although the check above
                   reported true there isn't much we can do. we still need to
                   set all other devices, hoping that at least they stay in
                   sync. */
            }
        }
    }

    return client->noClientException;
}


SetKeySyms(	ClientPtr		client,
		XkbDescPtr		xkb,
		xkbSetMapReq *		req,
		xkbSymMapWireDesc *	wire,
		XkbChangesPtr 		changes,
		DeviceIntPtr		dev)
{
register unsigned 	i,s;
XkbSymMapPtr		oldMap;
KeySym *		newSyms;
KeySym *		pSyms;
unsigned		first,last;

    oldMap = &xkb->map->key_sym_map[req->firstKeySym];
    for (i=0;i<req->nKeySyms;i++,oldMap++) {
	pSyms = (KeySym *)&wire[1];
	if (wire->nSyms>0) {
	    newSyms = XkbResizeKeySyms(xkb,i+req->firstKeySym,wire->nSyms);
	    for (s=0;s<wire->nSyms;s++) {
		newSyms[s]= pSyms[s];
	    }
	    if (client->swapped) {
		int n;
		for (s=0;s<wire->nSyms;s++) {
		    swapl(&newSyms[s],n);
		}
	    }
	}
	oldMap->kt_index[0] = wire->ktIndex[0];
	oldMap->kt_index[1] = wire->ktIndex[1];
	oldMap->kt_index[2] = wire->ktIndex[2];
	oldMap->kt_index[3] = wire->ktIndex[3];
	oldMap->group_info = wire->groupInfo;
	oldMap->width = wire->width;
	wire= (xkbSymMapWireDesc *)&pSyms[wire->nSyms];
    }
    first= req->firstKeySym;
    last= first+req->nKeySyms-1;
    if (changes->map.changed&XkbKeySymsMask) {
	int oldLast= (changes->map.first_key_sym+changes->map.num_key_syms-1);
	if (changes->map.first_key_sym<first)
	    first= changes->map.first_key_sym;
	if (oldLast>last)
	    last= oldLast;
    }
    changes->map.changed|= XkbKeySymsMask;
    changes->map.first_key_sym = first;
    changes->map.num_key_syms = (last-first+1);

    s= 0;
    for (i=xkb->min_key_code;i<=xkb->max_key_code;i++) {
	if (XkbKeyNumGroups(xkb,i)>s)
	    s= XkbKeyNumGroups(xkb,i);
    }
    if (s!=xkb->ctrls->num_groups) {
	xkbControlsNotify	cn;
	XkbControlsRec		old;
	cn.keycode= 0;
	cn.eventType= 0;
	cn.requestMajor= XkbReqCode;
	cn.requestMinor= X_kbSetMap;
	old= *xkb->ctrls;
	xkb->ctrls->num_groups= s;
	if (XkbComputeControlsNotify(dev,&old,xkb->ctrls,&cn,False))
	    XkbSendControlsNotify(dev,&cn);
    }
    return (char *)wire;
}


_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;
}


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;
}


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;
}


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;
}


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_show(struct kobject *kobj,
				struct kobj_attribute *attr, char *buf)
{
	return sprintf(buf, "0\n");
}

In drivers/pci/hotplug/rpadlpar_sysfs.c in the Linux kernel through 5.11.8, the RPA PCI Hotplug driver has a user-tolerable buffer overflow when writing a new device name to the driver from userspace, allowing userspace to write data to the kernel stack frame directly. This occurs because add_slot_store and remove_slot_store mishandle drc_name '\0' termination, aka CID-cc7a0bb058b8.


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");
}


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 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;
}


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);
}