@ -33,6 +33,7 @@
# include <sys/epoll.h>
# include <string.h>
# include <stdlib.h>
# include <libweston/zalloc.h>
# ifdef HAVE_MEMFD_CREATE
# include <sys/mman.h>
@ -40,6 +41,8 @@
# include "os-compatibility.h"
# define READONLY_SEALS (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
int
os_fd_set_cloexec ( int fd )
{
@ -233,3 +236,172 @@ strchrnul(const char *s, int c)
return ( char * ) s ;
}
# endif
struct ro_anonymous_file {
int fd ;
size_t size ;
} ;
/** Create a new anonymous read-only file of the given size and the given data
*
* \ param size The size of \ p data .
* \ param data The data of the file with the size \ p size .
* \ return A new \ c ro_anonymous_file , or NULL on failure .
*
* The intended use - case is for sending mid - sized data from the compositor
* to clients .
* If the function fails errno is set .
*/
struct ro_anonymous_file *
os_ro_anonymous_file_create ( size_t size ,
const char * data )
{
struct ro_anonymous_file * file ;
void * map ;
file = zalloc ( sizeof * file ) ;
if ( ! file ) {
errno = ENOMEM ;
return NULL ;
}
file - > size = size ;
file - > fd = os_create_anonymous_file ( size ) ;
if ( file - > fd = = - 1 )
goto err_free ;
map = mmap ( NULL , size , PROT_READ | PROT_WRITE , MAP_SHARED , file - > fd , 0 ) ;
if ( map = = MAP_FAILED )
goto err_close ;
memcpy ( map , data , size ) ;
munmap ( map , size ) ;
# ifdef HAVE_MEMFD_CREATE
/* try to put seals on the file to make it read-only so that we can
* return the fd later directly when support_shared is not set .
* os_ro_anonymous_file_get_fd can handle the fd even if it is not
* sealed read - only and will instead create a new anonymous file on
* each invocation .
*/
fcntl ( file - > fd , F_ADD_SEALS , READONLY_SEALS ) ;
# endif
return file ;
err_close :
close ( file - > fd ) ;
err_free :
free ( file ) ;
return NULL ;
}
/** Destroy an anonymous read-only file
*
* \ param file The file to destroy .
*/
void
os_ro_anonymous_file_destroy ( struct ro_anonymous_file * file )
{
close ( file - > fd ) ;
free ( file ) ;
}
/** Get the size of an anonymous read-only file
*
* \ param file The file to get the size of .
* \ return The size of the file .
*/
size_t
os_ro_anonymous_file_size ( struct ro_anonymous_file * file )
{
return file - > size ;
}
/** Returns a file descriptor for the given file, ready to be send to a client.
*
* \ param file The file for which to get a file descriptor .
* \ param mapmode Describes the ways in which the returned file descriptor can
* be used with mmap .
* \ return A file descriptor for the given file that can be send to a client
* or - 1 on failure .
*
* The returned file descriptor must not be shared between multiple clients .
* When \ p mapmode is RO_ANONYMOUS_FILE_MAPMODE_PRIVATE the file descriptor is
* only guaranteed to be mmapable with \ c MAP_PRIVATE , when \ p mapmode is
* RO_ANONYMOUS_FILE_MAPMODE_SHARED the file descriptor can be mmaped with
* either MAP_PRIVATE or MAP_SHARED .
* When you ' re done with the fd you must call \ c os_ro_anonymous_file_put_fd
* instead of calling \ c close .
* If the function fails errno is set .
*/
int
os_ro_anonymous_file_get_fd ( struct ro_anonymous_file * file ,
enum ro_anonymous_file_mapmode mapmode )
{
void * src , * dst ;
int seals , fd ;
seals = fcntl ( file - > fd , F_GET_SEALS ) ;
/* file was sealed for read-only and we don't have to support MAP_SHARED
* so we can simply pass the memfd fd
*/
if ( seals ! = - 1 & & mapmode = = RO_ANONYMOUS_FILE_MAPMODE_PRIVATE & &
( seals & READONLY_SEALS ) = = READONLY_SEALS )
return file - > fd ;
/* for all other cases we create a new anonymous file that can be mapped
* with MAP_SHARED and copy the contents to it and return that instead
*/
fd = os_create_anonymous_file ( file - > size ) ;
if ( fd = = - 1 )
return fd ;
src = mmap ( NULL , file - > size , PROT_READ , MAP_PRIVATE , file - > fd , 0 ) ;
if ( src = = MAP_FAILED ) {
close ( fd ) ;
return - 1 ;
}
dst = mmap ( NULL , file - > size , PROT_WRITE , MAP_SHARED , fd , 0 ) ;
if ( dst = = MAP_FAILED ) {
close ( fd ) ;
munmap ( src , file - > size ) ;
return - 1 ;
}
memcpy ( dst , src , file - > size ) ;
munmap ( src , file - > size ) ;
munmap ( dst , file - > size ) ;
return fd ;
}
/** Release a file descriptor returned by \c os_ro_anonymous_file_get_fd
*
* \ param fd A file descriptor returned by \ c os_ro_anonymous_file_get_fd .
* \ return 0 on success , or - 1 on failure .
*
* This function must be called for every file descriptor created with
* \ c os_ro_anonymous_file_get_fd to not leake any resources .
* If the function fails errno is set .
*/
int
os_ro_anonymous_file_put_fd ( int fd )
{
int seals = fcntl ( fd , F_GET_SEALS ) ;
if ( seals = = - 1 & & errno ! = EINVAL )
return - 1 ;
/* If the fd cannot be sealed seals is -1 at this point
* or the file can be sealed but has not been sealed for writing .
* In both cases we created a new anonymous file that we have to
* close .
*/
if ( seals = = - 1 | | ! ( seals & F_SEAL_WRITE ) )
close ( fd ) ;
return 0 ;
}