Imported Upstream version 2016.1
This commit is contained in:
parent
587fc5a5a1
commit
62a4fef292
1
bsdiff
1
bsdiff
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 1edf9f656850c0c64dae260960fabd8249ea9c60
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
.deps/*
|
||||||
|
.libs/*
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
.dirstamp
|
||||||
|
Makefile-bsdiff.am.inc
|
||||||
|
AUTHORS
|
||||||
|
NEWS
|
||||||
|
README
|
||||||
|
ChangeLog
|
||||||
|
COPYING
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Copyright (C) 2015 Giuseppe Scrivano <gscrivan@redhat.com>
|
||||||
|
#
|
||||||
|
# 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)
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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 <http://www.daemonology.net/bsdiff/>.
|
||||||
|
|
||||||
|
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)
|
||||||
|
<https://github.com/mendsley/bsdiff>
|
||||||
|
|
||||||
|
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 <https://www.carbongames.com> 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 `<stdint.h>` 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 <https://code.google.com/p/msinttypes/>.
|
||||||
|
The direct link for the lazy people is:
|
||||||
|
<https://msinttypes.googlecode.com/svn/trunk/stdint.h>.
|
||||||
|
|
||||||
|
If your compiler does not provide an implementation of `<stdint.h>` 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.
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
touch AUTHORS NEWS README ChangeLog
|
||||||
|
cp LICENSE COPYING
|
||||||
|
|
||||||
|
autoreconf -fis
|
||||||
|
|
@ -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 <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;k<start+len;k+=j) {
|
||||||
|
j=1;x=V[I[k]+h];
|
||||||
|
for(i=1;k+i<start+len;i++) {
|
||||||
|
if(V[I[k+i]+h]<x) {
|
||||||
|
x=V[I[k+i]+h];
|
||||||
|
j=0;
|
||||||
|
};
|
||||||
|
if(V[I[k+i]+h]==x) {
|
||||||
|
tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
|
||||||
|
j++;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
|
||||||
|
if(j==1) I[k]=-1;
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
x=V[I[start+len/2]+h];
|
||||||
|
jj=0;kk=0;
|
||||||
|
for(i=start;i<start+len;i++) {
|
||||||
|
if(V[I[i]+h]<x) jj++;
|
||||||
|
if(V[I[i]+h]==x) kk++;
|
||||||
|
};
|
||||||
|
jj+=start;kk+=jj;
|
||||||
|
|
||||||
|
i=start;j=0;k=0;
|
||||||
|
while(i<jj) {
|
||||||
|
if(V[I[i]+h]<x) {
|
||||||
|
i++;
|
||||||
|
} else if(V[I[i]+h]==x) {
|
||||||
|
tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
|
||||||
|
j++;
|
||||||
|
} else {
|
||||||
|
tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
|
||||||
|
k++;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
while(jj+j<kk) {
|
||||||
|
if(V[I[jj+j]+h]==x) {
|
||||||
|
j++;
|
||||||
|
} else {
|
||||||
|
tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
|
||||||
|
k++;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if(jj>start) split(I,V,start,jj-start,h);
|
||||||
|
|
||||||
|
for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
|
||||||
|
if(jj==kk-1) I[jj]=-1;
|
||||||
|
|
||||||
|
if(start+len>kk) 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;i<oldsize;i++) buckets[old[i]]++;
|
||||||
|
for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
|
||||||
|
for(i=255;i>0;i--) buckets[i]=buckets[i-1];
|
||||||
|
buckets[0]=0;
|
||||||
|
|
||||||
|
for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
|
||||||
|
I[0]=oldsize;
|
||||||
|
for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
|
||||||
|
V[oldsize]=0;
|
||||||
|
for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
|
||||||
|
I[0]=-1;
|
||||||
|
|
||||||
|
for(h=1;I[0]!=-(oldsize+1);h+=h) {
|
||||||
|
len=0;
|
||||||
|
for(i=0;i<oldsize+1;) {
|
||||||
|
if(I[i]<0) {
|
||||||
|
len-=I[i];
|
||||||
|
i-=I[i];
|
||||||
|
} else {
|
||||||
|
if(len) I[i-len]=-len;
|
||||||
|
len=V[I[i]]+1-i;
|
||||||
|
split(I,V,i,len,h);
|
||||||
|
i+=len;
|
||||||
|
len=0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if(len) I[i-len]=-len;
|
||||||
|
};
|
||||||
|
|
||||||
|
for(i=0;i<oldsize+1;i++) I[V[i]]=i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t matchlen(const uint8_t *old,int64_t oldsize,const uint8_t *new,int64_t newsize)
|
||||||
|
{
|
||||||
|
int64_t i;
|
||||||
|
|
||||||
|
for(i=0;(i<oldsize)&&(i<newsize);i++)
|
||||||
|
if(old[i]!=new[i]) break;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t search(const int64_t *I,const uint8_t *old,int64_t oldsize,
|
||||||
|
const uint8_t *new,int64_t newsize,int64_t st,int64_t en,int64_t *pos)
|
||||||
|
{
|
||||||
|
int64_t x,y;
|
||||||
|
|
||||||
|
if(en-st<2) {
|
||||||
|
x=matchlen(old+I[st],oldsize-I[st],new,newsize);
|
||||||
|
y=matchlen(old+I[en],oldsize-I[en],new,newsize);
|
||||||
|
|
||||||
|
if(x>y) {
|
||||||
|
*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(scan<req.newsize) {
|
||||||
|
oldscore=0;
|
||||||
|
|
||||||
|
for(scsc=scan+=len;scan<req.newsize;scan++) {
|
||||||
|
len=search(I,req.old,req.oldsize,req.new+scan,req.newsize-scan,
|
||||||
|
0,req.oldsize,&pos);
|
||||||
|
|
||||||
|
for(;scsc<scan+len;scsc++)
|
||||||
|
if((scsc+lastoffset<req.oldsize) &&
|
||||||
|
(req.old[scsc+lastoffset] == req.new[scsc]))
|
||||||
|
oldscore++;
|
||||||
|
|
||||||
|
if(((len==oldscore) && (len!=0)) ||
|
||||||
|
(len>oldscore+8)) break;
|
||||||
|
|
||||||
|
if((scan+lastoffset<req.oldsize) &&
|
||||||
|
(req.old[scan+lastoffset] == req.new[scan]))
|
||||||
|
oldscore--;
|
||||||
|
};
|
||||||
|
|
||||||
|
if((len!=oldscore) || (scan==req.newsize)) {
|
||||||
|
s=0;Sf=0;lenf=0;
|
||||||
|
for(i=0;(lastscan+i<scan)&&(lastpos+i<req.oldsize);) {
|
||||||
|
if(req.old[lastpos+i]==req.new[lastscan+i]) s++;
|
||||||
|
i++;
|
||||||
|
if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
|
||||||
|
};
|
||||||
|
|
||||||
|
lenb=0;
|
||||||
|
if(scan<req.newsize) {
|
||||||
|
s=0;Sb=0;
|
||||||
|
for(i=1;(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;i<overlap;i++) {
|
||||||
|
if(req.new[lastscan+lenf-overlap+i]==
|
||||||
|
req.old[lastpos+lenf-overlap+i]) s++;
|
||||||
|
if(req.new[scan-lenb+i]==
|
||||||
|
req.old[pos-lenb+i]) s--;
|
||||||
|
if(s>Ss) { 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;i<lenf;i++)
|
||||||
|
buffer[i]=req.new[lastscan+i]-req.old[lastpos+i];
|
||||||
|
if (writedata(req.stream, buffer, lenf))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Write extra data */
|
||||||
|
for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
|
||||||
|
buffer[i]=req.new[lastscan+lenf+i];
|
||||||
|
if (writedata(req.stream, buffer, (scan-lenb)-(lastscan+lenf)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
lastscan=scan-lenb;
|
||||||
|
lastpos=pos-lenb;
|
||||||
|
lastoffset=pos-scan;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new, int64_t newsize, struct bsdiff_stream* stream)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
struct bsdiff_request req;
|
||||||
|
|
||||||
|
if((req.I=stream->malloc((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 <sys/types.h>
|
||||||
|
|
||||||
|
#include <bzlib.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -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 <stddef.h>
|
||||||
|
# include <stdint.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -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(newpos<newsize) {
|
||||||
|
/* Read control data */
|
||||||
|
for(i=0;i<=2;i++) {
|
||||||
|
if (stream->read(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<ctrl[0];i++)
|
||||||
|
if((oldpos+i>=0) && (oldpos+i<oldsize))
|
||||||
|
new[newpos+i]+=old[oldpos+i];
|
||||||
|
|
||||||
|
/* Adjust pointers */
|
||||||
|
newpos+=ctrl[0];
|
||||||
|
oldpos+=ctrl[0];
|
||||||
|
|
||||||
|
/* Sanity-check */
|
||||||
|
if(newpos+ctrl[1]>newsize)
|
||||||
|
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 <bzlib.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -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 <stdint.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
1
libglnx
1
libglnx
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 03138641298fd6799f46b16423871f959332bacf
|
|
||||||
|
|
@ -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
|
||||||
|
*~
|
||||||
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
<one line to give the library's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Copyright (C) 2015 Colin Walters <walters@verbum.org>
|
||||||
|
#
|
||||||
|
# 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)
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glnx-backport-autoptr.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Colin Walters <walters@verbum.org>
|
||||||
|
*
|
||||||
|
* 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 <gio/gio.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Colin Walters <walters@verbum.org>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Colin Walters <walters@verbum.org>
|
||||||
|
*
|
||||||
|
* 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 <gio/gio.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,289 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013,2014,2015 Colin Walters <walters@verbum.org>
|
||||||
|
*
|
||||||
|
* 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 <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013,2014,2015 Colin Walters <walters@verbum.org>
|
||||||
|
*
|
||||||
|
* 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 <glnx-backport-autocleanups.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,305 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
|
||||||
|
#include <glnx-dirfd.h>
|
||||||
|
#include <glnx-errors.h>
|
||||||
|
#include <glnx-local-alloc.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <glnx-backport-autocleanups.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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-backport-autocleanups.h>
|
||||||
|
#include <glnx-errors.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <glnx-backport-autocleanups.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,750 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/sendfile.h>
|
||||||
|
#include <errno.h>
|
||||||
|
/* See linux.git/fs/btrfs/ioctl.h */
|
||||||
|
#define BTRFS_IOCTL_MAGIC 0x94
|
||||||
|
#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
|
||||||
|
|
||||||
|
#include <glnx-fdio.h>
|
||||||
|
#include <glnx-dirfd.h>
|
||||||
|
#include <glnx-errors.h>
|
||||||
|
#include <glnx-xattrs.h>
|
||||||
|
#include <glnx-backport-autoptr.h>
|
||||||
|
#include <glnx-local-alloc.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <glnx-backport-autocleanups.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/xattr.h>
|
||||||
|
/* 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 <libgen.h>
|
||||||
|
#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
|
||||||
|
|
@ -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 <walters@verbum.org>
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <gio/gunixoutputstream.h>
|
||||||
|
|
||||||
|
#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 ();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>
|
||||||
|
*
|
||||||
|
* 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 <gio/gio.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/fsuid.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/capability.h>
|
||||||
|
#include <sched.h>
|
||||||
|
|
||||||
|
pid_t glnx_libcontainer_run_chroot_private (const char *dest,
|
||||||
|
const char *binary,
|
||||||
|
char **argv);
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012,2015 Colin Walters <walters@verbum.org>
|
||||||
|
*
|
||||||
|
* 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 <ulink
|
||||||
|
* url="http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html">
|
||||||
|
* 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 <emphasis>stack
|
||||||
|
* allocated</emphasis> 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.
|
||||||
|
*
|
||||||
|
* <example id="gs-lfree"><title>Calling g_free automatically</title>
|
||||||
|
* <programlisting>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* }
|
||||||
|
* </programlisting>
|
||||||
|
* </example>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,222 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <gio/gio.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -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 <walters@verbum.org>
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
@ -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 <walters@verbum.org>
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
***/
|
||||||
|
|
||||||
|
#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 }
|
||||||
|
|
@ -0,0 +1,287 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
|
||||||
|
#include <glnx-shutil.h>
|
||||||
|
#include <glnx-errors.h>
|
||||||
|
#include <glnx-local-alloc.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <glnx-dirfd.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,526 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <glnx-xattrs.h>
|
||||||
|
#include <glnx-errors.h>
|
||||||
|
#include <glnx-local-alloc.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <glnx-backport-autocleanups.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/xattr.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||||
|
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
||||||
|
xmlns:gnome="http://api.gnome.org/doap-extensions#"
|
||||||
|
xmlns="http://usefulinc.com/ns/doap#">
|
||||||
|
|
||||||
|
<name>libglnx</name>
|
||||||
|
<shortname>libglnx</shortname>
|
||||||
|
|
||||||
|
<shortdesc xml:lang="en">"Copylib" for system service modules using GLib with Linux</shortdesc>
|
||||||
|
|
||||||
|
<description xml:lang="en">This module is intended for use by
|
||||||
|
infrastructure code using GLib that is also Linux specific, such as
|
||||||
|
ostree, NetworkManager, and others.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<license rdf:resource="http://usefulinc.com/doap/licenses/lgpl" />
|
||||||
|
<mailing-list rdf:resource="mailto:desktop-devel-list@gnome.org" />
|
||||||
|
|
||||||
|
<programming-language>C</programming-language>
|
||||||
|
|
||||||
|
<maintainer>
|
||||||
|
<foaf:Person>
|
||||||
|
<foaf:name>Colin Walters</foaf:name>
|
||||||
|
<foaf:mbox rdf:resource="mailto:walters@verbum.org"/>
|
||||||
|
<gnome:userid>walters</gnome:userid>
|
||||||
|
</foaf:Person>
|
||||||
|
</maintainer>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012,2013,2015 Colin Walters <walters@verbum.org>.
|
||||||
|
*
|
||||||
|
* 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 <gio/gio.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#include <glnx-local-alloc.h>
|
||||||
|
#include <glnx-backport-autocleanups.h>
|
||||||
|
#include <glnx-backports.h>
|
||||||
|
#include <glnx-lockfile.h>
|
||||||
|
#include <glnx-errors.h>
|
||||||
|
#include <glnx-dirfd.h>
|
||||||
|
#include <glnx-shutil.h>
|
||||||
|
#include <glnx-xattrs.h>
|
||||||
|
#include <glnx-libcontainer.h>
|
||||||
|
#include <glnx-console.h>
|
||||||
|
#include <glnx-fdio.h>
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
Loading…
Reference in New Issue