2006-11-26 11:13:41 -07:00
|
|
|
/************************************************************
|
|
|
|
|
|
|
|
Copyright 1989, 1998 The Open Group
|
|
|
|
|
|
|
|
Permission to use, copy, modify, distribute, and sell this software and its
|
|
|
|
documentation for any purpose is hereby granted without fee, provided that
|
|
|
|
the above copyright notice appear in all copies and that both that
|
|
|
|
copyright notice and this permission notice appear in supporting
|
|
|
|
documentation.
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
|
|
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
Except as contained in this notice, the name of The Open Group shall not be
|
|
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
|
|
in this Software without prior written authorization from The Open Group.
|
|
|
|
|
|
|
|
********************************************************/
|
|
|
|
|
|
|
|
/* THIS IS NOT AN X CONSORTIUM STANDARD OR AN X PROJECT TEAM SPECIFICATION */
|
|
|
|
|
|
|
|
#define SHM
|
|
|
|
|
|
|
|
#ifdef HAVE_DIX_CONFIG_H
|
|
|
|
#include <dix-config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/ipc.h>
|
|
|
|
#include <sys/shm.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/stat.h>
|
2014-05-02 13:27:46 -06:00
|
|
|
#include <fcntl.h>
|
2006-11-26 11:13:41 -07:00
|
|
|
#include <X11/X.h>
|
|
|
|
#include <X11/Xproto.h>
|
|
|
|
#include "misc.h"
|
|
|
|
#include "os.h"
|
|
|
|
#include "dixstruct.h"
|
|
|
|
#include "resource.h"
|
|
|
|
#include "scrnintstr.h"
|
|
|
|
#include "windowstr.h"
|
|
|
|
#include "pixmapstr.h"
|
|
|
|
#include "gcstruct.h"
|
|
|
|
#include "extnsionst.h"
|
|
|
|
#include "servermd.h"
|
2008-11-02 08:26:08 -07:00
|
|
|
#include "shmint.h"
|
|
|
|
#include "xace.h"
|
2010-04-13 13:54:46 -06:00
|
|
|
#include <X11/extensions/shmproto.h>
|
2006-11-26 11:13:41 -07:00
|
|
|
#include <X11/Xfuncproto.h>
|
2014-05-02 13:27:46 -06:00
|
|
|
#include <sys/mman.h>
|
2010-07-27 13:02:24 -06:00
|
|
|
#include "protocol-versions.h"
|
2014-05-02 13:27:46 -06:00
|
|
|
#include "busfault.h"
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2008-11-02 08:26:08 -07:00
|
|
|
/* Needed for Solaris cross-zone shared memory extension */
|
|
|
|
#ifdef HAVE_SHMCTL64
|
|
|
|
#include <sys/ipc_impl.h>
|
|
|
|
#define SHMSTAT(id, buf) shmctl64(id, IPC_STAT64, buf)
|
|
|
|
#define SHMSTAT_TYPE struct shmid_ds64
|
|
|
|
#define SHMPERM_TYPE struct ipc_perm64
|
|
|
|
#define SHM_PERM(buf) buf.shmx_perm
|
|
|
|
#define SHM_SEGSZ(buf) buf.shmx_segsz
|
|
|
|
#define SHMPERM_UID(p) p->ipcx_uid
|
|
|
|
#define SHMPERM_CUID(p) p->ipcx_cuid
|
|
|
|
#define SHMPERM_GID(p) p->ipcx_gid
|
|
|
|
#define SHMPERM_CGID(p) p->ipcx_cgid
|
|
|
|
#define SHMPERM_MODE(p) p->ipcx_mode
|
|
|
|
#define SHMPERM_ZONEID(p) p->ipcx_zoneid
|
|
|
|
#else
|
|
|
|
#define SHMSTAT(id, buf) shmctl(id, IPC_STAT, buf)
|
|
|
|
#define SHMSTAT_TYPE struct shmid_ds
|
|
|
|
#define SHMPERM_TYPE struct ipc_perm
|
|
|
|
#define SHM_PERM(buf) buf.shm_perm
|
|
|
|
#define SHM_SEGSZ(buf) buf.shm_segsz
|
|
|
|
#define SHMPERM_UID(p) p->uid
|
|
|
|
#define SHMPERM_CUID(p) p->cuid
|
|
|
|
#define SHMPERM_GID(p) p->gid
|
|
|
|
#define SHMPERM_CGID(p) p->cgid
|
|
|
|
#define SHMPERM_MODE(p) p->mode
|
|
|
|
#endif
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
#ifdef PANORAMIX
|
|
|
|
#include "panoramiX.h"
|
|
|
|
#include "panoramiXsrv.h"
|
|
|
|
#endif
|
|
|
|
|
2013-06-07 11:28:45 -06:00
|
|
|
#include "extinit.h"
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
typedef struct _ShmScrPrivateRec {
|
|
|
|
CloseScreenProcPtr CloseScreen;
|
|
|
|
ShmFuncsPtr shmFuncs;
|
|
|
|
DestroyPixmapProcPtr destroyPixmap;
|
|
|
|
} ShmScrPrivateRec;
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
static PixmapPtr fbShmCreatePixmap(XSHM_CREATE_PIXMAP_ARGS);
|
2012-06-10 07:21:05 -06:00
|
|
|
static int ShmDetachSegment(pointer /* value */ ,
|
|
|
|
XID /* shmseg */
|
2006-11-26 11:13:41 -07:00
|
|
|
);
|
2012-06-10 07:21:05 -06:00
|
|
|
static void ShmResetProc(ExtensionEntry * /* extEntry */
|
2006-11-26 11:13:41 -07:00
|
|
|
);
|
2012-06-10 07:21:05 -06:00
|
|
|
static void SShmCompletionEvent(xShmCompletionEvent * /* from */ ,
|
|
|
|
xShmCompletionEvent * /* to */
|
2006-11-26 11:13:41 -07:00
|
|
|
);
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
static Bool ShmDestroyPixmap(PixmapPtr pPixmap);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
static unsigned char ShmReqCode;
|
2010-07-27 13:02:24 -06:00
|
|
|
int ShmCompletionCode;
|
|
|
|
int BadShmSegCode;
|
|
|
|
RESTYPE ShmSegType;
|
2006-11-26 11:13:41 -07:00
|
|
|
static ShmDescPtr Shmsegs;
|
|
|
|
static Bool sharedPixmaps;
|
2010-12-05 08:36:02 -07:00
|
|
|
static DevPrivateKeyRec shmScrPrivateKeyRec;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2010-12-05 08:36:02 -07:00
|
|
|
#define shmScrPrivateKey (&shmScrPrivateKeyRec)
|
|
|
|
static DevPrivateKeyRec shmPixmapPrivateKeyRec;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2010-12-05 08:36:02 -07:00
|
|
|
#define shmPixmapPrivateKey (&shmPixmapPrivateKeyRec)
|
2012-06-10 07:21:05 -06:00
|
|
|
static ShmFuncs miFuncs = { NULL, NULL };
|
|
|
|
static ShmFuncs fbFuncs = { fbShmCreatePixmap, NULL };
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
#define ShmGetScreenPriv(s) ((ShmScrPrivateRec *)dixLookupPrivate(&(s)->devPrivates, shmScrPrivateKey))
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
#define VERIFY_SHMSEG(shmseg,shmdesc,client) \
|
|
|
|
{ \
|
2013-06-07 11:28:45 -06:00
|
|
|
int tmprc; \
|
|
|
|
tmprc = dixLookupResourceByType((pointer *)&(shmdesc), shmseg, ShmSegType, \
|
|
|
|
client, DixReadAccess); \
|
|
|
|
if (tmprc != Success) \
|
|
|
|
return tmprc; \
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#define VERIFY_SHMPTR(shmseg,offset,needwrite,shmdesc,client) \
|
|
|
|
{ \
|
|
|
|
VERIFY_SHMSEG(shmseg, shmdesc, client); \
|
|
|
|
if ((offset & 3) || (offset > shmdesc->size)) \
|
|
|
|
{ \
|
|
|
|
client->errorValue = offset; \
|
|
|
|
return BadValue; \
|
|
|
|
} \
|
|
|
|
if (needwrite && !shmdesc->writable) \
|
|
|
|
return BadAccess; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define VERIFY_SHMSIZE(shmdesc,offset,len,client) \
|
|
|
|
{ \
|
|
|
|
if ((offset + len) > shmdesc->size) \
|
|
|
|
{ \
|
|
|
|
return BadAccess; \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
2008-06-14 18:17:32 -06:00
|
|
|
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__DragonFly__)
|
2006-11-26 11:13:41 -07:00
|
|
|
#include <sys/signal.h>
|
|
|
|
|
|
|
|
static Bool badSysCall = FALSE;
|
|
|
|
|
|
|
|
static void
|
2009-09-06 13:44:18 -06:00
|
|
|
SigSysHandler(int signo)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
badSysCall = TRUE;
|
|
|
|
}
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
static Bool
|
|
|
|
CheckForShmSyscall(void)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
void (*oldHandler) (int);
|
2006-11-26 11:13:41 -07:00
|
|
|
int shmid = -1;
|
|
|
|
|
|
|
|
/* If no SHM support in the kernel, the bad syscall will generate SIGSYS */
|
|
|
|
oldHandler = signal(SIGSYS, SigSysHandler);
|
|
|
|
|
|
|
|
badSysCall = FALSE;
|
|
|
|
shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT);
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (shmid != -1) {
|
2006-11-26 11:13:41 -07:00
|
|
|
/* Successful allocation - clean up */
|
2012-06-10 07:21:05 -06:00
|
|
|
shmctl(shmid, IPC_RMID, NULL);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else {
|
2006-11-26 11:13:41 -07:00
|
|
|
/* Allocation failed */
|
|
|
|
badSysCall = TRUE;
|
|
|
|
}
|
|
|
|
signal(SIGSYS, oldHandler);
|
2010-12-05 08:36:02 -07:00
|
|
|
return !badSysCall;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#define MUST_CHECK_FOR_SHM_SYSCALL
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
static Bool
|
2013-06-07 11:28:45 -06:00
|
|
|
ShmCloseScreen(ScreenPtr pScreen)
|
2010-07-27 13:02:24 -06:00
|
|
|
{
|
|
|
|
ShmScrPrivateRec *screen_priv = ShmGetScreenPriv(pScreen);
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
pScreen->CloseScreen = screen_priv->CloseScreen;
|
|
|
|
dixSetPrivate(&pScreen->devPrivates, shmScrPrivateKey, NULL);
|
2010-12-05 08:36:02 -07:00
|
|
|
free(screen_priv);
|
2013-06-07 11:28:45 -06:00
|
|
|
return (*pScreen->CloseScreen) (pScreen);
|
2010-07-27 13:02:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static ShmScrPrivateRec *
|
|
|
|
ShmInitScreenPriv(ScreenPtr pScreen)
|
|
|
|
{
|
|
|
|
ShmScrPrivateRec *screen_priv = ShmGetScreenPriv(pScreen);
|
2012-06-10 07:21:05 -06:00
|
|
|
|
|
|
|
if (!screen_priv) {
|
|
|
|
screen_priv = calloc(1, sizeof(ShmScrPrivateRec));
|
|
|
|
screen_priv->CloseScreen = pScreen->CloseScreen;
|
|
|
|
dixSetPrivate(&pScreen->devPrivates, shmScrPrivateKey, screen_priv);
|
|
|
|
pScreen->CloseScreen = ShmCloseScreen;
|
2010-07-27 13:02:24 -06:00
|
|
|
}
|
|
|
|
return screen_priv;
|
|
|
|
}
|
|
|
|
|
2010-12-05 08:36:02 -07:00
|
|
|
static Bool
|
|
|
|
ShmRegisterPrivates(void)
|
|
|
|
{
|
|
|
|
if (!dixRegisterPrivateKey(&shmScrPrivateKeyRec, PRIVATE_SCREEN, 0))
|
2012-06-10 07:21:05 -06:00
|
|
|
return FALSE;
|
2010-12-05 08:36:02 -07:00
|
|
|
if (!dixRegisterPrivateKey(&shmPixmapPrivateKeyRec, PRIVATE_PIXMAP, 0))
|
2012-06-10 07:21:05 -06:00
|
|
|
return FALSE;
|
2010-12-05 08:36:02 -07:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
/*ARGSUSED*/ static void
|
|
|
|
ShmResetProc(ExtensionEntry * extEntry)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
int i;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
for (i = 0; i < screenInfo.numScreens; i++)
|
2012-06-10 07:21:05 -06:00
|
|
|
ShmRegisterFuncs(screenInfo.screens[i], NULL);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
void
|
2009-09-06 13:44:18 -06:00
|
|
|
ShmRegisterFuncs(ScreenPtr pScreen, ShmFuncsPtr funcs)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2010-12-05 08:36:02 -07:00
|
|
|
if (!ShmRegisterPrivates())
|
2012-06-10 07:21:05 -06:00
|
|
|
return;
|
2010-07-27 13:02:24 -06:00
|
|
|
ShmInitScreenPriv(pScreen)->shmFuncs = funcs;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
2012-06-10 07:21:05 -06:00
|
|
|
ShmDestroyPixmap(PixmapPtr pPixmap)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
ScreenPtr pScreen = pPixmap->drawable.pScreen;
|
2010-07-27 13:02:24 -06:00
|
|
|
ShmScrPrivateRec *screen_priv = ShmGetScreenPriv(pScreen);
|
2012-06-10 07:21:05 -06:00
|
|
|
Bool ret;
|
|
|
|
|
|
|
|
if (pPixmap->refcnt == 1) {
|
|
|
|
ShmDescPtr shmdesc;
|
|
|
|
|
|
|
|
shmdesc = (ShmDescPtr) dixLookupPrivate(&pPixmap->devPrivates,
|
|
|
|
shmPixmapPrivateKey);
|
|
|
|
if (shmdesc)
|
|
|
|
ShmDetachSegment((pointer) shmdesc, pPixmap->drawable.id);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
pScreen->DestroyPixmap = screen_priv->destroyPixmap;
|
2006-11-26 11:13:41 -07:00
|
|
|
ret = (*pScreen->DestroyPixmap) (pPixmap);
|
2010-07-27 13:02:24 -06:00
|
|
|
screen_priv->destroyPixmap = pScreen->DestroyPixmap;
|
2006-11-26 11:13:41 -07:00
|
|
|
pScreen->DestroyPixmap = ShmDestroyPixmap;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-27 13:02:24 -06:00
|
|
|
void
|
2009-09-06 13:44:18 -06:00
|
|
|
ShmRegisterFbFuncs(ScreenPtr pScreen)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2010-07-27 13:02:24 -06:00
|
|
|
ShmRegisterFuncs(pScreen, &fbFuncs);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
ProcShmQueryVersion(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2013-06-07 11:28:45 -06:00
|
|
|
xShmQueryVersionReply rep = {
|
|
|
|
.type = X_Reply,
|
|
|
|
.sharedPixmaps = sharedPixmaps,
|
|
|
|
.sequenceNumber = client->sequence,
|
|
|
|
.length = 0,
|
|
|
|
.majorVersion = SERVER_SHM_MAJOR_VERSION,
|
|
|
|
.minorVersion = SERVER_SHM_MINOR_VERSION,
|
|
|
|
.uid = geteuid(),
|
|
|
|
.gid = getegid(),
|
|
|
|
.pixmapFormat = sharedPixmaps ? ZPixmap : 0
|
|
|
|
};
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
REQUEST_SIZE_MATCH(xShmQueryVersionReq);
|
2013-06-07 11:28:45 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
if (client->swapped) {
|
2012-06-10 07:21:05 -06:00
|
|
|
swaps(&rep.sequenceNumber);
|
|
|
|
swapl(&rep.length);
|
|
|
|
swaps(&rep.majorVersion);
|
|
|
|
swaps(&rep.minorVersion);
|
|
|
|
swaps(&rep.uid);
|
|
|
|
swaps(&rep.gid);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2013-06-07 11:28:45 -06:00
|
|
|
WriteToClient(client, sizeof(xShmQueryVersionReply), &rep);
|
2010-12-05 08:36:02 -07:00
|
|
|
return Success;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Simulate the access() system call for a shared memory segement,
|
|
|
|
* using the credentials from the client if available
|
|
|
|
*/
|
|
|
|
static int
|
2012-06-10 07:21:05 -06:00
|
|
|
shm_access(ClientPtr client, SHMPERM_TYPE * perm, int readonly)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
int uid, gid;
|
|
|
|
mode_t mask;
|
2008-11-02 08:26:08 -07:00
|
|
|
int uidset = 0, gidset = 0;
|
|
|
|
LocalClientCredRec *lcc;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2008-11-02 08:26:08 -07:00
|
|
|
if (GetLocalClientCreds(client, &lcc) != -1) {
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (lcc->fieldsSet & LCC_UID_SET) {
|
|
|
|
uid = lcc->euid;
|
|
|
|
uidset = 1;
|
|
|
|
}
|
|
|
|
if (lcc->fieldsSet & LCC_GID_SET) {
|
|
|
|
gid = lcc->egid;
|
|
|
|
gidset = 1;
|
|
|
|
}
|
2008-11-02 08:26:08 -07:00
|
|
|
|
|
|
|
#if defined(HAVE_GETZONEID) && defined(SHMPERM_ZONEID)
|
2012-06-10 07:21:05 -06:00
|
|
|
if (((lcc->fieldsSet & LCC_ZID_SET) == 0) || (lcc->zoneid == -1)
|
|
|
|
|| (lcc->zoneid != SHMPERM_ZONEID(perm))) {
|
|
|
|
uidset = 0;
|
|
|
|
gidset = 0;
|
|
|
|
}
|
2008-11-02 08:26:08 -07:00
|
|
|
#endif
|
2012-06-10 07:21:05 -06:00
|
|
|
FreeLocalClientCreds(lcc);
|
|
|
|
|
|
|
|
if (uidset) {
|
|
|
|
/* User id 0 always gets access */
|
|
|
|
if (uid == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Check the owner */
|
|
|
|
if (SHMPERM_UID(perm) == uid || SHMPERM_CUID(perm) == uid) {
|
|
|
|
mask = S_IRUSR;
|
|
|
|
if (!readonly) {
|
|
|
|
mask |= S_IWUSR;
|
|
|
|
}
|
|
|
|
return (SHMPERM_MODE(perm) & mask) == mask ? 0 : -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gidset) {
|
|
|
|
/* Check the group */
|
|
|
|
if (SHMPERM_GID(perm) == gid || SHMPERM_CGID(perm) == gid) {
|
|
|
|
mask = S_IRGRP;
|
|
|
|
if (!readonly) {
|
|
|
|
mask |= S_IWGRP;
|
|
|
|
}
|
|
|
|
return (SHMPERM_MODE(perm) & mask) == mask ? 0 : -1;
|
|
|
|
}
|
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
/* Otherwise, check everyone else */
|
|
|
|
mask = S_IROTH;
|
|
|
|
if (!readonly) {
|
2012-06-10 07:21:05 -06:00
|
|
|
mask |= S_IWOTH;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2008-11-02 08:26:08 -07:00
|
|
|
return (SHMPERM_MODE(perm) & mask) == mask ? 0 : -1;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
ProcShmAttach(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2008-11-02 08:26:08 -07:00
|
|
|
SHMSTAT_TYPE buf;
|
2006-11-26 11:13:41 -07:00
|
|
|
ShmDescPtr shmdesc;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST(xShmAttachReq);
|
|
|
|
|
|
|
|
REQUEST_SIZE_MATCH(xShmAttachReq);
|
|
|
|
LEGAL_NEW_RESOURCE(stuff->shmseg, client);
|
2012-06-10 07:21:05 -06:00
|
|
|
if ((stuff->readOnly != xTrue) && (stuff->readOnly != xFalse)) {
|
|
|
|
client->errorValue = stuff->readOnly;
|
2010-12-05 08:36:02 -07:00
|
|
|
return BadValue;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2014-05-02 13:27:46 -06:00
|
|
|
for (shmdesc = Shmsegs; shmdesc; shmdesc = shmdesc->next) {
|
|
|
|
if (!SHMDESC_IS_FD(shmdesc) && shmdesc->shmid == stuff->shmid)
|
|
|
|
break;
|
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
if (shmdesc) {
|
|
|
|
if (!stuff->readOnly && !shmdesc->writable)
|
|
|
|
return BadAccess;
|
|
|
|
shmdesc->refcnt++;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else {
|
|
|
|
shmdesc = malloc(sizeof(ShmDescRec));
|
|
|
|
if (!shmdesc)
|
|
|
|
return BadAlloc;
|
2014-05-02 13:27:46 -06:00
|
|
|
#ifdef SHM_FD_PASSING
|
|
|
|
shmdesc->is_fd = FALSE;
|
|
|
|
#endif
|
2012-06-10 07:21:05 -06:00
|
|
|
shmdesc->addr = shmat(stuff->shmid, 0,
|
|
|
|
stuff->readOnly ? SHM_RDONLY : 0);
|
|
|
|
if ((shmdesc->addr == ((char *) -1)) || SHMSTAT(stuff->shmid, &buf)) {
|
|
|
|
free(shmdesc);
|
|
|
|
return BadAccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The attach was performed with root privs. We must
|
|
|
|
* do manual checking of access rights for the credentials
|
|
|
|
* of the client */
|
|
|
|
|
|
|
|
if (shm_access(client, &(SHM_PERM(buf)), stuff->readOnly) == -1) {
|
|
|
|
shmdt(shmdesc->addr);
|
|
|
|
free(shmdesc);
|
|
|
|
return BadAccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
shmdesc->shmid = stuff->shmid;
|
|
|
|
shmdesc->refcnt = 1;
|
|
|
|
shmdesc->writable = !stuff->readOnly;
|
|
|
|
shmdesc->size = SHM_SEGSZ(buf);
|
|
|
|
shmdesc->next = Shmsegs;
|
|
|
|
Shmsegs = shmdesc;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
if (!AddResource(stuff->shmseg, ShmSegType, (pointer) shmdesc))
|
|
|
|
return BadAlloc;
|
2010-12-05 08:36:02 -07:00
|
|
|
return Success;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
/*ARGSUSED*/ static int
|
2009-09-06 13:44:18 -06:00
|
|
|
ShmDetachSegment(pointer value, /* must conform to DeleteType */
|
2012-06-10 07:21:05 -06:00
|
|
|
XID shmseg)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
ShmDescPtr shmdesc = (ShmDescPtr) value;
|
2006-11-26 11:13:41 -07:00
|
|
|
ShmDescPtr *prev;
|
|
|
|
|
|
|
|
if (--shmdesc->refcnt)
|
2012-06-10 07:21:05 -06:00
|
|
|
return TRUE;
|
2014-05-02 13:27:46 -06:00
|
|
|
#if SHM_FD_PASSING
|
|
|
|
if (shmdesc->is_fd) {
|
|
|
|
if (shmdesc->busfault)
|
|
|
|
busfault_unregister(shmdesc->busfault);
|
|
|
|
munmap(shmdesc->addr, shmdesc->size);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
shmdt(shmdesc->addr);
|
2012-06-10 07:21:05 -06:00
|
|
|
for (prev = &Shmsegs; *prev != shmdesc; prev = &(*prev)->next);
|
2006-11-26 11:13:41 -07:00
|
|
|
*prev = shmdesc->next;
|
2010-12-05 08:36:02 -07:00
|
|
|
free(shmdesc);
|
2006-11-26 11:13:41 -07:00
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
ProcShmDetach(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
ShmDescPtr shmdesc;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST(xShmDetachReq);
|
|
|
|
|
|
|
|
REQUEST_SIZE_MATCH(xShmDetachReq);
|
|
|
|
VERIFY_SHMSEG(stuff->shmseg, shmdesc, client);
|
|
|
|
FreeResource(stuff->shmseg, RT_NONE);
|
2010-12-05 08:36:02 -07:00
|
|
|
return Success;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
2008-11-02 08:26:08 -07:00
|
|
|
/*
|
|
|
|
* If the given request doesn't exactly match PutImage's constraints,
|
|
|
|
* wrap the image in a scratch pixmap header and let CopyArea sort it out.
|
|
|
|
*/
|
2006-11-26 11:13:41 -07:00
|
|
|
static void
|
2008-11-02 08:26:08 -07:00
|
|
|
doShmPutImage(DrawablePtr dst, GCPtr pGC,
|
2012-06-10 07:21:05 -06:00
|
|
|
int depth, unsigned int format,
|
|
|
|
int w, int h, int sx, int sy, int sw, int sh, int dx, int dy,
|
|
|
|
char *data)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2008-11-02 08:26:08 -07:00
|
|
|
PixmapPtr pPixmap;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
2012-01-31 00:52:35 -07:00
|
|
|
if (format == ZPixmap || (format == XYPixmap && depth == 1)) {
|
2012-06-10 07:21:05 -06:00
|
|
|
pPixmap = GetScratchPixmapHeader(dst->pScreen, w, h, depth,
|
|
|
|
BitsPerPixel(depth),
|
|
|
|
PixmapBytePad(w, depth), data);
|
|
|
|
if (!pPixmap)
|
|
|
|
return;
|
|
|
|
pGC->ops->CopyArea((DrawablePtr) pPixmap, dst, pGC, sx, sy, sw, sh, dx,
|
|
|
|
dy);
|
|
|
|
FreeScratchPixmapHeader(pPixmap);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
GCPtr putGC = GetScratchGC(depth, dst->pScreen);
|
|
|
|
|
|
|
|
if (!putGC)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pPixmap = (*dst->pScreen->CreatePixmap) (dst->pScreen, sw, sh, depth,
|
|
|
|
CREATE_PIXMAP_USAGE_SCRATCH);
|
|
|
|
if (!pPixmap) {
|
|
|
|
FreeScratchGC(putGC);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ValidateGC(&pPixmap->drawable, putGC);
|
|
|
|
(*putGC->ops->PutImage) (&pPixmap->drawable, putGC, depth, -sx, -sy, w,
|
|
|
|
h, 0,
|
|
|
|
(format == XYPixmap) ? XYPixmap : ZPixmap,
|
|
|
|
data);
|
|
|
|
FreeScratchGC(putGC);
|
|
|
|
if (format == XYBitmap)
|
|
|
|
(void) (*pGC->ops->CopyPlane) (&pPixmap->drawable, dst, pGC, 0, 0,
|
|
|
|
sw, sh, dx, dy, 1L);
|
|
|
|
else
|
|
|
|
(void) (*pGC->ops->CopyArea) (&pPixmap->drawable, dst, pGC, 0, 0,
|
|
|
|
sw, sh, dx, dy);
|
|
|
|
(*pPixmap->drawable.pScreen->DestroyPixmap) (pPixmap);
|
2010-07-27 13:02:24 -06:00
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
2011-11-05 07:32:40 -06:00
|
|
|
static int
|
|
|
|
ProcShmPutImage(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2011-11-05 07:32:40 -06:00
|
|
|
GCPtr pGC;
|
|
|
|
DrawablePtr pDraw;
|
|
|
|
long length;
|
|
|
|
ShmDescPtr shmdesc;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST(xShmPutImageReq);
|
2011-11-05 07:32:40 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST_SIZE_MATCH(xShmPutImageReq);
|
2011-11-05 07:32:40 -06:00
|
|
|
VALIDATE_DRAWABLE_AND_GC(stuff->drawable, pDraw, DixWriteAccess);
|
|
|
|
VERIFY_SHMPTR(stuff->shmseg, stuff->offset, FALSE, shmdesc, client);
|
|
|
|
if ((stuff->sendEvent != xTrue) && (stuff->sendEvent != xFalse))
|
2012-06-10 07:21:05 -06:00
|
|
|
return BadValue;
|
|
|
|
if (stuff->format == XYBitmap) {
|
2011-11-05 07:32:40 -06:00
|
|
|
if (stuff->depth != 1)
|
|
|
|
return BadMatch;
|
|
|
|
length = PixmapBytePad(stuff->totalWidth, 1);
|
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else if (stuff->format == XYPixmap) {
|
2011-11-05 07:32:40 -06:00
|
|
|
if (pDraw->depth != stuff->depth)
|
|
|
|
return BadMatch;
|
|
|
|
length = PixmapBytePad(stuff->totalWidth, 1);
|
2012-06-10 07:21:05 -06:00
|
|
|
length *= stuff->depth;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else if (stuff->format == ZPixmap) {
|
2011-11-05 07:32:40 -06:00
|
|
|
if (pDraw->depth != stuff->depth)
|
|
|
|
return BadMatch;
|
|
|
|
length = PixmapBytePad(stuff->totalWidth, stuff->depth);
|
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else {
|
|
|
|
client->errorValue = stuff->format;
|
2011-11-05 07:32:40 -06:00
|
|
|
return BadValue;
|
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2011-11-05 07:32:40 -06:00
|
|
|
/*
|
|
|
|
* There's a potential integer overflow in this check:
|
|
|
|
* VERIFY_SHMSIZE(shmdesc, stuff->offset, length * stuff->totalHeight,
|
|
|
|
* client);
|
|
|
|
* the version below ought to avoid it
|
|
|
|
*/
|
|
|
|
if (stuff->totalHeight != 0 &&
|
2012-06-10 07:21:05 -06:00
|
|
|
length > (shmdesc->size - stuff->offset) / stuff->totalHeight) {
|
|
|
|
client->errorValue = stuff->totalWidth;
|
|
|
|
return BadValue;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
if (stuff->srcX > stuff->totalWidth) {
|
|
|
|
client->errorValue = stuff->srcX;
|
|
|
|
return BadValue;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
if (stuff->srcY > stuff->totalHeight) {
|
|
|
|
client->errorValue = stuff->srcY;
|
|
|
|
return BadValue;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
if ((stuff->srcX + stuff->srcWidth) > stuff->totalWidth) {
|
|
|
|
client->errorValue = stuff->srcWidth;
|
|
|
|
return BadValue;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
if ((stuff->srcY + stuff->srcHeight) > stuff->totalHeight) {
|
|
|
|
client->errorValue = stuff->srcHeight;
|
|
|
|
return BadValue;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2011-11-05 07:32:40 -06:00
|
|
|
if ((((stuff->format == ZPixmap) && (stuff->srcX == 0)) ||
|
2012-06-10 07:21:05 -06:00
|
|
|
((stuff->format != ZPixmap) &&
|
|
|
|
(stuff->srcX < screenInfo.bitmapScanlinePad) &&
|
|
|
|
((stuff->format == XYBitmap) ||
|
|
|
|
((stuff->srcY == 0) &&
|
|
|
|
(stuff->srcHeight == stuff->totalHeight))))) &&
|
|
|
|
((stuff->srcX + stuff->srcWidth) == stuff->totalWidth))
|
|
|
|
(*pGC->ops->PutImage) (pDraw, pGC, stuff->depth,
|
|
|
|
stuff->dstX, stuff->dstY,
|
|
|
|
stuff->totalWidth, stuff->srcHeight,
|
|
|
|
stuff->srcX, stuff->format,
|
|
|
|
shmdesc->addr + stuff->offset +
|
|
|
|
(stuff->srcY * length));
|
2011-11-05 07:32:40 -06:00
|
|
|
else
|
2012-06-10 07:21:05 -06:00
|
|
|
doShmPutImage(pDraw, pGC, stuff->depth, stuff->format,
|
|
|
|
stuff->totalWidth, stuff->totalHeight,
|
|
|
|
stuff->srcX, stuff->srcY,
|
|
|
|
stuff->srcWidth, stuff->srcHeight,
|
|
|
|
stuff->dstX, stuff->dstY, shmdesc->addr + stuff->offset);
|
|
|
|
|
|
|
|
if (stuff->sendEvent) {
|
2013-06-07 11:28:45 -06:00
|
|
|
xShmCompletionEvent ev = {
|
|
|
|
.type = ShmCompletionCode,
|
|
|
|
.drawable = stuff->drawable,
|
|
|
|
.minorEvent = X_ShmPutImage,
|
|
|
|
.majorEvent = ShmReqCode,
|
|
|
|
.shmseg = stuff->shmseg,
|
|
|
|
.offset = stuff->offset
|
|
|
|
};
|
2012-06-10 07:21:05 -06:00
|
|
|
WriteEventsToClient(client, 1, (xEvent *) &ev);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2011-11-05 07:32:40 -06:00
|
|
|
|
|
|
|
return Success;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
2011-11-05 07:32:40 -06:00
|
|
|
static int
|
|
|
|
ProcShmGetImage(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
DrawablePtr pDraw;
|
|
|
|
long lenPer = 0, length;
|
|
|
|
Mask plane = 0;
|
|
|
|
xShmGetImageReply xgi;
|
|
|
|
ShmDescPtr shmdesc;
|
2013-06-07 11:28:45 -06:00
|
|
|
VisualID visual = None;
|
2012-06-10 07:21:05 -06:00
|
|
|
int rc;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
REQUEST(xShmGetImageReq);
|
|
|
|
|
|
|
|
REQUEST_SIZE_MATCH(xShmGetImageReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
if ((stuff->format != XYPixmap) && (stuff->format != ZPixmap)) {
|
|
|
|
client->errorValue = stuff->format;
|
2010-12-05 08:36:02 -07:00
|
|
|
return BadValue;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0, DixReadAccess);
|
2007-11-24 12:04:00 -07:00
|
|
|
if (rc != Success)
|
2012-06-10 07:21:05 -06:00
|
|
|
return rc;
|
2006-11-26 11:13:41 -07:00
|
|
|
VERIFY_SHMPTR(stuff->shmseg, stuff->offset, TRUE, shmdesc, client);
|
2012-06-10 07:21:05 -06:00
|
|
|
if (pDraw->type == DRAWABLE_WINDOW) {
|
2013-06-07 11:28:45 -06:00
|
|
|
if ( /* check for being viewable */
|
2012-06-10 07:21:05 -06:00
|
|
|
!((WindowPtr) pDraw)->realized ||
|
|
|
|
/* check for being on screen */
|
|
|
|
pDraw->x + stuff->x < 0 ||
|
|
|
|
pDraw->x + stuff->x + (int) stuff->width > pDraw->pScreen->width
|
|
|
|
|| pDraw->y + stuff->y < 0 ||
|
|
|
|
pDraw->y + stuff->y + (int) stuff->height >
|
|
|
|
pDraw->pScreen->height ||
|
|
|
|
/* check for being inside of border */
|
|
|
|
stuff->x < -wBorderWidth((WindowPtr) pDraw) ||
|
|
|
|
stuff->x + (int) stuff->width >
|
|
|
|
wBorderWidth((WindowPtr) pDraw) + (int) pDraw->width ||
|
|
|
|
stuff->y < -wBorderWidth((WindowPtr) pDraw) ||
|
|
|
|
stuff->y + (int) stuff->height >
|
|
|
|
wBorderWidth((WindowPtr) pDraw) + (int) pDraw->height)
|
|
|
|
return BadMatch;
|
2013-06-07 11:28:45 -06:00
|
|
|
visual = wVisual(((WindowPtr) pDraw));
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else {
|
|
|
|
if (stuff->x < 0 ||
|
|
|
|
stuff->x + (int) stuff->width > pDraw->width ||
|
|
|
|
stuff->y < 0 || stuff->y + (int) stuff->height > pDraw->height)
|
|
|
|
return BadMatch;
|
2013-06-07 11:28:45 -06:00
|
|
|
visual = None;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2013-06-07 11:28:45 -06:00
|
|
|
xgi = (xShmGetImageReply) {
|
|
|
|
.type = X_Reply,
|
|
|
|
.sequenceNumber = client->sequence,
|
|
|
|
.length = 0,
|
|
|
|
.visual = visual,
|
|
|
|
.depth = pDraw->depth
|
|
|
|
};
|
2012-06-10 07:21:05 -06:00
|
|
|
if (stuff->format == ZPixmap) {
|
|
|
|
length = PixmapBytePad(stuff->width, pDraw->depth) * stuff->height;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else {
|
|
|
|
lenPer = PixmapBytePad(stuff->width, 1) * stuff->height;
|
|
|
|
plane = ((Mask) 1) << (pDraw->depth - 1);
|
|
|
|
/* only planes asked for */
|
|
|
|
length = lenPer * Ones(stuff->planeMask & (plane | (plane - 1)));
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
VERIFY_SHMSIZE(shmdesc, stuff->offset, length, client);
|
|
|
|
xgi.size = length;
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (length == 0) {
|
|
|
|
/* nothing to do */
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else if (stuff->format == ZPixmap) {
|
|
|
|
(*pDraw->pScreen->GetImage) (pDraw, stuff->x, stuff->y,
|
|
|
|
stuff->width, stuff->height,
|
|
|
|
stuff->format, stuff->planeMask,
|
|
|
|
shmdesc->addr + stuff->offset);
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2012-06-10 07:21:05 -06:00
|
|
|
else {
|
2011-11-05 07:32:40 -06:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
length = stuff->offset;
|
|
|
|
for (; plane; plane >>= 1) {
|
|
|
|
if (stuff->planeMask & plane) {
|
|
|
|
(*pDraw->pScreen->GetImage) (pDraw,
|
|
|
|
stuff->x, stuff->y,
|
|
|
|
stuff->width, stuff->height,
|
|
|
|
stuff->format, plane,
|
|
|
|
shmdesc->addr + length);
|
|
|
|
length += lenPer;
|
|
|
|
}
|
|
|
|
}
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (client->swapped) {
|
2012-06-10 07:21:05 -06:00
|
|
|
swaps(&xgi.sequenceNumber);
|
|
|
|
swapl(&xgi.length);
|
|
|
|
swapl(&xgi.visual);
|
|
|
|
swapl(&xgi.size);
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
2013-06-07 11:28:45 -06:00
|
|
|
WriteToClient(client, sizeof(xShmGetImageReply), &xgi);
|
2011-11-05 07:32:40 -06:00
|
|
|
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef PANORAMIX
|
2012-06-10 07:21:05 -06:00
|
|
|
static int
|
2011-11-05 07:32:40 -06:00
|
|
|
ProcPanoramiXShmPutImage(ClientPtr client)
|
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
int j, result, orig_x, orig_y;
|
|
|
|
PanoramiXRes *draw, *gc;
|
|
|
|
Bool sendEvent, isRoot;
|
2011-11-05 07:32:40 -06:00
|
|
|
|
|
|
|
REQUEST(xShmPutImageReq);
|
|
|
|
REQUEST_SIZE_MATCH(xShmPutImageReq);
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
result = dixLookupResourceByClass((pointer *) &draw, stuff->drawable,
|
|
|
|
XRC_DRAWABLE, client, DixWriteAccess);
|
2011-11-05 07:32:40 -06:00
|
|
|
if (result != Success)
|
|
|
|
return (result == BadValue) ? BadDrawable : result;
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
result = dixLookupResourceByType((pointer *) &gc, stuff->gc,
|
|
|
|
XRT_GC, client, DixReadAccess);
|
2011-11-05 07:32:40 -06:00
|
|
|
if (result != Success)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
isRoot = (draw->type == XRT_WINDOW) && draw->u.win.root;
|
|
|
|
|
|
|
|
orig_x = stuff->dstX;
|
|
|
|
orig_y = stuff->dstY;
|
|
|
|
sendEvent = stuff->sendEvent;
|
|
|
|
stuff->sendEvent = 0;
|
|
|
|
FOR_NSCREENS(j) {
|
2012-06-10 07:21:05 -06:00
|
|
|
if (!j)
|
|
|
|
stuff->sendEvent = sendEvent;
|
|
|
|
stuff->drawable = draw->info[j].id;
|
|
|
|
stuff->gc = gc->info[j].id;
|
|
|
|
if (isRoot) {
|
|
|
|
stuff->dstX = orig_x - screenInfo.screens[j]->x;
|
|
|
|
stuff->dstY = orig_y - screenInfo.screens[j]->y;
|
|
|
|
}
|
|
|
|
result = ProcShmPutImage(client);
|
|
|
|
if (result != Success)
|
|
|
|
break;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
static int
|
2011-11-05 07:32:40 -06:00
|
|
|
ProcPanoramiXShmGetImage(ClientPtr client)
|
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
PanoramiXRes *draw;
|
|
|
|
DrawablePtr *drawables;
|
|
|
|
DrawablePtr pDraw;
|
|
|
|
xShmGetImageReply xgi;
|
|
|
|
ShmDescPtr shmdesc;
|
|
|
|
int i, x, y, w, h, format, rc;
|
|
|
|
Mask plane = 0, planemask;
|
|
|
|
long lenPer = 0, length, widthBytesLine;
|
|
|
|
Bool isRoot;
|
2011-11-05 07:32:40 -06:00
|
|
|
|
|
|
|
REQUEST(xShmGetImageReq);
|
|
|
|
|
|
|
|
REQUEST_SIZE_MATCH(xShmGetImageReq);
|
|
|
|
|
|
|
|
if ((stuff->format != XYPixmap) && (stuff->format != ZPixmap)) {
|
2012-06-10 07:21:05 -06:00
|
|
|
client->errorValue = stuff->format;
|
2011-11-05 07:32:40 -06:00
|
|
|
return BadValue;
|
|
|
|
}
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
rc = dixLookupResourceByClass((pointer *) &draw, stuff->drawable,
|
|
|
|
XRC_DRAWABLE, client, DixWriteAccess);
|
2011-11-05 07:32:40 -06:00
|
|
|
if (rc != Success)
|
2012-06-10 07:21:05 -06:00
|
|
|
return (rc == BadValue) ? BadDrawable : rc;
|
2011-11-05 07:32:40 -06:00
|
|
|
|
|
|
|
if (draw->type == XRT_PIXMAP)
|
2012-06-10 07:21:05 -06:00
|
|
|
return ProcShmGetImage(client);
|
2011-11-05 07:32:40 -06:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0, DixReadAccess);
|
2011-11-05 07:32:40 -06:00
|
|
|
if (rc != Success)
|
2012-06-10 07:21:05 -06:00
|
|
|
return rc;
|
2011-11-05 07:32:40 -06:00
|
|
|
|
|
|
|
VERIFY_SHMPTR(stuff->shmseg, stuff->offset, TRUE, shmdesc, client);
|
|
|
|
|
|
|
|
x = stuff->x;
|
|
|
|
y = stuff->y;
|
|
|
|
w = stuff->width;
|
|
|
|
h = stuff->height;
|
|
|
|
format = stuff->format;
|
|
|
|
planemask = stuff->planeMask;
|
|
|
|
|
|
|
|
isRoot = (draw->type == XRT_WINDOW) && draw->u.win.root;
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (isRoot) {
|
|
|
|
if ( /* check for being onscreen */
|
|
|
|
x < 0 || x + w > PanoramiXPixWidth ||
|
|
|
|
y < 0 || y + h > PanoramiXPixHeight)
|
|
|
|
return BadMatch;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ( /* check for being onscreen */
|
|
|
|
screenInfo.screens[0]->x + pDraw->x + x < 0 ||
|
|
|
|
screenInfo.screens[0]->x + pDraw->x + x + w > PanoramiXPixWidth
|
|
|
|
|| screenInfo.screens[0]->y + pDraw->y + y < 0 ||
|
|
|
|
screenInfo.screens[0]->y + pDraw->y + y + h > PanoramiXPixHeight
|
|
|
|
||
|
|
|
|
/* check for being inside of border */
|
|
|
|
x < -wBorderWidth((WindowPtr) pDraw) ||
|
|
|
|
x + w > wBorderWidth((WindowPtr) pDraw) + (int) pDraw->width ||
|
|
|
|
y < -wBorderWidth((WindowPtr) pDraw) ||
|
|
|
|
y + h > wBorderWidth((WindowPtr) pDraw) + (int) pDraw->height)
|
|
|
|
return BadMatch;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
|
|
|
|
2010-12-05 08:36:02 -07:00
|
|
|
drawables = calloc(PanoramiXNumScreens, sizeof(DrawablePtr));
|
2012-06-10 07:21:05 -06:00
|
|
|
if (!drawables)
|
|
|
|
return BadAlloc;
|
2010-07-27 13:02:24 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
drawables[0] = pDraw;
|
2011-11-05 07:32:40 -06:00
|
|
|
FOR_NSCREENS_FORWARD_SKIP(i) {
|
2012-06-10 07:21:05 -06:00
|
|
|
rc = dixLookupDrawable(drawables + i, draw->info[i].id, client, 0,
|
|
|
|
DixReadAccess);
|
|
|
|
if (rc != Success) {
|
|
|
|
free(drawables);
|
|
|
|
return rc;
|
|
|
|
}
|
2007-11-24 12:04:00 -07:00
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2013-06-07 11:28:45 -06:00
|
|
|
xgi = (xShmGetImageReply) {
|
|
|
|
.type = X_Reply,
|
|
|
|
.sequenceNumber = client->sequence,
|
|
|
|
.length = 0,
|
|
|
|
.visual = wVisual(((WindowPtr) pDraw)),
|
|
|
|
.depth = pDraw->depth
|
|
|
|
};
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (format == ZPixmap) {
|
|
|
|
widthBytesLine = PixmapBytePad(w, pDraw->depth);
|
|
|
|
length = widthBytesLine * h;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
widthBytesLine = PixmapBytePad(w, 1);
|
|
|
|
lenPer = widthBytesLine * h;
|
|
|
|
plane = ((Mask) 1) << (pDraw->depth - 1);
|
|
|
|
length = lenPer * Ones(planemask & (plane | (plane - 1)));
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
VERIFY_SHMSIZE(shmdesc, stuff->offset, length, client);
|
|
|
|
xgi.size = length;
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (length == 0) { /* nothing to do */
|
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
else if (format == ZPixmap) {
|
2012-06-10 07:21:05 -06:00
|
|
|
XineramaGetImageData(drawables, x, y, w, h, format, planemask,
|
|
|
|
shmdesc->addr + stuff->offset,
|
|
|
|
widthBytesLine, isRoot);
|
|
|
|
}
|
|
|
|
else {
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
length = stuff->offset;
|
2006-11-26 11:13:41 -07:00
|
|
|
for (; plane; plane >>= 1) {
|
2012-06-10 07:21:05 -06:00
|
|
|
if (planemask & plane) {
|
|
|
|
XineramaGetImageData(drawables, x, y, w, h,
|
|
|
|
format, plane, shmdesc->addr + length,
|
|
|
|
widthBytesLine, isRoot);
|
|
|
|
length += lenPer;
|
|
|
|
}
|
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2010-12-05 08:36:02 -07:00
|
|
|
free(drawables);
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
if (client->swapped) {
|
2012-06-10 07:21:05 -06:00
|
|
|
swaps(&xgi.sequenceNumber);
|
|
|
|
swapl(&xgi.length);
|
|
|
|
swapl(&xgi.visual);
|
|
|
|
swapl(&xgi.size);
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2013-06-07 11:28:45 -06:00
|
|
|
WriteToClient(client, sizeof(xShmGetImageReply), &xgi);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2010-12-05 08:36:02 -07:00
|
|
|
return Success;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
ProcPanoramiXShmCreatePixmap(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
ScreenPtr pScreen = NULL;
|
|
|
|
PixmapPtr pMap = NULL;
|
|
|
|
DrawablePtr pDraw;
|
|
|
|
DepthPtr pDepth;
|
2007-11-24 12:04:00 -07:00
|
|
|
int i, j, result, rc;
|
2006-11-26 11:13:41 -07:00
|
|
|
ShmDescPtr shmdesc;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST(xShmCreatePixmapReq);
|
2008-01-17 08:43:43 -07:00
|
|
|
unsigned int width, height, depth;
|
|
|
|
unsigned long size;
|
2006-11-26 11:13:41 -07:00
|
|
|
PanoramiXRes *newPix;
|
|
|
|
|
|
|
|
REQUEST_SIZE_MATCH(xShmCreatePixmapReq);
|
|
|
|
client->errorValue = stuff->pid;
|
|
|
|
if (!sharedPixmaps)
|
2012-06-10 07:21:05 -06:00
|
|
|
return BadImplementation;
|
2006-11-26 11:13:41 -07:00
|
|
|
LEGAL_NEW_RESOURCE(stuff->pid, client);
|
2007-11-24 12:04:00 -07:00
|
|
|
rc = dixLookupDrawable(&pDraw, stuff->drawable, client, M_ANY,
|
2012-06-10 07:21:05 -06:00
|
|
|
DixGetAttrAccess);
|
2007-11-24 12:04:00 -07:00
|
|
|
if (rc != Success)
|
2012-06-10 07:21:05 -06:00
|
|
|
return rc;
|
2007-11-24 12:04:00 -07:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
VERIFY_SHMPTR(stuff->shmseg, stuff->offset, TRUE, shmdesc, client);
|
2008-01-17 08:43:43 -07:00
|
|
|
|
|
|
|
width = stuff->width;
|
|
|
|
height = stuff->height;
|
|
|
|
depth = stuff->depth;
|
2012-06-10 07:21:05 -06:00
|
|
|
if (!width || !height || !depth) {
|
|
|
|
client->errorValue = 0;
|
2006-11-26 11:13:41 -07:00
|
|
|
return BadValue;
|
|
|
|
}
|
2008-01-17 08:43:43 -07:00
|
|
|
if (width > 32767 || height > 32767)
|
|
|
|
return BadAlloc;
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (stuff->depth != 1) {
|
2006-11-26 11:13:41 -07:00
|
|
|
pDepth = pDraw->pScreen->allowedDepths;
|
2012-06-10 07:21:05 -06:00
|
|
|
for (i = 0; i < pDraw->pScreen->numDepths; i++, pDepth++)
|
|
|
|
if (pDepth->depth == stuff->depth)
|
|
|
|
goto CreatePmap;
|
|
|
|
client->errorValue = stuff->depth;
|
2006-11-26 11:13:41 -07:00
|
|
|
return BadValue;
|
|
|
|
}
|
2008-01-18 13:53:51 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
CreatePmap:
|
2008-01-18 13:53:51 -07:00
|
|
|
size = PixmapBytePad(width, depth) * height;
|
|
|
|
if (sizeof(size) == 4 && BitsPerPixel(depth) > 8) {
|
|
|
|
if (size < width * height)
|
|
|
|
return BadAlloc;
|
|
|
|
}
|
2008-01-21 14:38:22 -07:00
|
|
|
/* thankfully, offset is unsigned */
|
|
|
|
if (stuff->offset + size < size)
|
2012-06-10 07:21:05 -06:00
|
|
|
return BadAlloc;
|
2008-01-18 13:53:51 -07:00
|
|
|
|
2008-01-17 08:43:43 -07:00
|
|
|
VERIFY_SHMSIZE(shmdesc, stuff->offset, size, client);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (!(newPix = malloc(sizeof(PanoramiXRes))))
|
|
|
|
return BadAlloc;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
newPix->type = XRT_PIXMAP;
|
|
|
|
newPix->u.pix.shared = TRUE;
|
2011-11-05 07:32:40 -06:00
|
|
|
panoramix_setup_ids(newPix, client, stuff->pid);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2010-12-05 08:36:02 -07:00
|
|
|
result = Success;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
FOR_NSCREENS(j) {
|
2012-06-10 07:21:05 -06:00
|
|
|
ShmScrPrivateRec *screen_priv;
|
|
|
|
|
|
|
|
pScreen = screenInfo.screens[j];
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
screen_priv = ShmGetScreenPriv(pScreen);
|
|
|
|
pMap = (*screen_priv->shmFuncs->CreatePixmap) (pScreen,
|
|
|
|
stuff->width,
|
|
|
|
stuff->height,
|
|
|
|
stuff->depth,
|
|
|
|
shmdesc->addr +
|
|
|
|
stuff->offset);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (pMap) {
|
|
|
|
dixSetPrivate(&pMap->devPrivates, shmPixmapPrivateKey, shmdesc);
|
2006-11-26 11:13:41 -07:00
|
|
|
shmdesc->refcnt++;
|
2012-06-10 07:21:05 -06:00
|
|
|
pMap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
|
|
|
|
pMap->drawable.id = newPix->info[j].id;
|
|
|
|
if (!AddResource(newPix->info[j].id, RT_PIXMAP, (pointer) pMap)) {
|
|
|
|
result = BadAlloc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
result = BadAlloc;
|
|
|
|
break;
|
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (result == BadAlloc) {
|
|
|
|
while (j--)
|
|
|
|
FreeResource(newPix->info[j].id, RT_NONE);
|
|
|
|
free(newPix);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
AddResource(stuff->pid, XRT_PIXMAP, newPix);
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static PixmapPtr
|
2012-06-10 07:21:05 -06:00
|
|
|
fbShmCreatePixmap(ScreenPtr pScreen,
|
|
|
|
int width, int height, int depth, char *addr)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
2009-09-06 13:44:18 -06:00
|
|
|
PixmapPtr pPixmap;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
pPixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, pScreen->rootDepth, 0);
|
2006-11-26 11:13:41 -07:00
|
|
|
if (!pPixmap)
|
2012-06-10 07:21:05 -06:00
|
|
|
return NullPixmap;
|
|
|
|
|
|
|
|
if (!(*pScreen->ModifyPixmapHeader) (pPixmap, width, height, depth,
|
|
|
|
BitsPerPixel(depth),
|
|
|
|
PixmapBytePad(width, depth),
|
|
|
|
(pointer) addr)) {
|
|
|
|
(*pScreen->DestroyPixmap) (pPixmap);
|
|
|
|
return NullPixmap;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
return pPixmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
ProcShmCreatePixmap(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
PixmapPtr pMap;
|
2007-11-24 12:04:00 -07:00
|
|
|
DrawablePtr pDraw;
|
2006-11-26 11:13:41 -07:00
|
|
|
DepthPtr pDepth;
|
2009-09-06 13:44:18 -06:00
|
|
|
int i, rc;
|
2006-11-26 11:13:41 -07:00
|
|
|
ShmDescPtr shmdesc;
|
2010-07-27 13:02:24 -06:00
|
|
|
ShmScrPrivateRec *screen_priv;
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST(xShmCreatePixmapReq);
|
2008-01-17 08:43:43 -07:00
|
|
|
unsigned int width, height, depth;
|
|
|
|
unsigned long size;
|
2006-11-26 11:13:41 -07:00
|
|
|
|
|
|
|
REQUEST_SIZE_MATCH(xShmCreatePixmapReq);
|
|
|
|
client->errorValue = stuff->pid;
|
|
|
|
if (!sharedPixmaps)
|
2012-06-10 07:21:05 -06:00
|
|
|
return BadImplementation;
|
2006-11-26 11:13:41 -07:00
|
|
|
LEGAL_NEW_RESOURCE(stuff->pid, client);
|
2007-11-24 12:04:00 -07:00
|
|
|
rc = dixLookupDrawable(&pDraw, stuff->drawable, client, M_ANY,
|
2012-06-10 07:21:05 -06:00
|
|
|
DixGetAttrAccess);
|
2007-11-24 12:04:00 -07:00
|
|
|
if (rc != Success)
|
2012-06-10 07:21:05 -06:00
|
|
|
return rc;
|
2007-11-24 12:04:00 -07:00
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
VERIFY_SHMPTR(stuff->shmseg, stuff->offset, TRUE, shmdesc, client);
|
2012-06-10 07:21:05 -06:00
|
|
|
|
2008-01-17 08:43:43 -07:00
|
|
|
width = stuff->width;
|
|
|
|
height = stuff->height;
|
|
|
|
depth = stuff->depth;
|
2012-06-10 07:21:05 -06:00
|
|
|
if (!width || !height || !depth) {
|
|
|
|
client->errorValue = 0;
|
2006-11-26 11:13:41 -07:00
|
|
|
return BadValue;
|
|
|
|
}
|
2008-01-17 08:43:43 -07:00
|
|
|
if (width > 32767 || height > 32767)
|
2012-06-10 07:21:05 -06:00
|
|
|
return BadAlloc;
|
2008-01-17 08:43:43 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
if (stuff->depth != 1) {
|
2006-11-26 11:13:41 -07:00
|
|
|
pDepth = pDraw->pScreen->allowedDepths;
|
2012-06-10 07:21:05 -06:00
|
|
|
for (i = 0; i < pDraw->pScreen->numDepths; i++, pDepth++)
|
|
|
|
if (pDepth->depth == stuff->depth)
|
|
|
|
goto CreatePmap;
|
|
|
|
client->errorValue = stuff->depth;
|
2006-11-26 11:13:41 -07:00
|
|
|
return BadValue;
|
|
|
|
}
|
2008-01-18 13:53:51 -07:00
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
CreatePmap:
|
2008-01-18 13:53:51 -07:00
|
|
|
size = PixmapBytePad(width, depth) * height;
|
|
|
|
if (sizeof(size) == 4 && BitsPerPixel(depth) > 8) {
|
2012-06-10 07:21:05 -06:00
|
|
|
if (size < width * height)
|
|
|
|
return BadAlloc;
|
2008-01-18 13:53:51 -07:00
|
|
|
}
|
2008-01-21 14:38:22 -07:00
|
|
|
/* thankfully, offset is unsigned */
|
|
|
|
if (stuff->offset + size < size)
|
2012-06-10 07:21:05 -06:00
|
|
|
return BadAlloc;
|
2008-01-18 13:53:51 -07:00
|
|
|
|
2008-01-17 08:43:43 -07:00
|
|
|
VERIFY_SHMSIZE(shmdesc, stuff->offset, size, client);
|
2010-07-27 13:02:24 -06:00
|
|
|
screen_priv = ShmGetScreenPriv(pDraw->pScreen);
|
2012-06-10 07:21:05 -06:00
|
|
|
pMap = (*screen_priv->shmFuncs->CreatePixmap) (pDraw->pScreen, stuff->width,
|
|
|
|
stuff->height, stuff->depth,
|
|
|
|
shmdesc->addr +
|
|
|
|
stuff->offset);
|
|
|
|
if (pMap) {
|
|
|
|
rc = XaceHook(XACE_RESOURCE_ACCESS, client, stuff->pid, RT_PIXMAP,
|
|
|
|
pMap, RT_NONE, NULL, DixCreateAccess);
|
|
|
|
if (rc != Success) {
|
|
|
|
pDraw->pScreen->DestroyPixmap(pMap);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
dixSetPrivate(&pMap->devPrivates, shmPixmapPrivateKey, shmdesc);
|
|
|
|
shmdesc->refcnt++;
|
|
|
|
pMap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
|
|
|
|
pMap->drawable.id = stuff->pid;
|
|
|
|
if (AddResource(stuff->pid, RT_PIXMAP, (pointer) pMap)) {
|
|
|
|
return Success;
|
|
|
|
}
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
2010-12-05 08:36:02 -07:00
|
|
|
return BadAlloc;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
|
2014-05-02 13:27:46 -06:00
|
|
|
#ifdef SHM_FD_PASSING
|
|
|
|
|
|
|
|
static void
|
|
|
|
ShmBusfaultNotify(void *context)
|
|
|
|
{
|
|
|
|
ShmDescPtr shmdesc = context;
|
|
|
|
|
|
|
|
ErrorF("shared memory 0x%x truncated by client\n",
|
|
|
|
(unsigned int) shmdesc->resource);
|
|
|
|
busfault_unregister(shmdesc->busfault);
|
|
|
|
shmdesc->busfault = NULL;
|
|
|
|
FreeResource (shmdesc->resource, RT_NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ProcShmAttachFd(ClientPtr client)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
ShmDescPtr shmdesc;
|
|
|
|
REQUEST(xShmAttachFdReq);
|
|
|
|
struct stat statb;
|
|
|
|
|
|
|
|
SetReqFds(client, 1);
|
|
|
|
REQUEST_SIZE_MATCH(xShmAttachFdReq);
|
|
|
|
LEGAL_NEW_RESOURCE(stuff->shmseg, client);
|
|
|
|
if ((stuff->readOnly != xTrue) && (stuff->readOnly != xFalse)) {
|
|
|
|
client->errorValue = stuff->readOnly;
|
|
|
|
return BadValue;
|
|
|
|
}
|
|
|
|
fd = ReadFdFromClient(client);
|
|
|
|
if (fd < 0)
|
|
|
|
return BadMatch;
|
|
|
|
|
|
|
|
if (fstat(fd, &statb) < 0 || statb.st_size == 0) {
|
|
|
|
close(fd);
|
|
|
|
return BadMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
shmdesc = malloc(sizeof(ShmDescRec));
|
|
|
|
if (!shmdesc) {
|
|
|
|
close(fd);
|
|
|
|
return BadAlloc;
|
|
|
|
}
|
|
|
|
shmdesc->is_fd = TRUE;
|
|
|
|
shmdesc->addr = mmap(NULL, statb.st_size,
|
|
|
|
stuff->readOnly ? PROT_READ : PROT_READ|PROT_WRITE,
|
|
|
|
MAP_SHARED,
|
|
|
|
fd, 0);
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
if ((shmdesc->addr == ((char *) -1))) {
|
|
|
|
free(shmdesc);
|
|
|
|
return BadAccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
shmdesc->refcnt = 1;
|
|
|
|
shmdesc->writable = !stuff->readOnly;
|
|
|
|
shmdesc->size = statb.st_size;
|
|
|
|
shmdesc->resource = stuff->shmseg;
|
|
|
|
|
|
|
|
shmdesc->busfault = busfault_register_mmap(shmdesc->addr, shmdesc->size, ShmBusfaultNotify, shmdesc);
|
|
|
|
if (!shmdesc->busfault) {
|
|
|
|
munmap(shmdesc->addr, shmdesc->size);
|
|
|
|
free(shmdesc);
|
|
|
|
return BadAlloc;
|
|
|
|
}
|
|
|
|
|
|
|
|
shmdesc->next = Shmsegs;
|
|
|
|
Shmsegs = shmdesc;
|
|
|
|
|
|
|
|
if (!AddResource(stuff->shmseg, ShmSegType, (pointer) shmdesc))
|
|
|
|
return BadAlloc;
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
shm_tmpfile(void)
|
|
|
|
{
|
|
|
|
#ifdef SHMDIR
|
|
|
|
int fd;
|
|
|
|
int flags;
|
|
|
|
char template[] = SHMDIR "/shmfd-XXXXXX";
|
|
|
|
#ifdef O_TMPFILE
|
|
|
|
fd = open(SHMDIR, O_TMPFILE|O_RDWR|O_CLOEXEC|O_EXCL, 0666);
|
|
|
|
if (fd >= 0) {
|
|
|
|
ErrorF ("Using O_TMPFILE\n");
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
ErrorF ("Not using O_TMPFILE\n");
|
|
|
|
#endif
|
|
|
|
fd = mkstemp(template);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
unlink(template);
|
|
|
|
if (fcntl(fd, F_GETFD, &flags) >= 0) {
|
|
|
|
flags |= FD_CLOEXEC;
|
|
|
|
(void) fcntl(fd, F_SETFD, &flags);
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
#else
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ProcShmCreateSegment(ClientPtr client)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
ShmDescPtr shmdesc;
|
|
|
|
REQUEST(xShmCreateSegmentReq);
|
|
|
|
xShmCreateSegmentReply rep = {
|
|
|
|
.type = X_Reply,
|
|
|
|
.nfd = 1,
|
|
|
|
.sequenceNumber = client->sequence,
|
|
|
|
.length = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
REQUEST_SIZE_MATCH(xShmCreateSegmentReq);
|
|
|
|
if ((stuff->readOnly != xTrue) && (stuff->readOnly != xFalse)) {
|
|
|
|
client->errorValue = stuff->readOnly;
|
|
|
|
return BadValue;
|
|
|
|
}
|
|
|
|
fd = shm_tmpfile();
|
|
|
|
if (fd < 0)
|
|
|
|
return BadAlloc;
|
|
|
|
if (ftruncate(fd, stuff->size) < 0) {
|
|
|
|
close(fd);
|
|
|
|
return BadAlloc;
|
|
|
|
}
|
|
|
|
shmdesc = malloc(sizeof(ShmDescRec));
|
|
|
|
if (!shmdesc) {
|
|
|
|
close(fd);
|
|
|
|
return BadAlloc;
|
|
|
|
}
|
|
|
|
shmdesc->is_fd = TRUE;
|
|
|
|
shmdesc->addr = mmap(NULL, stuff->size,
|
|
|
|
stuff->readOnly ? PROT_READ : PROT_READ|PROT_WRITE,
|
|
|
|
MAP_SHARED,
|
|
|
|
fd, 0);
|
|
|
|
|
|
|
|
if ((shmdesc->addr == ((char *) -1))) {
|
|
|
|
close(fd);
|
|
|
|
free(shmdesc);
|
|
|
|
return BadAccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
shmdesc->refcnt = 1;
|
|
|
|
shmdesc->writable = !stuff->readOnly;
|
|
|
|
shmdesc->size = stuff->size;
|
|
|
|
|
|
|
|
shmdesc->busfault = busfault_register_mmap(shmdesc->addr, shmdesc->size, ShmBusfaultNotify, shmdesc);
|
|
|
|
if (!shmdesc->busfault) {
|
|
|
|
close(fd);
|
|
|
|
munmap(shmdesc->addr, shmdesc->size);
|
|
|
|
free(shmdesc);
|
|
|
|
return BadAlloc;
|
|
|
|
}
|
|
|
|
|
|
|
|
shmdesc->next = Shmsegs;
|
|
|
|
Shmsegs = shmdesc;
|
|
|
|
|
|
|
|
if (!AddResource(stuff->shmseg, ShmSegType, (pointer) shmdesc)) {
|
|
|
|
close(fd);
|
|
|
|
return BadAlloc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WriteFdToClient(client, fd, TRUE) < 0) {
|
|
|
|
FreeResource(stuff->shmseg, RT_NONE);
|
|
|
|
close(fd);
|
|
|
|
return BadAlloc;
|
|
|
|
}
|
|
|
|
WriteToClient(client, sizeof (xShmCreateSegmentReply), &rep);
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
#endif /* SHM_FD_PASSING */
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
static int
|
2012-06-10 07:21:05 -06:00
|
|
|
ProcShmDispatch(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
REQUEST(xReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
switch (stuff->data) {
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmQueryVersion:
|
2012-06-10 07:21:05 -06:00
|
|
|
return ProcShmQueryVersion(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmAttach:
|
2012-06-10 07:21:05 -06:00
|
|
|
return ProcShmAttach(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmDetach:
|
2012-06-10 07:21:05 -06:00
|
|
|
return ProcShmDetach(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmPutImage:
|
|
|
|
#ifdef PANORAMIX
|
2012-06-10 07:21:05 -06:00
|
|
|
if (!noPanoramiXExtension)
|
|
|
|
return ProcPanoramiXShmPutImage(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
#endif
|
2012-06-10 07:21:05 -06:00
|
|
|
return ProcShmPutImage(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmGetImage:
|
|
|
|
#ifdef PANORAMIX
|
2012-06-10 07:21:05 -06:00
|
|
|
if (!noPanoramiXExtension)
|
|
|
|
return ProcPanoramiXShmGetImage(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
#endif
|
2012-06-10 07:21:05 -06:00
|
|
|
return ProcShmGetImage(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmCreatePixmap:
|
|
|
|
#ifdef PANORAMIX
|
2012-06-10 07:21:05 -06:00
|
|
|
if (!noPanoramiXExtension)
|
|
|
|
return ProcPanoramiXShmCreatePixmap(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
#endif
|
2012-06-10 07:21:05 -06:00
|
|
|
return ProcShmCreatePixmap(client);
|
2014-05-02 13:27:46 -06:00
|
|
|
#ifdef SHM_FD_PASSING
|
|
|
|
case X_ShmAttachFd:
|
|
|
|
return ProcShmAttachFd(client);
|
|
|
|
case X_ShmCreateSegment:
|
|
|
|
return ProcShmCreateSegment(client);
|
|
|
|
#endif
|
2006-11-26 11:13:41 -07:00
|
|
|
default:
|
2012-06-10 07:21:05 -06:00
|
|
|
return BadRequest;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-10 07:21:05 -06:00
|
|
|
SShmCompletionEvent(xShmCompletionEvent * from, xShmCompletionEvent * to)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
to->type = from->type;
|
|
|
|
cpswaps(from->sequenceNumber, to->sequenceNumber);
|
|
|
|
cpswapl(from->drawable, to->drawable);
|
|
|
|
cpswaps(from->minorEvent, to->minorEvent);
|
|
|
|
to->majorEvent = from->majorEvent;
|
|
|
|
cpswapl(from->shmseg, to->shmseg);
|
|
|
|
cpswapl(from->offset, to->offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
SProcShmQueryVersion(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
REQUEST(xShmQueryVersionReq);
|
|
|
|
|
2012-06-10 07:21:05 -06:00
|
|
|
swaps(&stuff->length);
|
2006-11-26 11:13:41 -07:00
|
|
|
return ProcShmQueryVersion(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
SProcShmAttach(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
REQUEST(xShmAttachReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
swaps(&stuff->length);
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST_SIZE_MATCH(xShmAttachReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
swapl(&stuff->shmseg);
|
|
|
|
swapl(&stuff->shmid);
|
2006-11-26 11:13:41 -07:00
|
|
|
return ProcShmAttach(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
SProcShmDetach(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
REQUEST(xShmDetachReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
swaps(&stuff->length);
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST_SIZE_MATCH(xShmDetachReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
swapl(&stuff->shmseg);
|
2006-11-26 11:13:41 -07:00
|
|
|
return ProcShmDetach(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
SProcShmPutImage(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
REQUEST(xShmPutImageReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
swaps(&stuff->length);
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST_SIZE_MATCH(xShmPutImageReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
swapl(&stuff->drawable);
|
|
|
|
swapl(&stuff->gc);
|
|
|
|
swaps(&stuff->totalWidth);
|
|
|
|
swaps(&stuff->totalHeight);
|
|
|
|
swaps(&stuff->srcX);
|
|
|
|
swaps(&stuff->srcY);
|
|
|
|
swaps(&stuff->srcWidth);
|
|
|
|
swaps(&stuff->srcHeight);
|
|
|
|
swaps(&stuff->dstX);
|
|
|
|
swaps(&stuff->dstY);
|
|
|
|
swapl(&stuff->shmseg);
|
|
|
|
swapl(&stuff->offset);
|
2006-11-26 11:13:41 -07:00
|
|
|
return ProcShmPutImage(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
SProcShmGetImage(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
REQUEST(xShmGetImageReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
swaps(&stuff->length);
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST_SIZE_MATCH(xShmGetImageReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
swapl(&stuff->drawable);
|
|
|
|
swaps(&stuff->x);
|
|
|
|
swaps(&stuff->y);
|
|
|
|
swaps(&stuff->width);
|
|
|
|
swaps(&stuff->height);
|
|
|
|
swapl(&stuff->planeMask);
|
|
|
|
swapl(&stuff->shmseg);
|
|
|
|
swapl(&stuff->offset);
|
2006-11-26 11:13:41 -07:00
|
|
|
return ProcShmGetImage(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-09-06 13:44:18 -06:00
|
|
|
SProcShmCreatePixmap(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
REQUEST(xShmCreatePixmapReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
swaps(&stuff->length);
|
2006-11-26 11:13:41 -07:00
|
|
|
REQUEST_SIZE_MATCH(xShmCreatePixmapReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
swapl(&stuff->pid);
|
|
|
|
swapl(&stuff->drawable);
|
|
|
|
swaps(&stuff->width);
|
|
|
|
swaps(&stuff->height);
|
|
|
|
swapl(&stuff->shmseg);
|
|
|
|
swapl(&stuff->offset);
|
2006-11-26 11:13:41 -07:00
|
|
|
return ProcShmCreatePixmap(client);
|
|
|
|
}
|
|
|
|
|
2014-05-02 13:27:46 -06:00
|
|
|
#ifdef SHM_FD_PASSING
|
|
|
|
static int
|
|
|
|
SProcShmAttachFd(ClientPtr client)
|
|
|
|
{
|
|
|
|
REQUEST(xShmAttachFdReq);
|
|
|
|
SetReqFds(client, 1);
|
|
|
|
swaps(&stuff->length);
|
|
|
|
REQUEST_SIZE_MATCH(xShmAttachFdReq);
|
|
|
|
swapl(&stuff->shmseg);
|
|
|
|
return ProcShmAttachFd(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
SProcShmCreateSegment(ClientPtr client)
|
|
|
|
{
|
|
|
|
REQUEST(xShmCreateSegmentReq);
|
|
|
|
swaps(&stuff->length);
|
|
|
|
REQUEST_SIZE_MATCH(xShmCreateSegmentReq);
|
|
|
|
swapl(&stuff->shmseg);
|
|
|
|
swapl(&stuff->size);
|
|
|
|
return ProcShmCreateSegment(client);
|
|
|
|
}
|
|
|
|
#endif /* SHM_FD_PASSING */
|
|
|
|
|
2006-11-26 11:13:41 -07:00
|
|
|
static int
|
2012-06-10 07:21:05 -06:00
|
|
|
SProcShmDispatch(ClientPtr client)
|
2006-11-26 11:13:41 -07:00
|
|
|
{
|
|
|
|
REQUEST(xReq);
|
2012-06-10 07:21:05 -06:00
|
|
|
switch (stuff->data) {
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmQueryVersion:
|
2012-06-10 07:21:05 -06:00
|
|
|
return SProcShmQueryVersion(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmAttach:
|
2012-06-10 07:21:05 -06:00
|
|
|
return SProcShmAttach(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmDetach:
|
2012-06-10 07:21:05 -06:00
|
|
|
return SProcShmDetach(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmPutImage:
|
2012-06-10 07:21:05 -06:00
|
|
|
return SProcShmPutImage(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmGetImage:
|
2012-06-10 07:21:05 -06:00
|
|
|
return SProcShmGetImage(client);
|
2006-11-26 11:13:41 -07:00
|
|
|
case X_ShmCreatePixmap:
|
2012-06-10 07:21:05 -06:00
|
|
|
return SProcShmCreatePixmap(client);
|
2014-05-02 13:27:46 -06:00
|
|
|
#ifdef SHM_FD_PASSING
|
|
|
|
case X_ShmAttachFd:
|
|
|
|
return SProcShmAttachFd(client);
|
|
|
|
case X_ShmCreateSegment:
|
|
|
|
return SProcShmCreateSegment(client);
|
|
|
|
#endif
|
2006-11-26 11:13:41 -07:00
|
|
|
default:
|
2012-06-10 07:21:05 -06:00
|
|
|
return BadRequest;
|
2006-11-26 11:13:41 -07:00
|
|
|
}
|
|
|
|
}
|
2011-11-05 07:32:40 -06:00
|
|
|
|
|
|
|
void
|
2013-06-07 11:28:45 -06:00
|
|
|
ShmExtensionInit(void)
|
2011-11-05 07:32:40 -06:00
|
|
|
{
|
|
|
|
ExtensionEntry *extEntry;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
#ifdef MUST_CHECK_FOR_SHM_SYSCALL
|
2012-06-10 07:21:05 -06:00
|
|
|
if (!CheckForShmSyscall()) {
|
|
|
|
ErrorF("MIT-SHM extension disabled due to lack of kernel support\n");
|
|
|
|
return;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!ShmRegisterPrivates())
|
2012-06-10 07:21:05 -06:00
|
|
|
return;
|
2011-11-05 07:32:40 -06:00
|
|
|
|
|
|
|
sharedPixmaps = xFalse;
|
|
|
|
{
|
2012-06-10 07:21:05 -06:00
|
|
|
sharedPixmaps = xTrue;
|
|
|
|
for (i = 0; i < screenInfo.numScreens; i++) {
|
|
|
|
ShmScrPrivateRec *screen_priv =
|
|
|
|
ShmInitScreenPriv(screenInfo.screens[i]);
|
|
|
|
if (!screen_priv->shmFuncs)
|
|
|
|
screen_priv->shmFuncs = &miFuncs;
|
|
|
|
if (!screen_priv->shmFuncs->CreatePixmap)
|
|
|
|
sharedPixmaps = xFalse;
|
|
|
|
}
|
|
|
|
if (sharedPixmaps)
|
|
|
|
for (i = 0; i < screenInfo.numScreens; i++) {
|
|
|
|
ShmScrPrivateRec *screen_priv =
|
|
|
|
ShmGetScreenPriv(screenInfo.screens[i]);
|
|
|
|
screen_priv->destroyPixmap =
|
|
|
|
screenInfo.screens[i]->DestroyPixmap;
|
|
|
|
screenInfo.screens[i]->DestroyPixmap = ShmDestroyPixmap;
|
|
|
|
}
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
|
|
|
ShmSegType = CreateNewResourceType(ShmDetachSegment, "ShmSeg");
|
|
|
|
if (ShmSegType &&
|
2012-06-10 07:21:05 -06:00
|
|
|
(extEntry = AddExtension(SHMNAME, ShmNumberEvents, ShmNumberErrors,
|
|
|
|
ProcShmDispatch, SProcShmDispatch,
|
|
|
|
ShmResetProc, StandardMinorOpcode))) {
|
|
|
|
ShmReqCode = (unsigned char) extEntry->base;
|
|
|
|
ShmCompletionCode = extEntry->eventBase;
|
|
|
|
BadShmSegCode = extEntry->errorBase;
|
|
|
|
SetResourceTypeErrorValue(ShmSegType, BadShmSegCode);
|
|
|
|
EventSwapVector[ShmCompletionCode] = (EventSwapPtr) SShmCompletionEvent;
|
2011-11-05 07:32:40 -06:00
|
|
|
}
|
|
|
|
}
|