From 62a4fef29236501c7d83de0b71da70704ed907b5 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 27 Jan 2016 12:31:07 +0100 Subject: [PATCH] Imported Upstream version 2016.1 --- bsdiff | 1 - bsdiff/.gitignore | 11 + bsdiff/LICENSE | 24 + bsdiff/Makefile-bsdiff.am | 32 ++ bsdiff/Makefile.am | 11 + bsdiff/README.md | 129 +++++ bsdiff/autogen.sh | 6 + bsdiff/bsdiff.c | 445 ++++++++++++++++ bsdiff/bsdiff.h | 45 ++ bsdiff/bspatch.c | 187 +++++++ bsdiff/bspatch.h | 42 ++ bsdiff/configure.ac | 30 ++ libglnx | 1 - libglnx/.gitignore | 16 + libglnx/COPYING | 481 +++++++++++++++++ libglnx/Makefile-libglnx.am | 48 ++ libglnx/README.md | 53 ++ libglnx/glnx-backport-autocleanups.h | 117 +++++ libglnx/glnx-backport-autoptr.h | 133 +++++ libglnx/glnx-backports.c | 61 +++ libglnx/glnx-backports.h | 46 ++ libglnx/glnx-console.c | 289 +++++++++++ libglnx/glnx-console.h | 52 ++ libglnx/glnx-dirfd.c | 305 +++++++++++ libglnx/glnx-dirfd.h | 85 +++ libglnx/glnx-errors.c | 53 ++ libglnx/glnx-errors.h | 49 ++ libglnx/glnx-fdio.c | 750 +++++++++++++++++++++++++++ libglnx/glnx-fdio.h | 121 +++++ libglnx/glnx-libcontainer.c | 292 +++++++++++ libglnx/glnx-libcontainer.h | 36 ++ libglnx/glnx-local-alloc.c | 72 +++ libglnx/glnx-local-alloc.h | 222 ++++++++ libglnx/glnx-lockfile.c | 193 +++++++ libglnx/glnx-lockfile.h | 41 ++ libglnx/glnx-shutil.c | 287 ++++++++++ libglnx/glnx-shutil.h | 40 ++ libglnx/glnx-xattrs.c | 526 +++++++++++++++++++ libglnx/glnx-xattrs.h | 78 +++ libglnx/libglnx.doap | 31 ++ libglnx/libglnx.h | 39 ++ 41 files changed, 5478 insertions(+), 2 deletions(-) delete mode 160000 bsdiff create mode 100644 bsdiff/.gitignore create mode 100644 bsdiff/LICENSE create mode 100644 bsdiff/Makefile-bsdiff.am create mode 100644 bsdiff/Makefile.am create mode 100644 bsdiff/README.md create mode 100755 bsdiff/autogen.sh create mode 100644 bsdiff/bsdiff.c create mode 100644 bsdiff/bsdiff.h create mode 100644 bsdiff/bspatch.c create mode 100644 bsdiff/bspatch.h create mode 100644 bsdiff/configure.ac delete mode 160000 libglnx create mode 100644 libglnx/.gitignore create mode 100644 libglnx/COPYING create mode 100644 libglnx/Makefile-libglnx.am create mode 100644 libglnx/README.md create mode 100644 libglnx/glnx-backport-autocleanups.h create mode 100644 libglnx/glnx-backport-autoptr.h create mode 100644 libglnx/glnx-backports.c create mode 100644 libglnx/glnx-backports.h create mode 100644 libglnx/glnx-console.c create mode 100644 libglnx/glnx-console.h create mode 100644 libglnx/glnx-dirfd.c create mode 100644 libglnx/glnx-dirfd.h create mode 100644 libglnx/glnx-errors.c create mode 100644 libglnx/glnx-errors.h create mode 100644 libglnx/glnx-fdio.c create mode 100644 libglnx/glnx-fdio.h create mode 100644 libglnx/glnx-libcontainer.c create mode 100644 libglnx/glnx-libcontainer.h create mode 100644 libglnx/glnx-local-alloc.c create mode 100644 libglnx/glnx-local-alloc.h create mode 100644 libglnx/glnx-lockfile.c create mode 100644 libglnx/glnx-lockfile.h create mode 100644 libglnx/glnx-shutil.c create mode 100644 libglnx/glnx-shutil.h create mode 100644 libglnx/glnx-xattrs.c create mode 100644 libglnx/glnx-xattrs.h create mode 100644 libglnx/libglnx.doap create mode 100644 libglnx/libglnx.h diff --git a/bsdiff b/bsdiff deleted file mode 160000 index 1edf9f65..00000000 --- a/bsdiff +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1edf9f656850c0c64dae260960fabd8249ea9c60 diff --git a/bsdiff/.gitignore b/bsdiff/.gitignore new file mode 100644 index 00000000..4e134929 --- /dev/null +++ b/bsdiff/.gitignore @@ -0,0 +1,11 @@ +.deps/* +.libs/* +*.lo +*.o +.dirstamp +Makefile-bsdiff.am.inc +AUTHORS +NEWS +README +ChangeLog +COPYING diff --git a/bsdiff/LICENSE b/bsdiff/LICENSE new file mode 100644 index 00000000..5d406657 --- /dev/null +++ b/bsdiff/LICENSE @@ -0,0 +1,24 @@ + Copyright 2003-2005 Colin Percival + Copyright 2012 Matthew Endsley + All rights reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted providing that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. diff --git a/bsdiff/Makefile-bsdiff.am b/bsdiff/Makefile-bsdiff.am new file mode 100644 index 00000000..1d810ed9 --- /dev/null +++ b/bsdiff/Makefile-bsdiff.am @@ -0,0 +1,32 @@ +# Copyright (C) 2015 Giuseppe Scrivano +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted providing that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +EXTRA_DIST += $(libbsdiff_srcpath)/bsdiff.h $(libbsdiff_srcpath)/bspatch.h $(libbsdiff_srcpath)/LICENSE $(libbsdiff_srcpath)/README.md + +libbsdiff_la_SOURCES = \ + $(libbsdiff_srcpath)/bsdiff.c \ + $(libbsdiff_srcpath)/bspatch.c \ + $(NULL) + +libbsdiff_la_CFLAGS = $(AM_CFLAGS) diff --git a/bsdiff/Makefile.am b/bsdiff/Makefile.am new file mode 100644 index 00000000..abd3d7e3 --- /dev/null +++ b/bsdiff/Makefile.am @@ -0,0 +1,11 @@ +bin_PROGRAMS = bsdiff bspatch + +bsdiff_SOURCES = bsdiff.c + +bspatch_SOURCES = bspatch.c + +bsdiff_CFLAGS = -DBSDIFF_EXECUTABLE +bspatch_CFLAGS = -DBSPATCH_EXECUTABLE + +EXTRA_DIST = bsdiff.h bspatch.h + diff --git a/bsdiff/README.md b/bsdiff/README.md new file mode 100644 index 00000000..a4b1d232 --- /dev/null +++ b/bsdiff/README.md @@ -0,0 +1,129 @@ +bsdiff/bspatch +============== +bsdiff and bspatch are libraries for building and applying patches to binary +files. + +The original algorithm and implementation was developed by Colin Percival. The +algorithm is detailed in his (unpublished) paper, [Naïve Differences of Executable Code](http://www.daemonology.net/papers/bsdiff.pdf). For more information, visit his +website at . + +I maintain this project seperately from Colin's work, with the goal of making +the core functionality easily embedable in existing projects. + +Contact +------- +[@MatthewEndsley](https://twitter.com/#!/MatthewEndsley) + + +License +------- +Copyright 2003-2005 Colin Percival +Copyright 2012 Matthew Endsley + +This project is governed by the BSD 2-clause license. For details see the file +titled LICENSE in the project root folder. + +Overview +-------- +There are two separate libraries in the project, bsdiff and bspatch. Each are +self contained in bsdiff.c and bspatch.c The easiest way to integrate is to +simply copy the c file to your source folder and build it. + +The overarching goal was to modify the original bsdiff/bspatch code from Colin +and eliminate external dependencies and provide a simple interface to the core +functionality. + +I've exposed relevant functions via the `_stream` classes. The only external +dependency not exposed is `memcmp` in `bsdiff`. + +This library generates patches that are not compatible with the original bsdiff +tool. The impompatibilities were motivated by the patching needs for the game +AirMech and the following requirements: + +* Eliminate/minimize any seek operations when applying patches +* Eliminate any required disk I/O and support embedded streams +* Ability to easily embed the routines as a library instead of an external binary +* Compile+run on all platforms we use to build the game (Windows, Linux, NaCl, OSX) + +Compiling +--------- +The libraries should compile warning free in any moderately recent version of +gcc. The project uses `` which is technically a C99 file and not +available in Microsoft Visual Studio. The easiest solution here is to use the +msinttypes version of stdint.h from . +The direct link for the lazy people is: +. + +If your compiler does not provide an implementation of `` you can +remove the header from the bsdiff/bspatch files and provide your own typedefs +for the following symbols: `uint8_t`, `uint64_t` and `int64_t`. + +Examples +-------- +Each project has an optional main function that serves as an example for using +the library. Simply defined `BSDIFF_EXECUTABLE` or `BSPATCH_EXECUTABLE` to +enable building the standalone tools. + +Reference +--------- +### bsdiff + + struct bsdiff_stream + { + void* opaque; + void* (*malloc)(size_t size); + void (*free)(void* ptr); + int (*write)(struct bsdiff_stream* stream, + const void* buffer, int size); + }; + + int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new, + int64_t newsize, struct bsdiff_stream* stream); + + +In order to use `bsdiff`, you need to define functions for allocating memory and +writing binary data. This behavior is controlled by the `stream` parameted +passed to to `bsdiff(...)`. + +The `opaque` field is never read or modified from within the `bsdiff` function. +The caller can use this field to store custom state data needed for the callback +functions. + +The `malloc` and `free` members should point to functions that behave like the +standard `malloc` and `free` C functions. + +The `write` function is called by bsdiff to write a block of binary data to the +stream. The return value for `write` should be `0` on success and non-zero if +the callback failed to write all data. In the default example, bzip2 is used to +compress output data. + +`bsdiff` returns `0` on success and `-1` on failure. + +### bspatch + + struct bspatch_stream + { + void* opaque; + int (*read)(const struct bspatch_stream* stream, + void* buffer, int length); + }; + + int bspatch(const uint8_t* old, int64_t oldsize, uint8_t* new, + int64_t newsize, struct bspatch_stream* stream); + +The `bspatch` function transforms the data for a file using data generated from +`bsdiff`. The caller takes care of loading the old file and allocating space for +new file data. The `stream` parameter controls the process for reading binary +patch data. + +The `opaque` field is never read or modified from within the bspatch function. +The caller can use this field to store custom state data needed for the read +function. + +The `read` function is called by `bspatch` to read a block of binary data from +the stream. The return value for `read` should be `0` on success and non-zero +if the callback failed to read the requested amount of data. In the default +example, bzip2 is used to decompress input data. + +`bspatch` returns `0` on success and `-1` on failure. On success, `new` contains +the data for the patched file. diff --git a/bsdiff/autogen.sh b/bsdiff/autogen.sh new file mode 100755 index 00000000..ce934acd --- /dev/null +++ b/bsdiff/autogen.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +touch AUTHORS NEWS README ChangeLog +cp LICENSE COPYING + +autoreconf -fis diff --git a/bsdiff/bsdiff.c b/bsdiff/bsdiff.c new file mode 100644 index 00000000..628f1c1a --- /dev/null +++ b/bsdiff/bsdiff.c @@ -0,0 +1,445 @@ +/*- + * Copyright 2003-2005 Colin Percival + * Copyright 2012 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "bsdiff.h" + +#include +#include + +#define MIN(x,y) (((x)<(y)) ? (x) : (y)) + +static void split(int64_t *I,int64_t *V,int64_t start,int64_t len,int64_t h) +{ + int64_t i,j,k,x,tmp,jj,kk; + + if(len<16) { + for(k=start;kstart) split(I,V,start,jj-start,h); + + for(i=0;ikk) split(I,V,kk,start+len-kk,h); +} + +static void qsufsort(int64_t *I,int64_t *V,const uint8_t *old,int64_t oldsize) +{ + int64_t buckets[256]; + int64_t i,h,len; + + for(i=0;i<256;i++) buckets[i]=0; + for(i=0;i0;i--) buckets[i]=buckets[i-1]; + buckets[0]=0; + + for(i=0;iy) { + *pos=I[st]; + return x; + } else { + *pos=I[en]; + return y; + } + }; + + x=st+(en-st)/2; + if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) { + return search(I,old,oldsize,new,newsize,x,en,pos); + } else { + return search(I,old,oldsize,new,newsize,st,x,pos); + }; +} + +static void offtout(int64_t x,uint8_t *buf) +{ + int64_t y; + + if(x<0) y=-x; else y=x; + + buf[0]=y%256;y-=buf[0]; + y=y/256;buf[1]=y%256;y-=buf[1]; + y=y/256;buf[2]=y%256;y-=buf[2]; + y=y/256;buf[3]=y%256;y-=buf[3]; + y=y/256;buf[4]=y%256;y-=buf[4]; + y=y/256;buf[5]=y%256;y-=buf[5]; + y=y/256;buf[6]=y%256;y-=buf[6]; + y=y/256;buf[7]=y%256; + + if(x<0) buf[7]|=0x80; +} + +static int64_t writedata(struct bsdiff_stream* stream, const void* buffer, int64_t length) +{ + int64_t result = 0; + + while (length > 0) + { + const int smallsize = (int)MIN(length, INT_MAX); + const int writeresult = stream->write(stream, buffer, smallsize); + if (writeresult == -1) + { + return -1; + } + + result += writeresult; + length -= smallsize; + buffer = (uint8_t*)buffer + smallsize; + } + + return result; +} + +struct bsdiff_request +{ + const uint8_t* old; + int64_t oldsize; + const uint8_t* new; + int64_t newsize; + struct bsdiff_stream* stream; + int64_t *I; + uint8_t *buffer; +}; + +static int bsdiff_internal(const struct bsdiff_request req) +{ + int64_t *I,*V; + int64_t scan,pos,len; + int64_t lastscan,lastpos,lastoffset; + int64_t oldscore,scsc; + int64_t s,Sf,lenf,Sb,lenb; + int64_t overlap,Ss,lens; + int64_t i; + uint8_t *buffer; + uint8_t buf[8 * 3]; + + if((V=req.stream->malloc((req.oldsize+1)*sizeof(int64_t)))==NULL) return -1; + I = req.I; + + qsufsort(I,V,req.old,req.oldsize); + req.stream->free(V); + + buffer = req.buffer; + + /* Compute the differences, writing ctrl as we go */ + scan=0;len=0;pos=0; + lastscan=0;lastpos=0;lastoffset=0; + while(scanoldscore+8)) break; + + if((scan+lastoffsetSf*2-lenf) { Sf=s; lenf=i; }; + }; + + lenb=0; + if(scan=lastscan+i)&&(pos>=i);i++) { + if(req.old[pos-i]==req.new[scan-i]) s++; + if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; }; + }; + }; + + if(lastscan+lenf>scan-lenb) { + overlap=(lastscan+lenf)-(scan-lenb); + s=0;Ss=0;lens=0; + for(i=0;iSs) { Ss=s; lens=i+1; }; + }; + + lenf+=lens-overlap; + lenb-=lens; + }; + + offtout(lenf,buf); + offtout((scan-lenb)-(lastscan+lenf),buf+8); + offtout((pos-lenb)-(lastpos+lenf),buf+16); + + /* Write control data */ + if (writedata(req.stream, buf, sizeof(buf))) + return -1; + + /* Write diff data */ + for(i=0;imalloc((oldsize+1)*sizeof(int64_t)))==NULL) + return -1; + + if((req.buffer=stream->malloc(newsize+1))==NULL) + { + stream->free(req.I); + return -1; + } + + req.old = old; + req.oldsize = oldsize; + req.new = new; + req.newsize = newsize; + req.stream = stream; + + result = bsdiff_internal(req); + + stream->free(req.buffer); + stream->free(req.I); + + return result; +} + +#if defined(BSDIFF_EXECUTABLE) + +#include + +#include +#include +#include +#include +#include +#include + +static int bz2_write(struct bsdiff_stream* stream, const void* buffer, int size) +{ + int bz2err; + BZFILE* bz2; + + bz2 = (BZFILE*)stream->opaque; + BZ2_bzWrite(&bz2err, bz2, (void*)buffer, size); + if (bz2err != BZ_STREAM_END && bz2err != BZ_OK) + return -1; + + return 0; +} + +int main(int argc,char *argv[]) +{ + int fd; + int bz2err; + uint8_t *old,*new; + off_t oldsize,newsize; + uint8_t buf[8]; + FILE * pf; + struct bsdiff_stream stream; + BZFILE* bz2; + + memset(&bz2, 0, sizeof(bz2)); + stream.malloc = malloc; + stream.free = free; + stream.write = bz2_write; + + if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]); + + /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure + that we never try to malloc(0) and get a NULL pointer */ + if(((fd=open(argv[1],O_RDONLY,0))<0) || + ((oldsize=lseek(fd,0,SEEK_END))==-1) || + ((old=malloc(oldsize+1))==NULL) || + (lseek(fd,0,SEEK_SET)!=0) || + (read(fd,old,oldsize)!=oldsize) || + (close(fd)==-1)) err(1,"%s",argv[1]); + + + /* Allocate newsize+1 bytes instead of newsize bytes to ensure + that we never try to malloc(0) and get a NULL pointer */ + if(((fd=open(argv[2],O_RDONLY,0))<0) || + ((newsize=lseek(fd,0,SEEK_END))==-1) || + ((new=malloc(newsize+1))==NULL) || + (lseek(fd,0,SEEK_SET)!=0) || + (read(fd,new,newsize)!=newsize) || + (close(fd)==-1)) err(1,"%s",argv[2]); + + /* Create the patch file */ + if ((pf = fopen(argv[3], "w")) == NULL) + err(1, "%s", argv[3]); + + /* Write header (signature+newsize)*/ + offtout(newsize, buf); + if (fwrite("ENDSLEY/BSDIFF43", 16, 1, pf) != 1 || + fwrite(buf, sizeof(buf), 1, pf) != 1) + err(1, "Failed to write header"); + + + if (NULL == (bz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0))) + errx(1, "BZ2_bzWriteOpen, bz2err=%d", bz2err); + + stream.opaque = bz2; + if (bsdiff(old, oldsize, new, newsize, &stream)) + err(1, "bsdiff"); + + BZ2_bzWriteClose(&bz2err, bz2, 0, NULL, NULL); + if (bz2err != BZ_OK) + err(1, "BZ2_bzWriteClose, bz2err=%d", bz2err); + + if (fclose(pf)) + err(1, "fclose"); + + /* Free the memory we used */ + free(old); + free(new); + + return 0; +} + +#endif diff --git a/bsdiff/bsdiff.h b/bsdiff/bsdiff.h new file mode 100644 index 00000000..b37da5ff --- /dev/null +++ b/bsdiff/bsdiff.h @@ -0,0 +1,45 @@ +/*- + * Copyright 2003-2005 Colin Percival + * Copyright 2012 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BSDIFF_H +# define BSDIFF_H + +# include +# include + +struct bsdiff_stream +{ + void* opaque; + + void* (*malloc)(size_t size); + void (*free)(void* ptr); + int (*write)(struct bsdiff_stream* stream, const void* buffer, int size); +}; + +int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new, int64_t newsize, struct bsdiff_stream* stream); + +#endif diff --git a/bsdiff/bspatch.c b/bsdiff/bspatch.c new file mode 100644 index 00000000..881d7e3c --- /dev/null +++ b/bsdiff/bspatch.c @@ -0,0 +1,187 @@ +/*- + * Copyright 2003-2005 Colin Percival + * Copyright 2012 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "bspatch.h" + +static int64_t offtin(uint8_t *buf) +{ + int64_t y; + + y=buf[7]&0x7F; + y=y*256;y+=buf[6]; + y=y*256;y+=buf[5]; + y=y*256;y+=buf[4]; + y=y*256;y+=buf[3]; + y=y*256;y+=buf[2]; + y=y*256;y+=buf[1]; + y=y*256;y+=buf[0]; + + if(buf[7]&0x80) y=-y; + + return y; +} + +int bspatch(const uint8_t* old, int64_t oldsize, uint8_t* new, int64_t newsize, struct bspatch_stream* stream) +{ + uint8_t buf[8]; + int64_t oldpos,newpos; + int64_t ctrl[3]; + int64_t i; + + oldpos=0;newpos=0; + while(newposread(stream, buf, 8)) + return -1; + ctrl[i]=offtin(buf); + }; + + /* Sanity-check */ + if(newpos+ctrl[0]>newsize) + return -1; + + /* Read diff string */ + if (stream->read(stream, new + newpos, ctrl[0])) + return -1; + + /* Add old data to diff string */ + for(i=0;i=0) && (oldpos+inewsize) + return -1; + + /* Read extra string */ + if (stream->read(stream, new + newpos, ctrl[1])) + return -1; + + /* Adjust pointers */ + newpos+=ctrl[1]; + oldpos+=ctrl[2]; + }; + + return 0; +} + +#if defined(BSPATCH_EXECUTABLE) + +#include +#include +#include +#include +#include +#include +#include +#include + +static int bz2_read(const struct bspatch_stream* stream, void* buffer, int length) +{ + int n; + int bz2err; + BZFILE* bz2; + + bz2 = (BZFILE*)stream->opaque; + n = BZ2_bzRead(&bz2err, bz2, buffer, length); + if (n != length) + return -1; + + return 0; +} + +int main(int argc,char * argv[]) +{ + FILE * f; + int fd; + int bz2err; + uint8_t header[24]; + uint8_t *old, *new; + int64_t oldsize, newsize; + BZFILE* bz2; + struct bspatch_stream stream; + + if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]); + + /* Open patch file */ + if ((f = fopen(argv[3], "r")) == NULL) + err(1, "fopen(%s)", argv[3]); + + /* Read header */ + if (fread(header, 1, 24, f) != 24) { + if (feof(f)) + errx(1, "Corrupt patch\n"); + err(1, "fread(%s)", argv[3]); + } + + /* Check for appropriate magic */ + if (memcmp(header, "ENDSLEY/BSDIFF43", 16) != 0) + errx(1, "Corrupt patch\n"); + + /* Read lengths from header */ + newsize=offtin(header+16); + if(newsize<0) + errx(1,"Corrupt patch\n"); + + /* Close patch file and re-open it via libbzip2 at the right places */ + if(((fd=open(argv[1],O_RDONLY,0))<0) || + ((oldsize=lseek(fd,0,SEEK_END))==-1) || + ((old=malloc(oldsize+1))==NULL) || + (lseek(fd,0,SEEK_SET)!=0) || + (read(fd,old,oldsize)!=oldsize) || + (close(fd)==-1)) err(1,"%s",argv[1]); + if((new=malloc(newsize+1))==NULL) err(1,NULL); + + if (NULL == (bz2 = BZ2_bzReadOpen(&bz2err, f, 0, 0, NULL, 0))) + errx(1, "BZ2_bzReadOpen, bz2err=%d", bz2err); + + stream.read = bz2_read; + stream.opaque = bz2; + if (bspatch(old, oldsize, new, newsize, &stream)) + errx(1, "bspatch"); + + /* Clean up the bzip2 reads */ + BZ2_bzReadClose(&bz2err, bz2); + fclose(f); + + /* Write the new file */ + if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) || + (write(fd,new,newsize)!=newsize) || (close(fd)==-1)) + err(1,"%s",argv[2]); + + free(new); + free(old); + + return 0; +} + +#endif diff --git a/bsdiff/bspatch.h b/bsdiff/bspatch.h new file mode 100644 index 00000000..099c36e4 --- /dev/null +++ b/bsdiff/bspatch.h @@ -0,0 +1,42 @@ +/*- + * Copyright 2003-2005 Colin Percival + * Copyright 2012 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BSPATCH_H +# define BSPATCH_H + +# include + +struct bspatch_stream +{ + void* opaque; + int (*read)(const struct bspatch_stream* stream, void* buffer, int length); +}; + +int bspatch(const uint8_t* old, int64_t oldsize, uint8_t* new, int64_t newsize, struct bspatch_stream* stream); + +#endif + diff --git a/bsdiff/configure.ac b/bsdiff/configure.ac new file mode 100644 index 00000000..69de7432 --- /dev/null +++ b/bsdiff/configure.ac @@ -0,0 +1,30 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.69]) +AC_INIT([bsdiff], [0.1]) +AC_CONFIG_SRCDIR([bsdiff.c]) +AC_CONFIG_HEADERS([config.h]) +AM_INIT_AUTOMAKE([1.9]) + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. +# FIXME: Replace `main' with a function in `-lbz2': +AC_CHECK_LIB([bz2], [BZ2_bzReadOpen]) + +AC_CHECK_HEADERS([fcntl.h limits.h stddef.h stdint.h stdlib.h string.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_INT64_T +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_TYPE_UINT8_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_CHECK_FUNCS([memset]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/libglnx b/libglnx deleted file mode 160000 index 03138641..00000000 --- a/libglnx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 03138641298fd6799f46b16423871f959332bacf diff --git a/libglnx/.gitignore b/libglnx/.gitignore new file mode 100644 index 00000000..55b946e7 --- /dev/null +++ b/libglnx/.gitignore @@ -0,0 +1,16 @@ +# A path ostree writes to work around automake bug with +# subdir-objects +Makefile-libglnx.am.inc + +# Some standard bits +.deps +.libs +.dirstamp +*.typelib +*.la +*.lo +*.o +*.pyc +*.stamp +*~ + diff --git a/libglnx/COPYING b/libglnx/COPYING new file mode 100644 index 00000000..5bc8fb2c --- /dev/null +++ b/libglnx/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/libglnx/Makefile-libglnx.am b/libglnx/Makefile-libglnx.am new file mode 100644 index 00000000..712e931c --- /dev/null +++ b/libglnx/Makefile-libglnx.am @@ -0,0 +1,48 @@ +# Copyright (C) 2015 Colin Walters +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +EXTRA_DIST += $(libglnx_srcpath)/README.md $(libglnx_srcpath)/COPYING + +libglnx_la_SOURCES = \ + $(libglnx_srcpath)/glnx-backport-autocleanups.h \ + $(libglnx_srcpath)/glnx-backport-autoptr.h \ + $(libglnx_srcpath)/glnx-backports.h \ + $(libglnx_srcpath)/glnx-backports.c \ + $(libglnx_srcpath)/glnx-local-alloc.h \ + $(libglnx_srcpath)/glnx-local-alloc.c \ + $(libglnx_srcpath)/glnx-errors.h \ + $(libglnx_srcpath)/glnx-errors.c \ + $(libglnx_srcpath)/glnx-console.h \ + $(libglnx_srcpath)/glnx-console.c \ + $(libglnx_srcpath)/glnx-dirfd.h \ + $(libglnx_srcpath)/glnx-dirfd.c \ + $(libglnx_srcpath)/glnx-fdio.h \ + $(libglnx_srcpath)/glnx-fdio.c \ + $(libglnx_srcpath)/glnx-lockfile.h \ + $(libglnx_srcpath)/glnx-lockfile.c \ + $(libglnx_srcpath)/glnx-libcontainer.h \ + $(libglnx_srcpath)/glnx-libcontainer.c \ + $(libglnx_srcpath)/glnx-xattrs.h \ + $(libglnx_srcpath)/glnx-xattrs.c \ + $(libglnx_srcpath)/glnx-shutil.h \ + $(libglnx_srcpath)/glnx-shutil.c \ + $(libglnx_srcpath)/libglnx.h \ + $(NULL) + +libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) +libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic +libglnx_la_LIBADD = $(libglnx_libs) diff --git a/libglnx/README.md b/libglnx/README.md new file mode 100644 index 00000000..50a5f90d --- /dev/null +++ b/libglnx/README.md @@ -0,0 +1,53 @@ +libglnx is the successor to libgsystem: https://git.gnome.org/browse/libgsystem + +It is for modules which depend on both GLib and Linux, intended to be +used as a git submodule. + +Features: + + - File APIs which use `openat()` like APIs, but also take a `GCancellable` + to support dynamic cancellation + - APIs also have a `GError` parameter + - High level "shutil", somewhat inspired by Python's + - A "console" API for tty output + - Some basic container utility functions + - A backport of the GLib cleanup macros for projects which can't yet take + a dependency on 2.40. + +Why? +---- + +There are multiple projects which have a hard dependency on Linux and +GLib, such as NetworkManager, ostree, xdg-app, etc. It makes sense +for them to be able to share Linux-specific APIs. + +This module also contains some code taken from systemd, which has very +high quality LGPLv2+ shared library code, but most of the internal +shared library is private, and not namespaced. + +One could also compare this project to gnulib; the salient differences +there are that at least some of this module is eventually destined for +inclusion in GLib. + +Porting from libgsystem +----------------------- + +For all of the filesystem access code, libglnx exposes only +fd-relative API, not `GFile*`. It does use `GCancellable` where +applicable. + +For local allocation macros, you should start using the `g_auto` +macros from GLib. A backport is included in libglnx. There are a few +APIs not defined in GLib yet, such as `glnx_fd_close`. + +`gs_transfer_out_value` is replaced by `g_steal_pointer`. + +Contributing +------------ + +Currently there is not a Bugzilla product - one may be created +in the future. You can submit PRs against the Github mirror: + +https://github.com/GNOME/libglnx/pulls + +Or alternatively, email one of the maintainers directly. diff --git a/libglnx/glnx-backport-autocleanups.h b/libglnx/glnx-backport-autocleanups.h new file mode 100644 index 00000000..53f7ffd2 --- /dev/null +++ b/libglnx/glnx-backport-autocleanups.h @@ -0,0 +1,117 @@ +/* + * Copyright © 2015 Canonical Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * Author: Ryan Lortie + */ + +#pragma once + +#include + +#if !GLIB_CHECK_VERSION(2, 43, 4) + +static inline void +g_autoptr_cleanup_generic_gfree (void *p) +{ + void **pp = (void**)p; + if (*pp) + g_free (*pp); +} + +static inline void +g_autoptr_cleanup_gstring_free (GString *string) +{ + if (string) + g_string_free (string, TRUE); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncQueue, g_async_queue_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBookmarkFile, g_bookmark_file_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBytes, g_bytes_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GChecksum, g_checksum_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDateTime, g_date_time_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDir, g_dir_close) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GError, g_error_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHashTable, g_hash_table_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHmac, g_hmac_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GIOChannel, g_io_channel_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GKeyFile, g_key_file_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GList, g_list_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GArray, g_array_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPtrArray, g_ptr_array_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContext, g_main_context_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainLoop, g_main_loop_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSource, g_source_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GString, g_autoptr_cleanup_gstring_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMappedFile, g_mapped_file_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMarkupParseContext, g_markup_parse_context_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, g_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GNode, g_node_destroy) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionContext, g_option_context_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionGroup, g_option_group_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPatternSpec, g_pattern_spec_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GQueue, g_queue_free) +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GQueue, g_queue_clear) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRand, g_rand_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRegex, g_regex_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMatchInfo, g_match_info_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GScanner, g_scanner_destroy) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSequence, g_sequence_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSList, g_slist_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GStringChunk, g_string_chunk_free) +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(GStrv, g_strfreev, NULL) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GThread, g_thread_unref) +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GMutex, g_mutex_clear) +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GCond, g_cond_clear) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimer, g_timer_destroy) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimeZone, g_time_zone_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTree, g_tree_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariant, g_variant_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantBuilder, g_variant_builder_unref) +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantBuilder, g_variant_builder_clear) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantIter, g_variant_iter_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantDict, g_variant_dict_unref) +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocess, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocessLauncher, g_object_unref) + +/* Add GObject-based types as needed. */ +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCancellable, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverter, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterOutputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDataInputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFile, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileEnumerator, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileIOStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInfo, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileMonitor, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileOutputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryInputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryOutputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOutputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocket, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocketAddress, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTask, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsCertificate, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsDatabase, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsInteraction, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusConnection, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusMessage, g_object_unref) + +#endif diff --git a/libglnx/glnx-backport-autoptr.h b/libglnx/glnx-backport-autoptr.h new file mode 100644 index 00000000..b36919dc --- /dev/null +++ b/libglnx/glnx-backport-autoptr.h @@ -0,0 +1,133 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2015 Colin Walters + * + * GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#if !GLIB_CHECK_VERSION(2, 43, 4) + +#define _GLIB_AUTOPTR_FUNC_NAME(TypeName) glib_autoptr_cleanup_##TypeName +#define _GLIB_AUTOPTR_TYPENAME(TypeName) TypeName##_autoptr +#define _GLIB_AUTO_FUNC_NAME(TypeName) glib_auto_cleanup_##TypeName +#define _GLIB_CLEANUP(func) __attribute__((cleanup(func))) +#define _GLIB_DEFINE_AUTOPTR_CHAINUP(ModuleObjName, ParentName) \ + typedef ModuleObjName *_GLIB_AUTOPTR_TYPENAME(ModuleObjName); \ + static inline void _GLIB_AUTOPTR_FUNC_NAME(ModuleObjName) (ModuleObjName **_ptr) { \ + _GLIB_AUTOPTR_FUNC_NAME(ParentName) ((ParentName **) _ptr); } \ + + +/* these macros are API */ +#define G_DEFINE_AUTOPTR_CLEANUP_FUNC(TypeName, func) \ + typedef TypeName *_GLIB_AUTOPTR_TYPENAME(TypeName); \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + static inline void _GLIB_AUTOPTR_FUNC_NAME(TypeName) (TypeName **_ptr) { if (*_ptr) (func) (*_ptr); } \ + G_GNUC_END_IGNORE_DEPRECATIONS +#define G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(TypeName, func) \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + static inline void _GLIB_AUTO_FUNC_NAME(TypeName) (TypeName *_ptr) { (func) (_ptr); } \ + G_GNUC_END_IGNORE_DEPRECATIONS +#define G_DEFINE_AUTO_CLEANUP_FREE_FUNC(TypeName, func, none) \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + static inline void _GLIB_AUTO_FUNC_NAME(TypeName) (TypeName *_ptr) { if (*_ptr != none) (func) (*_ptr); } \ + G_GNUC_END_IGNORE_DEPRECATIONS +#define g_autoptr(TypeName) _GLIB_CLEANUP(_GLIB_AUTOPTR_FUNC_NAME(TypeName)) _GLIB_AUTOPTR_TYPENAME(TypeName) +#define g_auto(TypeName) _GLIB_CLEANUP(_GLIB_AUTO_FUNC_NAME(TypeName)) TypeName +#define g_autofree _GLIB_CLEANUP(g_autoptr_cleanup_generic_gfree) + +/** + * g_steal_pointer: + * @pp: a pointer to a pointer + * + * Sets @pp to %NULL, returning the value that was there before. + * + * Conceptually, this transfers the ownership of the pointer from the + * referenced variable to the "caller" of the macro (ie: "steals" the + * reference). + * + * The return value will be properly typed, according to the type of + * @pp. + * + * This can be very useful when combined with g_autoptr() to prevent the + * return value of a function from being automatically freed. Consider + * the following example (which only works on GCC and clang): + * + * |[ + * GObject * + * create_object (void) + * { + * g_autoptr(GObject) obj = g_object_new (G_TYPE_OBJECT, NULL); + * + * if (early_error_case) + * return NULL; + * + * return g_steal_pointer (&obj); + * } + * ]| + * + * It can also be used in similar ways for 'out' parameters and is + * particularly useful for dealing with optional out parameters: + * + * |[ + * gboolean + * get_object (GObject **obj_out) + * { + * g_autoptr(GObject) obj = g_object_new (G_TYPE_OBJECT, NULL); + * + * if (early_error_case) + * return FALSE; + * + * if (obj_out) + * *obj_out = g_steal_pointer (&obj); + * + * return TRUE; + * } + * ]| + * + * In the above example, the object will be automatically freed in the + * early error case and also in the case that %NULL was given for + * @obj_out. + * + * Since: 2.44 + */ +static inline gpointer +(g_steal_pointer) (gpointer pp) +{ + gpointer *ptr = (gpointer *) pp; + gpointer ref; + + ref = *ptr; + *ptr = NULL; + + return ref; +} + +/* type safety */ +#define g_steal_pointer(pp) \ + (0 ? (*(pp)) : (g_steal_pointer) (pp)) + +#endif /* !GLIB_CHECK_VERSION(2, 43, 3) */ + +G_END_DECLS diff --git a/libglnx/glnx-backports.c b/libglnx/glnx-backports.c new file mode 100644 index 00000000..c7bb600f --- /dev/null +++ b/libglnx/glnx-backports.c @@ -0,0 +1,61 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2015 Colin Walters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "glnx-backports.h" + +#if !GLIB_CHECK_VERSION(2, 44, 0) +gboolean +glnx_strv_contains (const gchar * const *strv, + const gchar *str) +{ + g_return_val_if_fail (strv != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + for (; *strv != NULL; strv++) + { + if (g_str_equal (str, *strv)) + return TRUE; + } + + return FALSE; +} + +gboolean +glnx_set_object (GObject **object_ptr, + GObject *new_object) +{ + GObject *old_object = *object_ptr; + + if (old_object == new_object) + return FALSE; + + if (new_object != NULL) + g_object_ref (new_object); + + *object_ptr = new_object; + + if (old_object != NULL) + g_object_unref (old_object); + + return TRUE; +} +#endif diff --git a/libglnx/glnx-backports.h b/libglnx/glnx-backports.h new file mode 100644 index 00000000..cd853cca --- /dev/null +++ b/libglnx/glnx-backports.h @@ -0,0 +1,46 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2015 Colin Walters + * + * GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#if !GLIB_CHECK_VERSION(2, 44, 0) + +#define g_strv_contains glnx_strv_contains +gboolean glnx_strv_contains (const gchar * const *strv, + const gchar *str); + +#define g_set_object(object_ptr, new_object) \ + (/* Check types match. */ \ + 0 ? *(object_ptr) = (new_object), FALSE : \ + glnx_set_object ((GObject **) (object_ptr), (GObject *) (new_object)) \ + ) +gboolean glnx_set_object (GObject **object_ptr, + GObject *new_object); + +#endif /* !GLIB_CHECK_VERSION(2, 44, 0) */ + +G_END_DECLS diff --git a/libglnx/glnx-console.c b/libglnx/glnx-console.c new file mode 100644 index 00000000..deb4c86c --- /dev/null +++ b/libglnx/glnx-console.c @@ -0,0 +1,289 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2013,2014,2015 Colin Walters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "glnx-console.h" + +#include +#include +#include +#include +#include +#include + +static char *current_text = NULL; +static gint current_percent = -1; +static gboolean locked; + +static gboolean +stdout_is_tty (void) +{ + static gsize initialized = 0; + static gboolean stdout_is_tty_v; + + if (g_once_init_enter (&initialized)) + { + stdout_is_tty_v = isatty (1); + g_once_init_leave (&initialized, 1); + } + + return stdout_is_tty_v; +} + +static volatile guint cached_columns = 0; +static volatile guint cached_lines = 0; + +static int +fd_columns (int fd) +{ + struct winsize ws = {}; + + if (ioctl (fd, TIOCGWINSZ, &ws) < 0) + return -errno; + + if (ws.ws_col <= 0) + return -EIO; + + return ws.ws_col; +} + +/** + * glnx_console_columns: + * + * Returns: The number of columns for terminal output + */ +guint +glnx_console_columns (void) +{ + if (G_UNLIKELY (cached_columns == 0)) + { + int c; + + c = fd_columns (STDOUT_FILENO); + + if (c <= 0) + c = 80; + + if (c > 256) + c = 256; + + cached_columns = c; + } + + return cached_columns; +} + +static int +fd_lines (int fd) +{ + struct winsize ws = {}; + + if (ioctl (fd, TIOCGWINSZ, &ws) < 0) + return -errno; + + if (ws.ws_row <= 0) + return -EIO; + + return ws.ws_row; +} + +/** + * glnx_console_lines: + * + * Returns: The number of lines for terminal output + */ +guint +glnx_console_lines (void) +{ + if (G_UNLIKELY (cached_lines == 0)) + { + int l; + + l = fd_lines (STDOUT_FILENO); + + if (l <= 0) + l = 24; + + cached_lines = l; + } + + return cached_lines; +} + +static void +on_sigwinch (int signum) +{ + cached_columns = 0; + cached_lines = 0; +} + +void +glnx_console_lock (GLnxConsoleRef *console) +{ + static gsize sigwinch_initialized = 0; + + g_return_if_fail (!locked); + g_return_if_fail (!console->locked); + + console->is_tty = stdout_is_tty (); + + locked = console->locked = TRUE; + + current_percent = 0; + + if (console->is_tty) + { + if (g_once_init_enter (&sigwinch_initialized)) + { + signal (SIGWINCH, on_sigwinch); + g_once_init_leave (&sigwinch_initialized, 1); + } + + { static const char initbuf[] = { '\n', 0x1B, 0x37 }; + (void) fwrite (initbuf, 1, sizeof (initbuf), stdout); + } + } +} + +static void +printpad (const char *padbuf, + guint padbuf_len, + guint n) +{ + const guint d = n / padbuf_len; + const guint r = n % padbuf_len; + guint i; + + for (i = 0; i < d; i++) + fwrite (padbuf, 1, padbuf_len, stdout); + fwrite (padbuf, 1, r, stdout); +} + +/** + * glnx_console_progress_text_percent: + * @text: Show this text before the progress bar + * @percentage: An integer in the range of 0 to 100 + * + * On a tty, print to the console @text followed by an ASCII art + * progress bar whose percentage is @percentage. If stdout is not a + * tty, a more basic line by line change will be printed. + * + * You must have called glnx_console_lock() before invoking this + * function. + * + */ +void +glnx_console_progress_text_percent (const char *text, + guint percentage) +{ + static const char equals[] = "===================="; + const guint n_equals = sizeof (equals) - 1; + static const char spaces[] = " "; + const guint n_spaces = sizeof (spaces) - 1; + const guint ncolumns = glnx_console_columns (); + const guint bar_min = 10; + const guint input_textlen = text ? strlen (text) : 0; + guint textlen; + guint barlen; + + g_return_if_fail (percentage >= 0 && percentage <= 100); + + if (text && !*text) + text = NULL; + + if (percentage == current_percent + && g_strcmp0 (text, current_text) == 0) + return; + + if (!stdout_is_tty ()) + { + if (text) + fprintf (stdout, "%s", text); + if (percentage != -1) + { + if (text) + fputc (' ', stdout); + fprintf (stdout, "%u%%", percentage); + } + fputc ('\n', stdout); + fflush (stdout); + return; + } + + if (ncolumns < bar_min) + return; /* TODO: spinner */ + + /* Restore cursor */ + { const char beginbuf[2] = { 0x1B, 0x38 }; + (void) fwrite (beginbuf, 1, sizeof (beginbuf), stdout); + } + + textlen = MIN (input_textlen, ncolumns - bar_min); + barlen = ncolumns - textlen; + + if (textlen > 0) + { + fwrite (text, 1, textlen - 1, stdout); + fputc (' ', stdout); + } + + { + const guint nbraces = 2; + const guint textpercent_len = 5; + const guint bar_internal_len = barlen - nbraces - textpercent_len; + const guint eqlen = bar_internal_len * (percentage / 100.0); + const guint spacelen = bar_internal_len - eqlen; + + fputc ('[', stdout); + printpad (equals, n_equals, eqlen); + printpad (spaces, n_spaces, spacelen); + fputc (']', stdout); + fprintf (stdout, " %3d%%", percentage); + } + + { const guint spacelen = ncolumns - textlen - barlen; + printpad (spaces, n_spaces, spacelen); + } + + fflush (stdout); +} + +/** + * glnx_console_unlock: + * + * Print a newline, and reset all cached console progress state. + * + * This function does nothing if stdout is not a tty. + */ +void +glnx_console_unlock (GLnxConsoleRef *console) +{ + g_return_if_fail (locked); + g_return_if_fail (console->locked); + + current_percent = -1; + g_clear_pointer (¤t_text, g_free); + + if (console->is_tty) + fputc ('\n', stdout); + + locked = FALSE; +} diff --git a/libglnx/glnx-console.h b/libglnx/glnx-console.h new file mode 100644 index 00000000..9f620cc7 --- /dev/null +++ b/libglnx/glnx-console.h @@ -0,0 +1,52 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2013,2014,2015 Colin Walters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +struct GLnxConsoleRef { + gboolean locked; + gboolean is_tty; +}; + +typedef struct GLnxConsoleRef GLnxConsoleRef; + +void glnx_console_lock (GLnxConsoleRef *ref); + +void glnx_console_progress_text_percent (const char *text, + guint percentage); + +void glnx_console_unlock (GLnxConsoleRef *ref); + +guint glnx_console_lines (void); + +guint glnx_console_columns (void); + +static inline void +glnx_console_ref_cleanup (GLnxConsoleRef *p) +{ + glnx_console_unlock (p); +} +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxConsoleRef, glnx_console_ref_cleanup) + +G_END_DECLS diff --git a/libglnx/glnx-dirfd.c b/libglnx/glnx-dirfd.c new file mode 100644 index 00000000..d126f15a --- /dev/null +++ b/libglnx/glnx-dirfd.c @@ -0,0 +1,305 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include +#include + +/** + * glnx_opendirat_with_errno: + * @dfd: File descriptor for origin directory + * @name: Pathname, relative to @dfd + * @follow: Whether or not to follow symbolic links + * + * Use openat() to open a directory, using a standard set of flags. + * This function sets errno. + */ +int +glnx_opendirat_with_errno (int dfd, + const char *path, + gboolean follow) +{ + int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY; + if (!follow) + flags |= O_NOFOLLOW; + + dfd = glnx_dirfd_canonicalize (dfd); + + return openat (dfd, path, flags); +} + +/** + * glnx_opendirat: + * @dfd: File descriptor for origin directory + * @path: Pathname, relative to @dfd + * @follow: Whether or not to follow symbolic links + * @error: Error + * + * Use openat() to open a directory, using a standard set of flags. + */ +gboolean +glnx_opendirat (int dfd, + const char *path, + gboolean follow, + int *out_fd, + GError **error) +{ + int ret = glnx_opendirat_with_errno (dfd, path, follow); + if (ret == -1) + { + glnx_set_prefix_error_from_errno (error, "%s", "openat"); + return FALSE; + } + *out_fd = ret; + return TRUE; +} + +struct GLnxRealDirfdIterator +{ + gboolean initialized; + int fd; + DIR *d; +}; +typedef struct GLnxRealDirfdIterator GLnxRealDirfdIterator; + +/** + * glnx_dirfd_iterator_init_at: + * @dfd: File descriptor, may be AT_FDCWD or -1 + * @path: Path, may be relative to @df + * @follow: If %TRUE and the last component of @path is a symlink, follow it + * @out_dfd_iter: (out caller-allocates): A directory iterator, will be initialized + * @error: Error + * + * Initialize @out_dfd_iter from @dfd and @path. + */ +gboolean +glnx_dirfd_iterator_init_at (int dfd, + const char *path, + gboolean follow, + GLnxDirFdIterator *out_dfd_iter, + GError **error) +{ + gboolean ret = FALSE; + glnx_fd_close int fd = -1; + + if (!glnx_opendirat (dfd, path, follow, &fd, error)) + goto out; + + if (!glnx_dirfd_iterator_init_take_fd (fd, out_dfd_iter, error)) + goto out; + fd = -1; /* Transfer ownership */ + + ret = TRUE; + out: + return ret; +} + +/** + * glnx_dirfd_iterator_init_take_fd: + * @dfd: File descriptor - ownership is taken + * @dfd_iter: A directory iterator + * @error: Error + * + * Steal ownership of @dfd, using it to initialize @dfd_iter for + * iteration. + */ +gboolean +glnx_dirfd_iterator_init_take_fd (int dfd, + GLnxDirFdIterator *dfd_iter, + GError **error) +{ + gboolean ret = FALSE; + GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; + DIR *d = NULL; + + d = fdopendir (dfd); + if (!d) + { + glnx_set_prefix_error_from_errno (error, "%s", "fdopendir"); + goto out; + } + + real_dfd_iter->fd = dfd; + real_dfd_iter->d = d; + + ret = TRUE; + out: + return ret; +} + +/** + * glnx_dirfd_iterator_next_dent: + * @dfd_iter: A directory iterator + * @out_dent: (out) (transfer none): Pointer to dirent; do not free + * @cancellable: Cancellable + * @error: Error + * + * Read the next value from @dfd_iter, causing @out_dent to be + * updated. If end of stream is reached, @out_dent will be set + * to %NULL, and %TRUE will be returned. + */ +gboolean +glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, + struct dirent **out_dent, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + goto out; + + do + { + errno = 0; + *out_dent = readdir (real_dfd_iter->d); + if (*out_dent == NULL && errno != 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "fdopendir"); + goto out; + } + } while (*out_dent && + (strcmp ((*out_dent)->d_name, ".") == 0 || + strcmp ((*out_dent)->d_name, "..") == 0)); + + ret = TRUE; + out: + return ret; +} + +/** + * glnx_dirfd_iterator_clear: + * @dfd_iter: Iterator, will be de-initialized + * + * Unset @dfd_iter, freeing any resources. If @dfd_iter is not + * initialized, do nothing. + */ +void +glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter) +{ + GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; + /* fd is owned by dfd_iter */ + (void) closedir (real_dfd_iter->d); + real_dfd_iter->initialized = FALSE; +} + +/** + * glnx_fdrel_abspath: + * @dfd: Directory fd + * @path: Path + * + * Turn a fd-relative pair into something that can be used for legacy + * APIs expecting absolute paths. + * + * This is Linux specific, and only valid inside this process (unless + * you set up the child process to have the exact same fd number, but + * don't try that). + */ +char * +glnx_fdrel_abspath (int dfd, + const char *path) +{ + dfd = glnx_dirfd_canonicalize (dfd); + if (dfd == AT_FDCWD) + return g_strdup (path); + return g_strdup_printf ("/proc/self/fd/%d/%s", dfd, path); +} + +/** + * glnx_mkdtempat: + * @dfd: Directory fd + * @tmpl: (type filename): template directory name + * @mode: permissions to create the temporary directory with + * @error: Error + * + * Similar to g_mkdtemp_full, but using openat. + */ +gboolean +glnx_mkdtempat (int dfd, + gchar *tmpl, + int mode, + GError **error) +{ + char *XXXXXX; + int count; + static const char letters[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + static const int NLETTERS = sizeof (letters) - 1; + glong value; + GTimeVal tv; + static int counter = 0; + + g_return_val_if_fail (tmpl != NULL, -1); + + /* find the last occurrence of "XXXXXX" */ + XXXXXX = g_strrstr (tmpl, "XXXXXX"); + + if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + "Invalid temporary directory template '%s'", tmpl); + return FALSE; + } + + /* Get some more or less random data. */ + g_get_current_time (&tv); + value = (tv.tv_usec ^ tv.tv_sec) + counter++; + + for (count = 0; count < 100; value += 7777, ++count) + { + glong v = value; + + /* Fill in the random bits. */ + XXXXXX[0] = letters[v % NLETTERS]; + v /= NLETTERS; + XXXXXX[1] = letters[v % NLETTERS]; + v /= NLETTERS; + XXXXXX[2] = letters[v % NLETTERS]; + v /= NLETTERS; + XXXXXX[3] = letters[v % NLETTERS]; + v /= NLETTERS; + XXXXXX[4] = letters[v % NLETTERS]; + v /= NLETTERS; + XXXXXX[5] = letters[v % NLETTERS]; + + if (mkdirat (dfd, tmpl, mode) == -1) + { + if (errno == EEXIST) + continue; + + /* Any other error will apply also to other names we might + * try, and there are 2^32 or so of them, so give up now. + */ + glnx_set_prefix_error_from_errno (error, "%s", "mkdirat"); + return FALSE; + } + + return TRUE; + } + + g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, + "mkstempat ran out of combinations to try."); + return FALSE; +} diff --git a/libglnx/glnx-dirfd.h b/libglnx/glnx-dirfd.h new file mode 100644 index 00000000..5b7b77a9 --- /dev/null +++ b/libglnx/glnx-dirfd.h @@ -0,0 +1,85 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +/** + * glnx_dirfd_canonicalize: + * @fd: A directory file descriptor + * + * It's often convenient in programs to use `-1` for "unassigned fd", + * and also because gobject-introspection doesn't support `AT_FDCWD`, + * libglnx honors `-1` to mean `AT_FDCWD`. This small inline function + * canonicalizes `-1 -> AT_FDCWD`. + */ +static inline int +glnx_dirfd_canonicalize (int fd) +{ + if (fd == -1) + return AT_FDCWD; + return fd; +} + +struct GLnxDirFdIterator { + gboolean initialized; + int fd; + gpointer padding_data[4]; +}; + +typedef struct GLnxDirFdIterator GLnxDirFdIterator; +gboolean glnx_dirfd_iterator_init_at (int dfd, const char *path, + gboolean follow, + GLnxDirFdIterator *dfd_iter, GError **error); +gboolean glnx_dirfd_iterator_init_take_fd (int dfd, GLnxDirFdIterator *dfd_iter, GError **error); +gboolean glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, + struct dirent **out_dent, + GCancellable *cancellable, + GError **error); +void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter); + +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxDirFdIterator, glnx_dirfd_iterator_clear) + +int glnx_opendirat_with_errno (int dfd, + const char *path, + gboolean follow); + +gboolean glnx_opendirat (int dfd, + const char *path, + gboolean follow, + int *out_fd, + GError **error); + +char *glnx_fdrel_abspath (int dfd, + const char *path); + +gboolean glnx_mkdtempat (int dfd, + gchar *tmpl, + int mode, + GError **error); + +G_END_DECLS diff --git a/libglnx/glnx-errors.c b/libglnx/glnx-errors.c new file mode 100644 index 00000000..ab7bc0aa --- /dev/null +++ b/libglnx/glnx-errors.c @@ -0,0 +1,53 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +void +glnx_real_set_prefix_error_from_errno (GError **error, + gint errsv, + const char *format, + ...) +{ + if (!error) + return; + else + { + GString *buf = g_string_new (""); + va_list args; + + va_start (args, format); + g_string_append_vprintf (buf, format, args); + va_end (args); + + g_string_append (buf, ": "); + g_string_append (buf, g_strerror (errsv)); + + g_set_error_literal (error, + G_IO_ERROR, + g_io_error_from_errno (errsv), + buf->str); + g_string_free (buf, TRUE); + errno = errsv; + } +} diff --git a/libglnx/glnx-errors.h b/libglnx/glnx-errors.h new file mode 100644 index 00000000..fffef6c9 --- /dev/null +++ b/libglnx/glnx-errors.h @@ -0,0 +1,49 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +#define glnx_set_error_from_errno(error) \ + do { \ + int errsv = errno; \ + g_set_error_literal (error, G_IO_ERROR, \ + g_io_error_from_errno (errsv), \ + g_strerror (errsv)); \ + errno = errsv; \ + } while (0); + +#define glnx_set_prefix_error_from_errno(error, format, args...) \ + do { \ + int errsv = errno; \ + glnx_real_set_prefix_error_from_errno (error, errsv, format, args); \ + errno = errsv; \ + } while (0); + +void glnx_real_set_prefix_error_from_errno (GError **error, + gint errsv, + const char *format, + ...) G_GNUC_PRINTF (3,4); + +G_END_DECLS diff --git a/libglnx/glnx-fdio.c b/libglnx/glnx-fdio.c new file mode 100644 index 00000000..7db33c4c --- /dev/null +++ b/libglnx/glnx-fdio.c @@ -0,0 +1,750 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * Portions derived from systemd: + * Copyright 2010 Lennart Poettering + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +/* See linux.git/fs/btrfs/ioctl.h */ +#define BTRFS_IOCTL_MAGIC 0x94 +#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int) + +#include +#include +#include +#include +#include +#include + +static guint8* +glnx_fd_readall_malloc (int fd, + gsize *out_len, + gboolean nul_terminate, + GCancellable *cancellable, + GError **error) +{ + gboolean success = FALSE; + const guint maxreadlen = 4096; + int res; + struct stat stbuf; + guint8* buf = NULL; + gsize buf_allocated; + gsize buf_size = 0; + gssize bytes_read; + + do + res = fstat (fd, &stbuf); + while (G_UNLIKELY (res == -1 && errno == EINTR)); + if (res == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (S_ISREG (stbuf.st_mode) && stbuf.st_size > 0) + buf_allocated = stbuf.st_size; + else + buf_allocated = 16; + + buf = g_malloc (buf_allocated); + + while (TRUE) + { + gsize readlen = MIN (buf_allocated - buf_size, maxreadlen); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + goto out; + + do + bytes_read = read (fd, buf + buf_size, readlen); + while (G_UNLIKELY (bytes_read == -1 && errno == EINTR)); + if (G_UNLIKELY (bytes_read == -1)) + { + glnx_set_error_from_errno (error); + goto out; + } + if (bytes_read == 0) + break; + + buf_size += bytes_read; + if (buf_allocated - buf_size < maxreadlen) + buf = g_realloc (buf, buf_allocated *= 2); + } + + if (nul_terminate) + { + if (buf_allocated - buf_size == 0) + buf = g_realloc (buf, buf_allocated + 1); + buf[buf_size] = '\0'; + } + + success = TRUE; + out: + if (success) + { + *out_len = buf_size; + return buf; + } + g_free (buf); + return NULL; +} + +/** + * glnx_fd_readall_bytes: + * @fd: A file descriptor + * @cancellable: Cancellable: + * @error: Error + * + * Read all data from file descriptor @fd into a #GBytes. It's + * recommended to only use this for small files. + * + * Returns: (transfer full): A newly allocated #GBytes + */ +GBytes * +glnx_fd_readall_bytes (int fd, + GCancellable *cancellable, + GError **error) +{ + guint8 *buf; + gsize len; + + buf = glnx_fd_readall_malloc (fd, &len, FALSE, cancellable, error); + if (!buf) + return NULL; + + return g_bytes_new_take (buf, len); +} + +/** + * glnx_fd_readall_utf8: + * @fd: A file descriptor + * @out_len: (out): Returned length + * @cancellable: Cancellable: + * @error: Error + * + * Read all data from file descriptor @fd, validating + * the result as UTF-8. + * + * Returns: (transfer full): A string validated as UTF-8, or %NULL on error. + */ +char * +glnx_fd_readall_utf8 (int fd, + gsize *out_len, + GCancellable *cancellable, + GError **error) +{ + gboolean success = FALSE; + guint8 *buf; + gsize len; + + buf = glnx_fd_readall_malloc (fd, &len, TRUE, cancellable, error); + if (!buf) + goto out; + + if (!g_utf8_validate ((char*)buf, len, NULL)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "Invalid UTF-8"); + goto out; + } + + success = TRUE; + out: + if (success) + { + if (out_len) + *out_len = len; + return (char*)buf; + } + g_free (buf); + return NULL; +} + +/** + * glnx_file_get_contents_utf8_at: + * @dfd: Directory file descriptor + * @subpath: Path relative to @dfd + * @out_len: (out) (allow-none): Optional length + * @cancellable: Cancellable + * @error: Error + * + * Read the entire contents of the file referred + * to by @dfd and @subpath, validate the result as UTF-8. + * The length is optionally stored in @out_len. + * + * Returns: (transfer full): UTF-8 validated text, or %NULL on error + */ +char * +glnx_file_get_contents_utf8_at (int dfd, + const char *subpath, + gsize *out_len, + GCancellable *cancellable, + GError **error) +{ + gboolean success = FALSE; + glnx_fd_close int fd = -1; + char *buf = NULL; + gsize len; + + dfd = glnx_dirfd_canonicalize (dfd); + + do + fd = openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC); + while (G_UNLIKELY (fd == -1 && errno == EINTR)); + if (G_UNLIKELY (fd == -1)) + { + glnx_set_error_from_errno (error); + goto out; + } + + buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error); + if (G_UNLIKELY(!buf)) + goto out; + + success = TRUE; + out: + if (success) + { + if (out_len) + *out_len = len; + return buf; + } + g_free (buf); + return NULL; +} + +/** + * glnx_readlinkat_malloc: + * @dfd: Directory file descriptor + * @subpath: Subpath + * @cancellable: Cancellable + * @error: Error + * + * Read the value of a symlink into a dynamically + * allocated buffer. + */ +char * +glnx_readlinkat_malloc (int dfd, + const char *subpath, + GCancellable *cancellable, + GError **error) +{ + size_t l = 100; + + dfd = glnx_dirfd_canonicalize (dfd); + + for (;;) + { + char *c; + ssize_t n; + + c = g_malloc (l); + n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1)); + if (n < 0) + { + glnx_set_error_from_errno (error); + g_free (c); + return FALSE; + } + + if ((size_t) n < l-1) + { + c[n] = 0; + return c; + } + + g_free (c); + l *= 2; + } + + g_assert_not_reached (); +} + +static gboolean +copy_symlink_at (int src_dfd, + const char *src_subpath, + const struct stat *src_stbuf, + int dest_dfd, + const char *dest_subpath, + GLnxFileCopyFlags copyflags, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + g_autofree char *buf = NULL; + + buf = glnx_readlinkat_malloc (src_dfd, src_subpath, cancellable, error); + if (!buf) + goto out; + + if (TEMP_FAILURE_RETRY (symlinkat (buf, dest_dfd, dest_subpath)) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (!(copyflags & GLNX_FILE_COPY_NOXATTRS)) + { + g_autoptr(GVariant) xattrs = NULL; + + if (!glnx_dfd_name_get_all_xattrs (src_dfd, src_subpath, &xattrs, + cancellable, error)) + goto out; + + if (!glnx_dfd_name_set_all_xattrs (dest_dfd, dest_subpath, xattrs, + cancellable, error)) + goto out; + } + + if (TEMP_FAILURE_RETRY (fchownat (dest_dfd, dest_subpath, + src_stbuf->st_uid, src_stbuf->st_gid, + AT_SYMLINK_NOFOLLOW)) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + ret = TRUE; + out: + return ret; +} + +#define COPY_BUFFER_SIZE (16*1024) + +/* From systemd */ + +static int btrfs_reflink(int infd, int outfd) { + int r; + + g_return_val_if_fail(infd >= 0, -1); + g_return_val_if_fail(outfd >= 0, -1); + + r = ioctl(outfd, BTRFS_IOC_CLONE, infd); + if (r < 0) + return -errno; + + return 0; +} + +static int loop_write(int fd, const void *buf, size_t nbytes) { + const uint8_t *p = buf; + + g_return_val_if_fail(fd >= 0, -1); + g_return_val_if_fail(buf, -1); + + errno = 0; + + while (nbytes > 0) { + ssize_t k; + + k = write(fd, p, nbytes); + if (k < 0) { + if (errno == EINTR) + continue; + + return -errno; + } + + if (k == 0) /* Can't really happen */ + return -EIO; + + p += k; + nbytes -= k; + } + + return 0; +} + +static int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { + bool try_sendfile = true; + int r; + + g_return_val_if_fail (fdf >= 0, -1); + g_return_val_if_fail (fdt >= 0, -1); + + /* Try btrfs reflinks first. */ + if (try_reflink && max_bytes == (off_t) -1) { + r = btrfs_reflink(fdf, fdt); + if (r >= 0) + return r; + } + + for (;;) { + size_t m = COPY_BUFFER_SIZE; + ssize_t n; + + if (max_bytes != (off_t) -1) { + + if (max_bytes <= 0) + return -EFBIG; + + if ((off_t) m > max_bytes) + m = (size_t) max_bytes; + } + + /* First try sendfile(), unless we already tried */ + if (try_sendfile) { + + n = sendfile(fdt, fdf, NULL, m); + if (n < 0) { + if (errno != EINVAL && errno != ENOSYS) + return -errno; + + try_sendfile = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else if (n > 0) + /* Succcess! */ + goto next; + } + + /* As a fallback just copy bits by hand */ + { + char buf[m]; + + n = read(fdf, buf, m); + if (n < 0) + return -errno; + if (n == 0) /* EOF */ + break; + + r = loop_write(fdt, buf, (size_t) n); + if (r < 0) + return r; + } + + next: + if (max_bytes != (off_t) -1) { + g_assert(max_bytes >= n); + max_bytes -= n; + } + } + + return 0; +} + +/** + * glnx_file_copy_at: + * @src_dfd: Source directory fd + * @src_subpath: Subpath relative to @src_dfd + * @dest_dfd: Target directory fd + * @dest_subpath: Destination name + * @copyflags: Flags + * @cancellable: cancellable + * @error: Error + * + * Perform a full copy of the regular file or + * symbolic link from @src_subpath to @dest_subpath. + * + * If @src_subpath is anything other than a regular + * file or symbolic link, an error will be returned. + */ +gboolean +glnx_file_copy_at (int src_dfd, + const char *src_subpath, + struct stat *src_stbuf, + int dest_dfd, + const char *dest_subpath, + GLnxFileCopyFlags copyflags, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + int r; + int dest_open_flags; + struct timespec ts[2]; + glnx_fd_close int src_fd = -1; + glnx_fd_close int dest_fd = -1; + struct stat local_stbuf; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + goto out; + + src_dfd = glnx_dirfd_canonicalize (src_dfd); + dest_dfd = glnx_dirfd_canonicalize (dest_dfd); + + /* Automatically do stat() if no stat buffer was supplied */ + if (!src_stbuf) + { + if (fstatat (src_dfd, src_subpath, &local_stbuf, AT_SYMLINK_NOFOLLOW) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + src_stbuf = &local_stbuf; + } + + if (S_ISLNK (src_stbuf->st_mode)) + { + return copy_symlink_at (src_dfd, src_subpath, src_stbuf, + dest_dfd, dest_subpath, + copyflags, + cancellable, error); + } + else if (!S_ISREG (src_stbuf->st_mode)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Cannot copy non-regular/non-symlink file: %s", src_subpath); + goto out; + } + + src_fd = TEMP_FAILURE_RETRY (openat (src_dfd, src_subpath, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW)); + if (src_fd == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + + dest_open_flags = O_WRONLY | O_CREAT | O_CLOEXEC | O_NOCTTY; + if (!(copyflags & GLNX_FILE_COPY_OVERWRITE)) + dest_open_flags |= O_EXCL; + else + dest_open_flags |= O_TRUNC; + + dest_fd = TEMP_FAILURE_RETRY (openat (dest_dfd, dest_subpath, dest_open_flags, src_stbuf->st_mode)); + if (dest_fd == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + + r = copy_bytes (src_fd, dest_fd, (off_t) -1, TRUE); + if (r < 0) + { + errno = -r; + glnx_set_error_from_errno (error); + goto out; + } + + if (fchown (dest_fd, src_stbuf->st_uid, src_stbuf->st_gid) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (fchmod (dest_fd, src_stbuf->st_mode & 07777) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + ts[0] = src_stbuf->st_atim; + ts[1] = src_stbuf->st_mtim; + (void) futimens (dest_fd, ts); + + if (!(copyflags & GLNX_FILE_COPY_NOXATTRS)) + { + g_autoptr(GVariant) xattrs = NULL; + + if (!glnx_fd_get_all_xattrs (src_fd, &xattrs, + cancellable, error)) + goto out; + + if (!glnx_fd_set_all_xattrs (dest_fd, xattrs, + cancellable, error)) + goto out; + } + + if (copyflags & GLNX_FILE_COPY_DATASYNC) + { + if (fdatasync (dest_fd) < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + + r = close (dest_fd); + dest_fd = -1; + if (r < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + ret = TRUE; + out: + if (!ret) + (void) unlinkat (dest_dfd, dest_subpath, 0); + return ret; +} + +/** + * glnx_file_replace_contents_at: + * @dfd: Directory fd + * @subpath: Subpath + * @buf: (array len=len) (element-type guint8): File contents + * @len: Length (if `-1`, assume @buf is `NUL` terminated) + * @flags: Flags + * @cancellable: Cancellable + * @error: Error + * + * Create a new file, atomically replacing the contents of @subpath + * (relative to @dfd) with @buf. By default, if the file already + * existed, fdatasync() will be used before rename() to ensure stable + * contents. This and other behavior can be controlled via @flags. + * + * Note that no metadata from the existing file is preserved, such as + * uid/gid or extended attributes. The default mode will be `0666`, + * modified by umask. + */ +gboolean +glnx_file_replace_contents_at (int dfd, + const char *subpath, + const guint8 *buf, + gsize len, + GLnxFileReplaceFlags flags, + GCancellable *cancellable, + GError **error) +{ + return glnx_file_replace_contents_with_perms_at (dfd, subpath, buf, len, + (mode_t) -1, (uid_t) -1, (gid_t) -1, + flags, cancellable, error); + +} + +/** + * glnx_file_replace_contents_with_perms_at: + * @dfd: Directory fd + * @subpath: Subpath + * @buf: (array len=len) (element-type guint8): File contents + * @len: Length (if `-1`, assume @buf is `NUL` terminated) + * @mode: File mode; if `-1`, use `0666 - umask` + * @flags: Flags + * @cancellable: Cancellable + * @error: Error + * + * Like glnx_file_replace_contents_at(), but also supports + * setting mode, and uid/gid. + */ +gboolean +glnx_file_replace_contents_with_perms_at (int dfd, + const char *subpath, + const guint8 *buf, + gsize len, + mode_t mode, + uid_t uid, + gid_t gid, + GLnxFileReplaceFlags flags, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + int r; + /* We use the /proc/self trick as there's no mkostemp_at() yet */ + g_autofree char *tmppath = g_strdup_printf ("/proc/self/fd/%d/.tmpXXXXXX", dfd); + glnx_fd_close int fd = -1; + + dfd = glnx_dirfd_canonicalize (dfd); + + if ((fd = g_mkstemp_full (tmppath, O_WRONLY | O_CLOEXEC, + mode == (mode_t) -1 ? 0666 : mode)) == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (len == -1) + len = strlen ((char*)buf); + + /* Note that posix_fallocate does *not* set errno but returns it. */ + r = posix_fallocate (fd, 0, len); + if (r != 0) + { + errno = r; + glnx_set_error_from_errno (error); + goto out; + } + + if ((r = loop_write (fd, buf, len)) != 0) + { + errno = -r; + glnx_set_error_from_errno (error); + goto out; + } + + if (!(flags & GLNX_FILE_REPLACE_NODATASYNC)) + { + struct stat stbuf; + gboolean do_sync; + + if (fstatat (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW) != 0) + { + if (errno != ENOENT) + { + glnx_set_error_from_errno (error); + goto out; + } + do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0; + } + else + do_sync = TRUE; + + if (do_sync) + { + if (fdatasync (fd) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + } + + if (uid != (uid_t) -1) + { + if (fchown (fd, uid, gid) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + + /* If a mode was forced, override umask */ + if (mode != (mode_t) -1) + { + if (fchmod (fd, mode) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + + if (renameat (dfd, tmppath, dfd, subpath) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + ret = TRUE; + out: + return ret; +} diff --git a/libglnx/glnx-fdio.h b/libglnx/glnx-fdio.h new file mode 100644 index 00000000..a90544a9 --- /dev/null +++ b/libglnx/glnx-fdio.h @@ -0,0 +1,121 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +/* From systemd/src/shared/util.h */ +/* When we include libgen.h because we need dirname() we immediately + * undefine basename() since libgen.h defines it as a macro to the XDG + * version which is really broken. */ +#include +#undef basename + +G_BEGIN_DECLS + +/* Irritatingly, g_basename() which is what we want + * is deprecated. + */ +static inline +const char *glnx_basename (const char *path) +{ + return (basename) (path); +} + +GBytes * +glnx_fd_readall_bytes (int fd, + GCancellable *cancellable, + GError **error); + +char * +glnx_fd_readall_utf8 (int fd, + gsize *out_len, + GCancellable *cancellable, + GError **error); + +char * +glnx_file_get_contents_utf8_at (int dfd, + const char *subpath, + gsize *out_len, + GCancellable *cancellable, + GError **error); + +/** + * GLnxFileReplaceFlags: + * @GLNX_FILE_REPLACE_DATASYNC_NEW: Call fdatasync() even if the file did not exist + * @GLNX_FILE_REPLACE_NODATASYNC: Never call fdatasync() + * + * Flags controlling file replacement. + */ +typedef enum { + GLNX_FILE_REPLACE_DATASYNC_NEW = (1 << 0), + GLNX_FILE_REPLACE_NODATASYNC = (1 << 1), +} GLnxFileReplaceFlags; + +gboolean +glnx_file_replace_contents_at (int dfd, + const char *subpath, + const guint8 *buf, + gsize len, + GLnxFileReplaceFlags flags, + GCancellable *cancellable, + GError **error); + +gboolean +glnx_file_replace_contents_with_perms_at (int dfd, + const char *subpath, + const guint8 *buf, + gsize len, + mode_t mode, + uid_t uid, + gid_t gid, + GLnxFileReplaceFlags flags, + GCancellable *cancellable, + GError **error); + +char * +glnx_readlinkat_malloc (int dfd, + const char *subpath, + GCancellable *cancellable, + GError **error); + +typedef enum { + GLNX_FILE_COPY_OVERWRITE, + GLNX_FILE_COPY_NOXATTRS, + GLNX_FILE_COPY_DATASYNC +} GLnxFileCopyFlags; + +gboolean +glnx_file_copy_at (int src_dfd, + const char *src_subpath, + struct stat *src_stbuf, + int dest_dfd, + const char *dest_subpath, + GLnxFileCopyFlags copyflags, + GCancellable *cancellable, + GError **error); + +G_END_DECLS diff --git a/libglnx/glnx-libcontainer.c b/libglnx/glnx-libcontainer.c new file mode 100644 index 00000000..8c0f3407 --- /dev/null +++ b/libglnx/glnx-libcontainer.c @@ -0,0 +1,292 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Portions derived from src/nspawn/nspawn.c: + * Copyright 2010 Lennart Poettering + * + * Copyright (C) 2014,2015 Colin Walters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glnx-libcontainer.h" + +#include "glnx-backport-autocleanups.h" +#include "glnx-local-alloc.h" + +static void _perror_fatal (const char *message) __attribute__ ((noreturn)); + +static void +_perror_fatal (const char *message) +{ + perror (message); + exit (1); +} + +typedef enum { + CONTAINER_UNINIT = 0, + CONTAINER_YES = 1, + CONTAINER_NO = 2 +} ContainerDetectionState; + +static gboolean +currently_in_container (void) +{ + static gsize container_detected = CONTAINER_UNINIT; + + if (g_once_init_enter (&container_detected)) + { + ContainerDetectionState tmp_state = CONTAINER_NO; + struct stat stbuf; + + /* http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface/ */ + if (getenv ("container") != NULL + || stat ("/.dockerinit", &stbuf) == 0) + tmp_state = CONTAINER_YES; + /* But since Docker isn't on board, yet, so... + http://stackoverflow.com/questions/23513045/how-to-check-if-a-process-is-running-inside-docker-container */ + g_once_init_leave (&container_detected, tmp_state); + } + return container_detected == CONTAINER_YES; +} + +#if 0 +static gboolean +glnx_libcontainer_bind_mount_readonly (const char *path, GError **error) +{ + gboolean ret = FALSE; + + if (mount (path, path, NULL, MS_BIND | MS_PRIVATE, NULL) != 0) + { + int errsv = errno; + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "mount(%s, MS_BIND): %s", + path, + g_strerror (errsv)); + goto out; + } + if (mount (path, path, NULL, MS_BIND | MS_PRIVATE | MS_REMOUNT | MS_RDONLY, NULL) != 0) + { + int errsv = errno; + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "mount(%s, MS_BIND | MS_RDONLY): %s", + path, + g_strerror (errsv)); + goto out; + } + + ret = TRUE; + out: + return ret; +} +#endif + +/* Based on code from nspawn.c */ +static int +glnx_libcontainer_make_api_mounts (const char *dest) +{ + typedef struct MountPoint { + const char *what; + const char *where; + const char *type; + const char *options; + unsigned long flags; + gboolean fatal; + } MountPoint; + + static const MountPoint mount_table[] = { + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE }, + { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, TRUE }, /* Bind mount first */ + { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, TRUE }, /* Then, make it r/o */ + { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE }, + { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, TRUE }, + { "devpts", "/dev/pts", "devpts","newinstance,ptmxmode=0666,mode=620,gid=5", MS_NOSUID|MS_NOEXEC, TRUE }, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, TRUE }, + { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, TRUE }, + { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, FALSE }, /* Bind mount first */ + { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, FALSE }, /* Then, make it r/o */ + }; + + unsigned k; + + for (k = 0; k < G_N_ELEMENTS(mount_table); k++) + { + g_autofree char *where = NULL; + int t; + + where = g_build_filename (dest, mount_table[k].where, NULL); + + t = mkdir (where, 0755); + if (t < 0 && errno != EEXIST) + { + if (!mount_table[k].fatal) + continue; + return -1; + } + + if (mount (mount_table[k].what, + where, + mount_table[k].type, + mount_table[k].flags, + mount_table[k].options) < 0) + { + if (errno == ENOENT && !mount_table[k].fatal) + continue; + return -1; + } + } + + return 0; +} + +static int +glnx_libcontainer_prep_dev (const char *dest_devdir) +{ + glnx_fd_close int src_fd = -1; + glnx_fd_close int dest_fd = -1; + struct stat stbuf; + guint i; + static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; + + src_fd = openat (AT_FDCWD, "/dev", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); + if (src_fd == -1) + return -1; + + dest_fd = openat (AT_FDCWD, dest_devdir, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); + if (dest_fd == -1) + return -1; + + for (i = 0; i < G_N_ELEMENTS (devnodes); i++) + { + const char *nodename = devnodes[i]; + + if (fstatat (src_fd, nodename, &stbuf, 0) == -1) + { + if (errno == ENOENT) + continue; + else + return -1; + } + + if (mknodat (dest_fd, nodename, stbuf.st_mode, stbuf.st_rdev) != 0) + return -1; + if (fchmodat (dest_fd, nodename, stbuf.st_mode, 0) != 0) + return -1; + } + + return 0; +} + +pid_t +glnx_libcontainer_run_chroot_private (const char *dest, + const char *binary, + char **argv) +{ + /* Make most new namespaces; note our use of CLONE_NEWNET means we + * have no networking in the container root. + */ + const int cloneflags = + SIGCHLD | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_SYSVSEM | CLONE_NEWUTS; + pid_t child; + gboolean in_container = currently_in_container (); + + if (!in_container) + { + if ((child = syscall (__NR_clone, cloneflags, NULL)) < 0) + return -1; + } + else + { + if ((child = fork ()) < 0) + return -1; + } + + if (child != 0) + return child; + + if (!in_container) + { + if (mount (NULL, "/", "none", MS_PRIVATE | MS_REC, NULL) != 0) + { + if (errno == EINVAL) + { + /* Ok, we may be inside a mock chroot or the like. In + * that case, let's just fall back to not + * containerizing. + */ + in_container = TRUE; + } + else + _perror_fatal ("mount: "); + } + + if (!in_container) + { + if (mount (NULL, "/", "none", MS_PRIVATE | MS_REMOUNT | MS_NOSUID, NULL) != 0) + _perror_fatal ("mount (MS_NOSUID): "); + } + } + + if (chdir (dest) != 0) + _perror_fatal ("chdir: "); + + if (!in_container) + { + if (glnx_libcontainer_make_api_mounts (dest) != 0) + _perror_fatal ("preparing api mounts: "); + + if (glnx_libcontainer_prep_dev ("dev") != 0) + _perror_fatal ("preparing /dev: "); + + if (mount (".", ".", NULL, MS_BIND | MS_PRIVATE, NULL) != 0) + _perror_fatal ("mount (MS_BIND)"); + + if (mount (dest, "/", NULL, MS_MOVE, NULL) != 0) + _perror_fatal ("mount (MS_MOVE)"); + } + + if (chroot (".") != 0) + _perror_fatal ("chroot: "); + + if (chdir ("/") != 0) + _perror_fatal ("chdir: "); + + if (binary[0] == '/') + { + if (execv (binary, argv) != 0) + _perror_fatal ("execv: "); + } + else + { + /* Set PATH to something sane. */ + setenv ("PATH", "/usr/sbin:/usr/bin", 1); + + if (execvp (binary, argv) != 0) + _perror_fatal ("execvp: "); + } + + g_assert_not_reached (); +} diff --git a/libglnx/glnx-libcontainer.h b/libglnx/glnx-libcontainer.h new file mode 100644 index 00000000..10855db7 --- /dev/null +++ b/libglnx/glnx-libcontainer.h @@ -0,0 +1,36 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +pid_t glnx_libcontainer_run_chroot_private (const char *dest, + const char *binary, + char **argv); diff --git a/libglnx/glnx-local-alloc.c b/libglnx/glnx-local-alloc.c new file mode 100644 index 00000000..692f0de2 --- /dev/null +++ b/libglnx/glnx-local-alloc.c @@ -0,0 +1,72 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012,2015 Colin Walters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "glnx-local-alloc.h" + +/** + * SECTION:glnxlocalalloc + * @title: GLnx local allocation + * @short_description: Release local variables automatically when they go out of scope + * + * These macros leverage the GCC extension __attribute__ ((cleanup)) + * to allow calling a cleanup function such as g_free() when a + * variable goes out of scope. See + * for more information on the attribute. + * + * The provided macros make it easy to use the cleanup attribute for + * types that come with GLib. The primary two are #glnx_free and + * #glnx_unref_object, which correspond to g_free() and + * g_object_unref(), respectively. + * + * The rationale behind this is that particularly when handling error + * paths, it can be very tricky to ensure the right variables are + * freed. With this, one simply applies glnx_unref_object to a + * locally-allocated #GFile for example, and it will be automatically + * unreferenced when it goes out of scope. + * + * Note - you should only use these macros for stack + * allocated variables. They don't provide garbage + * collection or let you avoid freeing things. They're simply a + * compiler assisted deterministic mechanism for calling a cleanup + * function when a stack frame ends. + * + * Calling g_free automatically + * + * + * GFile * + * create_file (GError **error) + * { + * glnx_free char *random_id = NULL; + * + * if (!prepare_file (error)) + * return NULL; + * + * random_id = alloc_random_id (); + * + * return create_file_real (error); + * // Note that random_id is freed here automatically + * } + * + * + * + */ diff --git a/libglnx/glnx-local-alloc.h b/libglnx/glnx-local-alloc.h new file mode 100644 index 00000000..af5af4b8 --- /dev/null +++ b/libglnx/glnx-local-alloc.h @@ -0,0 +1,222 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define GLNX_DEFINE_CLEANUP_FUNCTION(Type, name, func) \ + static inline void name (void *v) \ + { \ + func (*(Type*)v); \ + } + +#define GLNX_DEFINE_CLEANUP_FUNCTION0(Type, name, func) \ + static inline void name (void *v) \ + { \ + if (*(Type*)v) \ + func (*(Type*)v); \ + } + +/** + * glnx_free: + * + * Call g_free() on a variable location when it goes out of scope. + */ +#define glnx_free __attribute__ ((cleanup(glnx_local_free))) +#ifdef GLNX_GSYSTEM_COMPAT +#define gs_free __attribute__ ((cleanup(glnx_local_free))) +#endif +GLNX_DEFINE_CLEANUP_FUNCTION(void*, glnx_local_free, g_free) + +/** + * glnx_unref_object: + * + * Call g_object_unref() on a variable location when it goes out of + * scope. Note that unlike g_object_unref(), the variable may be + * %NULL. + */ +#define glnx_unref_object __attribute__ ((cleanup(glnx_local_obj_unref))) +#ifdef GLNX_GSYSTEM_COMPAT +#define gs_unref_object __attribute__ ((cleanup(glnx_local_obj_unref))) +#endif +GLNX_DEFINE_CLEANUP_FUNCTION0(GObject*, glnx_local_obj_unref, g_object_unref) + +/** + * glnx_unref_variant: + * + * Call g_variant_unref() on a variable location when it goes out of + * scope. Note that unlike g_variant_unref(), the variable may be + * %NULL. + */ +#define glnx_unref_variant __attribute__ ((cleanup(glnx_local_variant_unref))) +#ifdef GLNX_GSYSTEM_COMPAT +#define gs_unref_variant __attribute__ ((cleanup(glnx_local_variant_unref))) +#endif +GLNX_DEFINE_CLEANUP_FUNCTION0(GVariant*, glnx_local_variant_unref, g_variant_unref) + +/** + * glnx_free_variant_iter: + * + * Call g_variant_iter_free() on a variable location when it goes out of + * scope. + */ +#define glnx_free_variant_iter __attribute__ ((cleanup(glnx_local_variant_iter_free))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GVariantIter*, glnx_local_variant_iter_free, g_variant_iter_free) + +/** + * glnx_free_variant_builder: + * + * Call g_variant_builder_unref() on a variable location when it goes out of + * scope. + */ +#define glnx_unref_variant_builder __attribute__ ((cleanup(glnx_local_variant_builder_unref))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GVariantBuilder*, glnx_local_variant_builder_unref, g_variant_builder_unref) + +/** + * glnx_unref_array: + * + * Call g_array_unref() on a variable location when it goes out of + * scope. Note that unlike g_array_unref(), the variable may be + * %NULL. + + */ +#define glnx_unref_array __attribute__ ((cleanup(glnx_local_array_unref))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GArray*, glnx_local_array_unref, g_array_unref) + +/** + * glnx_unref_ptrarray: + * + * Call g_ptr_array_unref() on a variable location when it goes out of + * scope. Note that unlike g_ptr_array_unref(), the variable may be + * %NULL. + + */ +#define glnx_unref_ptrarray __attribute__ ((cleanup(glnx_local_ptrarray_unref))) +#ifdef GLNX_GSYSTEM_COMPAT +#define gs_unref_ptrarray __attribute__ ((cleanup(glnx_local_ptrarray_unref))) +#endif +GLNX_DEFINE_CLEANUP_FUNCTION0(GPtrArray*, glnx_local_ptrarray_unref, g_ptr_array_unref) + +/** + * glnx_unref_hashtable: + * + * Call g_hash_table_unref() on a variable location when it goes out + * of scope. Note that unlike g_hash_table_unref(), the variable may + * be %NULL. + */ +#define glnx_unref_hashtable __attribute__ ((cleanup(glnx_local_hashtable_unref))) +#ifdef GLNX_GSYSTEM_COMPAT +#define gs_unref_hashtable __attribute__ ((cleanup(glnx_local_hashtable_unref))) +#endif +GLNX_DEFINE_CLEANUP_FUNCTION0(GHashTable*, glnx_local_hashtable_unref, g_hash_table_unref) + +/** + * glnx_free_list: + * + * Call g_list_free() on a variable location when it goes out + * of scope. + */ +#define glnx_free_list __attribute__ ((cleanup(glnx_local_free_list))) +GLNX_DEFINE_CLEANUP_FUNCTION(GList*, glnx_local_free_list, g_list_free) + +/** + * glnx_free_slist: + * + * Call g_slist_free() on a variable location when it goes out + * of scope. + */ +#define glnx_free_slist __attribute__ ((cleanup(glnx_local_free_slist))) +GLNX_DEFINE_CLEANUP_FUNCTION(GSList*, glnx_local_free_slist, g_slist_free) + +/** + * glnx_free_checksum: + * + * Call g_checksum_free() on a variable location when it goes out + * of scope. Note that unlike g_checksum_free(), the variable may + * be %NULL. + */ +#define glnx_free_checksum __attribute__ ((cleanup(glnx_local_checksum_free))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GChecksum*, glnx_local_checksum_free, g_checksum_free) + +/** + * glnx_unref_bytes: + * + * Call g_bytes_unref() on a variable location when it goes out + * of scope. Note that unlike g_bytes_unref(), the variable may + * be %NULL. + */ +#define glnx_unref_bytes __attribute__ ((cleanup(glnx_local_bytes_unref))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GBytes*, glnx_local_bytes_unref, g_bytes_unref) + +/** + * glnx_strfreev: + * + * Call g_strfreev() on a variable location when it goes out of scope. + */ +#define glnx_strfreev __attribute__ ((cleanup(glnx_local_strfreev))) +GLNX_DEFINE_CLEANUP_FUNCTION(char**, glnx_local_strfreev, g_strfreev) + +/** + * glnx_free_error: + * + * Call g_error_free() on a variable location when it goes out of scope. + */ +#define glnx_free_error __attribute__ ((cleanup(glnx_local_free_error))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GError*, glnx_local_free_error, g_error_free) + +/** + * glnx_unref_keyfile: + * + * Call g_key_file_unref() on a variable location when it goes out of scope. + */ +#define glnx_unref_keyfile __attribute__ ((cleanup(glnx_local_keyfile_unref))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, glnx_local_keyfile_unref, g_key_file_unref) + +static inline void +glnx_cleanup_close_fdp (int *fdp) +{ + int fd; + + g_assert (fdp); + + fd = *fdp; + if (fd != -1) + (void) close (fd); +} + +/** + * glnx_fd_close: + * + * Call close() on a variable location when it goes out of scope. + */ +#define glnx_fd_close __attribute__((cleanup(glnx_cleanup_close_fdp))) + +static inline int +glnx_steal_fd (int *fdp) +{ + int fd = *fdp; + *fdp = -1; + return fd; +} + +G_END_DECLS diff --git a/libglnx/glnx-lockfile.c b/libglnx/glnx-lockfile.c new file mode 100644 index 00000000..be9ad3a0 --- /dev/null +++ b/libglnx/glnx-lockfile.c @@ -0,0 +1,193 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + Now copied into libglnx: + - Use GError + + Copyright 2010 Lennart Poettering + Copyright 2015 Colin Walters + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glnx-lockfile.h" +#include "glnx-errors.h" +#include "glnx-fdio.h" +#include "glnx-backport-autocleanups.h" +#include "glnx-local-alloc.h" + +#define newa(t, n) ((t*) alloca(sizeof(t)*(n))) + +/** + * glnx_make_lock_file: + * @dfd: Directory file descriptor (if not `AT_FDCWD`, must have lifetime `>=` @out_lock) + * @p: Path + * @operation: one of `LOCK_SH`, `LOCK_EX`, `LOCK_UN`, as passed to flock() + * @out_lock: (out) (caller allocates): Return location for lock + * @error: Error + * + * Block until a lock file named @p (relative to @dfd) can be created, + * using the flags in @operation, returning the lock data in the + * caller-allocated location @out_lock. + * + * This API wraps new-style process locking if available, otherwise + * falls back to BSD locks. + */ +gboolean +glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_lock, GError **error) { + gboolean ret = FALSE; + glnx_fd_close int fd = -1; + g_autofree char *t = NULL; + int r; + + /* + * We use UNPOSIX locks if they are available. They have nice + * semantics, and are mostly compatible with NFS. However, + * they are only available on new kernels. When we detect we + * are running on an older kernel, then we fall back to good + * old BSD locks. They also have nice semantics, but are + * slightly problematic on NFS, where they are upgraded to + * POSIX locks, even though locally they are orthogonal to + * POSIX locks. + */ + + t = g_strdup(p); + + for (;;) { +#ifdef F_OFD_SETLK + struct flock fl = { + .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK, + .l_whence = SEEK_SET, + }; +#endif + struct stat st; + + fd = openat(dfd, p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); + if (fd < 0) { + glnx_set_error_from_errno(error); + goto out; + } + + /* Unfortunately, new locks are not in RHEL 7.1 glibc */ +#ifdef F_OFD_SETLK + r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl); +#else + r = -1; + errno = EINVAL; +#endif + if (r < 0) { + + /* If the kernel is too old, use good old BSD locks */ + if (errno == EINVAL) + r = flock(fd, operation); + + if (r < 0) { + glnx_set_error_from_errno(error); + goto out; + } + } + + /* If we acquired the lock, let's check if the file + * still exists in the file system. If not, then the + * previous exclusive owner removed it and then closed + * it. In such a case our acquired lock is worthless, + * hence try again. */ + + r = fstat(fd, &st); + if (r < 0) { + glnx_set_error_from_errno(error); + goto out; + } + if (st.st_nlink > 0) + break; + + (void) close(fd); + fd = -1; + } + + /* Note that if this is not AT_FDCWD, the caller takes responsibility + * for the fd's lifetime being >= that of the lock. + */ + out_lock->dfd = dfd; + out_lock->path = t; + out_lock->fd = fd; + out_lock->operation = operation; + + fd = -1; + t = NULL; + + ret = TRUE; + out: + return ret; +} + +void glnx_release_lock_file(GLnxLockFile *f) { + int r; + + if (!f) + return; + + if (f->path) { + + /* If we are the exclusive owner we can safely delete + * the lock file itself. If we are not the exclusive + * owner, we can try becoming it. */ + + if (f->fd >= 0 && + (f->operation & ~LOCK_NB) == LOCK_SH) { +#ifdef F_OFD_SETLK + static const struct flock fl = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + }; + + r = fcntl(f->fd, F_OFD_SETLK, &fl); +#else + r = -1; + errno = EINVAL; +#endif + if (r < 0 && errno == EINVAL) + r = flock(f->fd, LOCK_EX|LOCK_NB); + + if (r >= 0) + f->operation = LOCK_EX|LOCK_NB; + } + + if ((f->operation & ~LOCK_NB) == LOCK_EX) { + (void) unlinkat(f->dfd, f->path, 0); + } + + g_free(f->path); + f->path = NULL; + } + + (void) close (f->fd); + f->fd = -1; + f->operation = 0; +} diff --git a/libglnx/glnx-lockfile.h b/libglnx/glnx-lockfile.h new file mode 100644 index 00000000..6d132e5f --- /dev/null +++ b/libglnx/glnx-lockfile.h @@ -0,0 +1,41 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + Copyright 2015 Colin Walters + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "config.h" + +#include "glnx-backport-autoptr.h" + +typedef struct GLnxLockFile { + int dfd; + char *path; + int fd; + int operation; +} GLnxLockFile; + +gboolean glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *ret, GError **error); +void glnx_release_lock_file(GLnxLockFile *f); + +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxLockFile, glnx_release_lock_file) + +#define GLNX_LOCK_FILE_INIT { .fd = -1, .dfd = AT_FDCWD, .path = NULL } diff --git a/libglnx/glnx-shutil.c b/libglnx/glnx-shutil.c new file mode 100644 index 00000000..281e5bf0 --- /dev/null +++ b/libglnx/glnx-shutil.c @@ -0,0 +1,287 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include +#include + +static unsigned char +struct_stat_to_dt (struct stat *stbuf) +{ + if (S_ISDIR (stbuf->st_mode)) + return DT_DIR; + if (S_ISREG (stbuf->st_mode)) + return DT_REG; + if (S_ISCHR (stbuf->st_mode)) + return DT_CHR; + if (S_ISBLK (stbuf->st_mode)) + return DT_BLK; + if (S_ISFIFO (stbuf->st_mode)) + return DT_FIFO; + if (S_ISLNK (stbuf->st_mode)) + return DT_LNK; + if (S_ISSOCK (stbuf->st_mode)) + return DT_SOCK; + return DT_UNKNOWN; +} + +static gboolean +glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + struct dirent *dent; + + while (TRUE) + { + if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, cancellable, error)) + goto out; + + if (dent == NULL) + break; + + if (dent->d_type == DT_UNKNOWN) + { + struct stat stbuf; + if (fstatat (dfd_iter->fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) + { + if (errno == ENOENT) + continue; + else + { + glnx_set_error_from_errno (error); + goto out; + } + } + dent->d_type = struct_stat_to_dt (&stbuf); + /* Assume unknown types are just treated like regular files */ + if (dent->d_type == DT_UNKNOWN) + dent->d_type = DT_REG; + } + + if (dent->d_type == DT_DIR) + { + g_auto(GLnxDirFdIterator) child_dfd_iter = { 0, }; + + if (!glnx_dirfd_iterator_init_at (dfd_iter->fd, dent->d_name, FALSE, + &child_dfd_iter, error)) + goto out; + + if (!glnx_shutil_rm_rf_children (&child_dfd_iter, cancellable, error)) + goto out; + + if (unlinkat (dfd_iter->fd, dent->d_name, AT_REMOVEDIR) == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + } + else + { + if (unlinkat (dfd_iter->fd, dent->d_name, 0) == -1) + { + if (errno != ENOENT) + { + glnx_set_error_from_errno (error); + goto out; + } + } + } + } + + ret = TRUE; + out: + return ret; +} + +/** + * glnx_shutil_rm_rf_at: + * @dfd: A directory file descriptor, or `AT_FDCWD` or `-1` for current + * @path: Path + * @cancellable: Cancellable + * @error: Error + * + * Recursively delete the filename referenced by the combination of + * the directory fd @dfd and @path; it may be a file or directory. No + * error is thrown if @path does not exist. + */ +gboolean +glnx_shutil_rm_rf_at (int dfd, + const char *path, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + glnx_fd_close int target_dfd = -1; + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + + dfd = glnx_dirfd_canonicalize (dfd); + + /* With O_NOFOLLOW first */ + target_dfd = openat (dfd, path, + O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); + + if (target_dfd == -1) + { + int errsv = errno; + if (errsv == ENOENT) + { + ; + } + else if (errsv == ENOTDIR || errsv == ELOOP) + { + if (unlinkat (dfd, path, 0) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + else + { + glnx_set_error_from_errno (error); + goto out; + } + } + else + { + if (!glnx_dirfd_iterator_init_take_fd (target_dfd, &dfd_iter, error)) + goto out; + target_dfd = -1; + + if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error)) + goto out; + + if (unlinkat (dfd, path, AT_REMOVEDIR) == -1) + { + int errsv = errno; + if (errsv != ENOENT) + { + glnx_set_error_from_errno (error); + goto out; + } + } + } + + ret = TRUE; + out: + return ret; +} + +static gboolean +mkdir_p_at_internal (int dfd, + char *path, + int mode, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gboolean did_recurse = FALSE; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + goto out; + + again: + if (mkdirat (dfd, path, mode) == -1) + { + if (errno == ENOENT) + { + char *lastslash; + + g_assert (!did_recurse); + + lastslash = strrchr (path, '/'); + g_assert (lastslash != NULL); + /* Note we can mutate the buffer as we dup'd it */ + *lastslash = '\0'; + + if (!glnx_shutil_mkdir_p_at (dfd, path, mode, + cancellable, error)) + goto out; + + /* Now restore it for another mkdir attempt */ + *lastslash = '/'; + + did_recurse = TRUE; + goto again; + } + else if (errno == EEXIST) + { + /* Fall through; it may not have been a directory, + * but we'll find that out on the next call up. + */ + } + else + { + glnx_set_error_from_errno (error); + goto out; + } + } + + ret = TRUE; + out: + return ret; +} + +/** + * glnx_shutil_mkdir_p_at: + * @dfd: Directory fd + * @path: Directory path to be created + * @mode: Mode for newly created directories + * @cancellable: Cancellable + * @error: Error + * + * Similar to g_mkdir_with_parents(), except operates relative to the + * directory fd @dfd. + */ +gboolean +glnx_shutil_mkdir_p_at (int dfd, + const char *path, + int mode, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + struct stat stbuf; + char *buf; + + /* Fast path stat to see whether it already exists */ + if (fstatat (dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == 0) + { + if (S_ISDIR (stbuf.st_mode)) + { + ret = TRUE; + goto out; + } + } + + buf = strdupa (path); + + if (!mkdir_p_at_internal (dfd, buf, mode, cancellable, error)) + goto out; + + ret = TRUE; + out: + return ret; +} diff --git a/libglnx/glnx-shutil.h b/libglnx/glnx-shutil.h new file mode 100644 index 00000000..8cc732c3 --- /dev/null +++ b/libglnx/glnx-shutil.h @@ -0,0 +1,40 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +gboolean +glnx_shutil_rm_rf_at (int dfd, + const char *path, + GCancellable *cancellable, + GError **error); + +gboolean +glnx_shutil_mkdir_p_at (int dfd, + const char *path, + int mode, + GCancellable *cancellable, + GError **error); + +G_END_DECLS diff --git a/libglnx/glnx-xattrs.c b/libglnx/glnx-xattrs.c new file mode 100644 index 00000000..b5a59228 --- /dev/null +++ b/libglnx/glnx-xattrs.c @@ -0,0 +1,526 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include +#include +#include + +static GVariant * +variant_new_ay_bytes (GBytes *bytes) +{ + gsize size; + gconstpointer data; + data = g_bytes_get_data (bytes, &size); + g_bytes_ref (bytes); + return g_variant_new_from_data (G_VARIANT_TYPE ("ay"), data, size, + TRUE, (GDestroyNotify)g_bytes_unref, bytes); +} + +static char * +canonicalize_xattrs (char *xattr_string, + size_t len) +{ + char *p; + GSList *xattrs = NULL; + GSList *iter; + GString *result; + + result = g_string_new (0); + + p = xattr_string; + while (p < xattr_string+len) + { + xattrs = g_slist_prepend (xattrs, p); + p += strlen (p) + 1; + } + + xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp); + for (iter = xattrs; iter; iter = iter->next) { + g_string_append (result, iter->data); + g_string_append_c (result, '\0'); + } + + g_slist_free (xattrs); + return g_string_free (result, FALSE); +} + +static gboolean +read_xattr_name_array (const char *path, + int fd, + const char *xattrs, + size_t len, + GVariantBuilder *builder, + GError **error) +{ + gboolean ret = FALSE; + const char *p; + int r; + const char *funcstr; + + g_assert (path != NULL || fd != -1); + + funcstr = fd != -1 ? "fgetxattr" : "lgetxattr"; + + p = xattrs; + while (p < xattrs+len) + { + ssize_t bytes_read; + char *buf; + GBytes *bytes = NULL; + + if (fd != -1) + bytes_read = fgetxattr (fd, p, NULL, 0); + else + bytes_read = lgetxattr (path, p, NULL, 0); + if (bytes_read < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", funcstr); + goto out; + } + if (bytes_read == 0) + continue; + + buf = g_malloc (bytes_read); + bytes = g_bytes_new_take (buf, bytes_read); + if (fd != -1) + r = fgetxattr (fd, p, buf, bytes_read); + else + r = lgetxattr (path, p, buf, bytes_read); + if (r < 0) + { + g_bytes_unref (bytes); + glnx_set_prefix_error_from_errno (error, "%s", funcstr); + goto out; + } + + g_variant_builder_add (builder, "(@ay@ay)", + g_variant_new_bytestring (p), + variant_new_ay_bytes (bytes)); + + p = p + strlen (p) + 1; + g_bytes_unref (bytes); + } + + ret = TRUE; + out: + return ret; +} + +static gboolean +get_xattrs_impl (const char *path, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ssize_t bytes_read; + glnx_free char *xattr_names = NULL; + glnx_free char *xattr_names_canonical = NULL; + GVariantBuilder builder; + gboolean builder_initialized = FALSE; + g_autoptr(GVariant) ret_xattrs = NULL; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)")); + builder_initialized = TRUE; + + bytes_read = llistxattr (path, NULL, 0); + + if (bytes_read < 0) + { + if (errno != ENOTSUP) + { + glnx_set_prefix_error_from_errno (error, "%s", "llistxattr"); + goto out; + } + } + else if (bytes_read > 0) + { + xattr_names = g_malloc (bytes_read); + if (llistxattr (path, xattr_names, bytes_read) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "llistxattr"); + goto out; + } + xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read); + + if (!read_xattr_name_array (path, -1, xattr_names_canonical, bytes_read, &builder, error)) + goto out; + } + + ret_xattrs = g_variant_builder_end (&builder); + builder_initialized = FALSE; + g_variant_ref_sink (ret_xattrs); + + ret = TRUE; + if (out_xattrs) + *out_xattrs = g_steal_pointer (&ret_xattrs); + out: + if (!builder_initialized) + g_variant_builder_clear (&builder); + return ret; +} + +/** + * glnx_fd_get_all_xattrs: + * @fd: a file descriptor + * @out_xattrs: (out): A new #GVariant containing the extended attributes + * @cancellable: Cancellable + * @error: Error + * + * Read all extended attributes from @fd in a canonical sorted order, and + * set @out_xattrs with the result. + * + * If the filesystem does not support extended attributes, @out_xattrs + * will have 0 elements, and this function will return successfully. + */ +gboolean +glnx_fd_get_all_xattrs (int fd, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ssize_t bytes_read; + glnx_free char *xattr_names = NULL; + glnx_free char *xattr_names_canonical = NULL; + GVariantBuilder builder; + gboolean builder_initialized = FALSE; + g_autoptr(GVariant) ret_xattrs = NULL; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)")); + builder_initialized = TRUE; + + bytes_read = flistxattr (fd, NULL, 0); + + if (bytes_read < 0) + { + if (errno != ENOTSUP) + { + glnx_set_prefix_error_from_errno (error, "%s", "flistxattr"); + goto out; + } + } + else if (bytes_read > 0) + { + xattr_names = g_malloc (bytes_read); + if (flistxattr (fd, xattr_names, bytes_read) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "flistxattr"); + goto out; + } + xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read); + + if (!read_xattr_name_array (NULL, fd, xattr_names_canonical, bytes_read, &builder, error)) + goto out; + } + + ret_xattrs = g_variant_builder_end (&builder); + builder_initialized = FALSE; + g_variant_ref_sink (ret_xattrs); + + ret = TRUE; + if (out_xattrs) + *out_xattrs = g_steal_pointer (&ret_xattrs); + out: + if (!builder_initialized) + g_variant_builder_clear (&builder); + return ret; +} + +/** + * glnx_dfd_name_get_all_xattrs: + * @dfd: Parent directory file descriptor + * @name: File name + * @out_xattrs: (out): Extended attribute set + * @cancellable: Cancellable + * @error: Error + * + * Load all extended attributes for the file named @name residing in + * directory @dfd. + */ +gboolean +glnx_dfd_name_get_all_xattrs (int dfd, + const char *name, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error) +{ + if (dfd == AT_FDCWD || dfd == -1) + { + return get_xattrs_impl (name, out_xattrs, cancellable, error); + } + else + { + char buf[PATH_MAX]; + /* A workaround for the lack of lgetxattrat(), thanks to Florian Weimer: + * https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html + */ + snprintf (buf, sizeof (buf), "/proc/self/fd/%d/%s", dfd, name); + return get_xattrs_impl (buf, out_xattrs, cancellable, error); + } +} + +static gboolean +set_all_xattrs_for_path (const char *path, + GVariant *xattrs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + int i, n; + + n = g_variant_n_children (xattrs); + for (i = 0; i < n; i++) + { + const guint8* name; + g_autoptr(GVariant) value = NULL; + const guint8* value_data; + gsize value_len; + + g_variant_get_child (xattrs, i, "(^&ay@ay)", + &name, &value); + value_data = g_variant_get_fixed_array (value, &value_len, 1); + + if (lsetxattr (path, (char*)name, (char*)value_data, value_len, 0) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "lsetxattr"); + goto out; + } + } + + ret = TRUE; + out: + return ret; +} + +/** + * glnx_dfd_name_set_all_xattrs: + * @dfd: Parent directory file descriptor + * @name: File name + * @xattrs: Extended attribute set + * @cancellable: Cancellable + * @error: Error + * + * Set all extended attributes for the file named @name residing in + * directory @dfd. + */ +gboolean +glnx_dfd_name_set_all_xattrs (int dfd, + const char *name, + GVariant *xattrs, + GCancellable *cancellable, + GError **error) +{ + if (dfd == AT_FDCWD || dfd == -1) + { + return set_all_xattrs_for_path (name, xattrs, cancellable, error); + } + else + { + char buf[PATH_MAX]; + /* A workaround for the lack of lsetxattrat(), thanks to Florian Weimer: + * https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html + */ + snprintf (buf, sizeof (buf), "/proc/self/fd/%d/%s", dfd, name); + return set_all_xattrs_for_path (buf, xattrs, cancellable, error); + } +} + +/** + * glnx_fd_set_all_xattrs: + * @fd: File descriptor + * @xattrs: Extended attributes + * @cancellable: Cancellable + * @error: Error + * + * For each attribute in @xattrs, set its value on the file or + * directory referred to by @fd. This function does not remove any + * attributes not in @xattrs. + */ +gboolean +glnx_fd_set_all_xattrs (int fd, + GVariant *xattrs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + int i, n; + + n = g_variant_n_children (xattrs); + for (i = 0; i < n; i++) + { + const guint8* name; + const guint8* value_data; + g_autoptr(GVariant) value = NULL; + gsize value_len; + int res; + + g_variant_get_child (xattrs, i, "(^&ay@ay)", + &name, &value); + value_data = g_variant_get_fixed_array (value, &value_len, 1); + + do + res = fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0); + while (G_UNLIKELY (res == -1 && errno == EINTR)); + if (G_UNLIKELY (res == -1)) + { + glnx_set_prefix_error_from_errno (error, "%s", "fsetxattr"); + goto out; + } + } + + ret = TRUE; + out: + return ret; +} + +/** + * glnx_lgetxattrat: + * @dfd: Directory file descriptor + * @subpath: Subpath + * @attribute: Extended attribute to retrieve + * @error: Error + * + * Retrieve an extended attribute value, relative to a directory file + * descriptor. + */ +GBytes * +glnx_lgetxattrat (int dfd, + const char *subpath, + const char *attribute, + GError **error) +{ + char pathbuf[PATH_MAX]; + GBytes *bytes = NULL; + ssize_t bytes_read, real_size; + guint8 *buf; + + snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath); + + do + bytes_read = lgetxattr (pathbuf, attribute, NULL, 0); + while (G_UNLIKELY (bytes_read < 0 && errno == EINTR)); + if (G_UNLIKELY (bytes_read < 0)) + { + glnx_set_error_from_errno (error); + goto out; + } + + buf = g_malloc (bytes_read); + do + real_size = lgetxattr (pathbuf, attribute, buf, bytes_read); + while (G_UNLIKELY (real_size < 0 && errno == EINTR)); + if (G_UNLIKELY (real_size < 0)) + { + glnx_set_error_from_errno (error); + g_free (buf); + goto out; + } + + bytes = g_bytes_new_take (buf, real_size); + out: + return bytes; +} + +/** + * glnx_fgetxattr_bytes: + * @fd: Directory file descriptor + * @attribute: Extended attribute to retrieve + * @error: Error + * + * Returns: (transfer full): An extended attribute value, or %NULL on error + */ +GBytes * +glnx_fgetxattr_bytes (int fd, + const char *attribute, + GError **error) +{ + GBytes *bytes = NULL; + ssize_t bytes_read, real_size; + guint8 *buf; + + do + bytes_read = fgetxattr (fd, attribute, NULL, 0); + while (G_UNLIKELY (bytes_read < 0 && errno == EINTR)); + if (G_UNLIKELY (bytes_read < 0)) + { + glnx_set_error_from_errno (error); + goto out; + } + + buf = g_malloc (bytes_read); + do + real_size = fgetxattr (fd, attribute, buf, bytes_read); + while (G_UNLIKELY (real_size < 0 && errno == EINTR)); + if (G_UNLIKELY (real_size < 0)) + { + glnx_set_error_from_errno (error); + g_free (buf); + goto out; + } + + bytes = g_bytes_new_take (buf, real_size); + out: + return bytes; +} + +/** + * glnx_lsetxattrat: + * @dfd: Directory file descriptor + * @subpath: Path + * @attribute: An attribute name + * @value: (array length=len) (element-type guint8): Attribute value + * @len: Length of @value + * @flags: Flags, containing either XATTR_CREATE or XATTR_REPLACE + * @error: Error + * + * Set an extended attribute, relative to a directory file descriptor. + */ +gboolean +glnx_lsetxattrat (int dfd, + const char *subpath, + const char *attribute, + const guint8 *value, + gsize len, + int flags, + GError **error) +{ + char pathbuf[PATH_MAX]; + int res; + + snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath); + + do + res = lsetxattr (subpath, attribute, value, len, flags); + while (G_UNLIKELY (res == -1 && errno == EINTR)); + if (G_UNLIKELY (res == -1)) + { + glnx_set_error_from_errno (error); + return FALSE; + } + + return TRUE; +} + diff --git a/libglnx/glnx-xattrs.h b/libglnx/glnx-xattrs.h new file mode 100644 index 00000000..a566a224 --- /dev/null +++ b/libglnx/glnx-xattrs.h @@ -0,0 +1,78 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +gboolean +glnx_dfd_name_get_all_xattrs (int dfd, + const char *name, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error); + +gboolean +glnx_fd_get_all_xattrs (int fd, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error); + +gboolean +glnx_dfd_name_set_all_xattrs (int dfd, + const char *name, + GVariant *xattrs, + GCancellable *cancellable, + GError **error); + +gboolean +glnx_fd_set_all_xattrs (int fd, + GVariant *xattrs, + GCancellable *cancellable, + GError **error); + +GBytes * +glnx_lgetxattrat (int dfd, + const char *subpath, + const char *attribute, + GError **error); + +GBytes * +glnx_fgetxattr_bytes (int fd, + const char *attribute, + GError **error); + +gboolean +glnx_lsetxattrat (int dfd, + const char *subpath, + const char *attribute, + const guint8 *value, + gsize len, + int flags, + GError **error); + +G_END_DECLS diff --git a/libglnx/libglnx.doap b/libglnx/libglnx.doap new file mode 100644 index 00000000..2b38074e --- /dev/null +++ b/libglnx/libglnx.doap @@ -0,0 +1,31 @@ + + + + libglnx + libglnx + + "Copylib" for system service modules using GLib with Linux + + This module is intended for use by + infrastructure code using GLib that is also Linux specific, such as + ostree, NetworkManager, and others. + + + + + + C + + + + Colin Walters + + walters + + + + diff --git a/libglnx/libglnx.h b/libglnx/libglnx.h new file mode 100644 index 00000000..9bd41c45 --- /dev/null +++ b/libglnx/libglnx.h @@ -0,0 +1,39 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012,2013,2015 Colin Walters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +G_END_DECLS