From fdec793cdc2ef9a6ea66b311cb1068a7bd4a3be3 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 17 Jan 2013 13:46:55 -0800 Subject: Add support for MIT-SHM AttachFd request This passes a file descriptor from the client to the server, which is then mmap'd Signed-off-by: Keith Packard Reviewed-by: Adam Jackson --- Xext/shm.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- Xext/shmint.h | 1 + include/os.h | 2 + os/io.c | 8 +++ 4 files changed, 167 insertions(+), 3 deletions(-) diff --git a/Xext/shm.c b/Xext/shm.c index f6995ccfb..1a70260fb 100644 --- a/Xext/shm.c +++ b/Xext/shm.c @@ -53,6 +53,7 @@ in this Software without prior written authorization from The Open Group. #include "xace.h" #include #include +#include #include "protocol-versions.h" /* Needed for Solaris cross-zone shared memory extension */ @@ -382,8 +383,10 @@ ProcShmAttach(ClientPtr client) client->errorValue = stuff->readOnly; return BadValue; } - for (shmdesc = Shmsegs; - shmdesc && (shmdesc->shmid != stuff->shmid); shmdesc = shmdesc->next); + for (shmdesc = Shmsegs; shmdesc; shmdesc = shmdesc->next) { + if (!shmdesc->is_fd && shmdesc->shmid == stuff->shmid) + break; + } if (shmdesc) { if (!stuff->readOnly && !shmdesc->writable) return BadAccess; @@ -393,6 +396,7 @@ ProcShmAttach(ClientPtr client) shmdesc = malloc(sizeof(ShmDescRec)); if (!shmdesc) return BadAlloc; + shmdesc->is_fd = FALSE; shmdesc->addr = shmat(stuff->shmid, 0, stuff->readOnly ? SHM_RDONLY : 0); if ((shmdesc->addr == ((char *) -1)) || SHMSTAT(stuff->shmid, &buf)) { @@ -431,7 +435,10 @@ ShmDetachSegment(pointer value, /* must conform to DeleteType */ if (--shmdesc->refcnt) return TRUE; - shmdt(shmdesc->addr); + if (shmdesc->is_fd) + munmap(shmdesc->addr, shmdesc->size); + else + shmdt(shmdesc->addr); for (prev = &Shmsegs; *prev != shmdesc; prev = &(*prev)->next); *prev = shmdesc->next; free(shmdesc); @@ -1087,6 +1094,122 @@ ProcShmCreatePixmap(ClientPtr client) return BadAlloc; } +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->next = Shmsegs; + Shmsegs = shmdesc; + + if (!AddResource(stuff->shmseg, ShmSegType, (pointer) shmdesc)) + return BadAlloc; + return Success; +} + +static int +ProcShmCreateSegment(ClientPtr client) +{ + int fd; + ShmDescPtr shmdesc; + REQUEST(xShmCreateSegmentReq); + xShmCreateSegmentReply rep = { + .type = X_Reply, + .nfd = 1, + .sequenceNumber = client->sequence, + .length = 0, + }; + char template[] = "/tmp/shm-XXXXXX"; + + REQUEST_SIZE_MATCH(xShmCreateSegmentReq); + if ((stuff->readOnly != xTrue) && (stuff->readOnly != xFalse)) { + client->errorValue = stuff->readOnly; + return BadValue; + } + fd = mkstemp(template); + if (fd < 0) + return BadAlloc; + unlink(template); + 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->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; +} + static int ProcShmDispatch(ClientPtr client) { @@ -1116,6 +1239,10 @@ ProcShmDispatch(ClientPtr client) return ProcPanoramiXShmCreatePixmap(client); #endif return ProcShmCreatePixmap(client); + case X_ShmAttachFd: + return ProcShmAttachFd(client); + case X_ShmCreateSegment: + return ProcShmCreateSegment(client); default: return BadRequest; } @@ -1216,6 +1343,28 @@ SProcShmCreatePixmap(ClientPtr client) return ProcShmCreatePixmap(client); } +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); +} + static int SProcShmDispatch(ClientPtr client) { @@ -1233,6 +1382,10 @@ SProcShmDispatch(ClientPtr client) return SProcShmGetImage(client); case X_ShmCreatePixmap: return SProcShmCreatePixmap(client); + case X_ShmAttachFd: + return SProcShmAttachFd(client); + case X_ShmCreateSegment: + return SProcShmCreateSegment(client); default: return BadRequest; } diff --git a/Xext/shmint.h b/Xext/shmint.h index 9002ce501..db35fbbbe 100644 --- a/Xext/shmint.h +++ b/Xext/shmint.h @@ -61,6 +61,7 @@ typedef struct _ShmDesc { int shmid; int refcnt; char *addr; + Bool is_fd; Bool writable; unsigned long size; } ShmDescRec, *ShmDescPtr; diff --git a/include/os.h b/include/os.h index b654a0d45..11b219845 100644 --- a/include/os.h +++ b/include/os.h @@ -100,6 +100,8 @@ extern _X_EXPORT int ReadRequestFromClient(ClientPtr /*client */ ); extern _X_EXPORT int ReadFdFromClient(ClientPtr client); +extern _X_EXPORT int WriteFdToClient(ClientPtr client, int fd, Bool do_close); + extern _X_EXPORT Bool InsertFakeRequest(ClientPtr /*client */ , char * /*data */ , int /*count */ ); diff --git a/os/io.c b/os/io.c index 83df6e9b0..a20faa56f 100644 --- a/os/io.c +++ b/os/io.c @@ -506,6 +506,14 @@ ReadFdFromClient(ClientPtr client) return fd; } +int +WriteFdToClient(ClientPtr client, int fd, Bool do_close) +{ + OsCommPtr oc = (OsCommPtr) client->osPrivate; + + return _XSERVTransSendFd(oc->trans_conn, fd, do_close); +} + /***************************************************************** * InsertFakeRequest * Splice a consed up (possibly partial) request in as the next request. -- cgit v1.2.3