mirror of
https://github.com/projectatomic/bubblewrap.git
synced 2026-02-06 09:46:09 +01:00
If they already exist as a regular file then we don't need to create it, which is good because doing so would break if the filesystem is readonly. I.e. that returns EROFS instead of EEXISTS in that case.
564 lines
9.4 KiB
C
564 lines
9.4 KiB
C
/* bubblewrap
|
|
* Copyright (C) 2016 Alexander Larsson
|
|
*
|
|
* 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 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include "utils.h"
|
|
#include <sys/syscall.h>
|
|
|
|
void
|
|
die_with_error (const char *format, ...)
|
|
{
|
|
va_list args;
|
|
int errsv;
|
|
|
|
errsv = errno;
|
|
|
|
va_start (args, format);
|
|
vfprintf (stderr, format, args);
|
|
va_end (args);
|
|
|
|
fprintf (stderr, ": %s\n", strerror (errsv));
|
|
|
|
exit (1);
|
|
}
|
|
|
|
void
|
|
die (const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start (args, format);
|
|
vfprintf (stderr, format, args);
|
|
va_end (args);
|
|
|
|
fprintf (stderr, "\n");
|
|
|
|
exit (1);
|
|
}
|
|
|
|
void
|
|
die_oom (void)
|
|
{
|
|
die ("Out of memory");
|
|
}
|
|
|
|
void *
|
|
xmalloc (size_t size)
|
|
{
|
|
void *res = malloc (size);
|
|
if (res == NULL)
|
|
die_oom ();
|
|
return res;
|
|
}
|
|
|
|
void *
|
|
xcalloc (size_t size)
|
|
{
|
|
void *res = calloc (1, size);
|
|
if (res == NULL)
|
|
die_oom ();
|
|
return res;
|
|
}
|
|
|
|
void *
|
|
xrealloc (void *ptr, size_t size)
|
|
{
|
|
void *res = realloc (ptr, size);
|
|
if (size != 0 && res == NULL)
|
|
die_oom ();
|
|
return res;
|
|
}
|
|
|
|
char *
|
|
xstrdup (const char *str)
|
|
{
|
|
char *res;
|
|
|
|
assert (str != NULL);
|
|
|
|
res = strdup (str);
|
|
if (res == NULL)
|
|
die_oom ();
|
|
|
|
return res;
|
|
}
|
|
|
|
void
|
|
strfreev (char **str_array)
|
|
{
|
|
if (str_array)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; str_array[i] != NULL; i++)
|
|
free (str_array[i]);
|
|
|
|
free (str_array);
|
|
}
|
|
}
|
|
|
|
bool
|
|
has_prefix (const char *str,
|
|
const char *prefix)
|
|
{
|
|
return strncmp (str, prefix, strlen (prefix)) == 0;
|
|
}
|
|
|
|
void
|
|
xsetenv (const char *name, const char *value, int overwrite)
|
|
{
|
|
if (setenv (name, value, overwrite))
|
|
die ("setenv failed");
|
|
}
|
|
|
|
void
|
|
xunsetenv (const char *name)
|
|
{
|
|
if (unsetenv(name))
|
|
die ("unsetenv failed");
|
|
}
|
|
|
|
char *
|
|
strconcat (const char *s1,
|
|
const char *s2)
|
|
{
|
|
size_t len = 0;
|
|
char *res;
|
|
|
|
if (s1)
|
|
len += strlen (s1);
|
|
if (s2)
|
|
len += strlen (s2);
|
|
|
|
res = xmalloc (len + 1);
|
|
*res = 0;
|
|
if (s1)
|
|
strcat (res, s1);
|
|
if (s2)
|
|
strcat (res, s2);
|
|
|
|
return res;
|
|
}
|
|
|
|
char *
|
|
strconcat3 (const char *s1,
|
|
const char *s2,
|
|
const char *s3)
|
|
{
|
|
size_t len = 0;
|
|
char *res;
|
|
|
|
if (s1)
|
|
len += strlen (s1);
|
|
if (s2)
|
|
len += strlen (s2);
|
|
if (s3)
|
|
len += strlen (s3);
|
|
|
|
res = xmalloc (len + 1);
|
|
*res = 0;
|
|
if (s1)
|
|
strcat (res, s1);
|
|
if (s2)
|
|
strcat (res, s2);
|
|
if (s3)
|
|
strcat (res, s3);
|
|
|
|
return res;
|
|
}
|
|
|
|
char*
|
|
strdup_printf (const char *format,
|
|
...)
|
|
{
|
|
char *buffer = NULL;
|
|
va_list args;
|
|
|
|
va_start (args, format);
|
|
vasprintf (&buffer, format, args);
|
|
va_end (args);
|
|
|
|
if (buffer == NULL)
|
|
die_oom ();
|
|
|
|
return buffer;
|
|
}
|
|
|
|
int
|
|
fdwalk (int proc_fd, int (*cb)(void *data, int fd), void *data)
|
|
{
|
|
int open_max;
|
|
int fd;
|
|
int dfd;
|
|
int res = 0;
|
|
DIR *d;
|
|
|
|
dfd = openat (proc_fd, "self/fd", O_DIRECTORY | O_PATH);
|
|
if (dfd == -1)
|
|
return res;
|
|
|
|
if ((d = fdopendir (dfd)))
|
|
{
|
|
struct dirent *de;
|
|
|
|
while ((de = readdir (d)))
|
|
{
|
|
long l;
|
|
char *e = NULL;
|
|
|
|
if (de->d_name[0] == '.')
|
|
continue;
|
|
|
|
errno = 0;
|
|
l = strtol (de->d_name, &e, 10);
|
|
if (errno != 0 || !e || *e)
|
|
continue;
|
|
|
|
fd = (int) l;
|
|
|
|
if ((long) fd != l)
|
|
continue;
|
|
|
|
if (fd == dirfd (d))
|
|
continue;
|
|
|
|
if ((res = cb (data, fd)) != 0)
|
|
break;
|
|
}
|
|
|
|
closedir (d);
|
|
return res;
|
|
}
|
|
|
|
open_max = sysconf (_SC_OPEN_MAX);
|
|
|
|
for (fd = 0; fd < open_max; fd++)
|
|
if ((res = cb (data, fd)) != 0)
|
|
break;
|
|
|
|
return res;
|
|
}
|
|
|
|
/* Sets errno on error (!= 0), ENOSPC on short write */
|
|
int
|
|
write_to_fd (int fd,
|
|
const char *content,
|
|
ssize_t len)
|
|
{
|
|
ssize_t res;
|
|
|
|
while (len > 0)
|
|
{
|
|
res = write (fd, content, len);
|
|
if (res < 0 && errno == EINTR)
|
|
continue;
|
|
if (res <= 0)
|
|
{
|
|
if (res == 0) /* Unexpected short write, should not happen when writing to a file */
|
|
errno = ENOSPC;
|
|
return -1;
|
|
}
|
|
len -= res;
|
|
content += res;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Sets errno on error (!= 0), ENOSPC on short write */
|
|
int
|
|
write_file_at (int dirfd,
|
|
const char *path,
|
|
const char *content)
|
|
{
|
|
int fd;
|
|
bool res;
|
|
int errsv;
|
|
|
|
fd = openat (dirfd, path, O_RDWR | O_CLOEXEC, 0);
|
|
if (fd == -1)
|
|
return -1;
|
|
|
|
res = 0;
|
|
if (content)
|
|
res = write_to_fd (fd, content, strlen (content));
|
|
|
|
errsv = errno;
|
|
close (fd);
|
|
errno = errsv;
|
|
|
|
return res;
|
|
}
|
|
|
|
/* Sets errno on error (!= 0), ENOSPC on short write */
|
|
int
|
|
create_file (const char *path,
|
|
mode_t mode,
|
|
const char *content)
|
|
{
|
|
int fd;
|
|
int res;
|
|
int errsv;
|
|
|
|
fd = creat (path, mode);
|
|
if (fd == -1)
|
|
return -1;
|
|
|
|
res = 0;
|
|
if (content)
|
|
res = write_to_fd (fd, content, strlen (content));
|
|
|
|
errsv = errno;
|
|
close (fd);
|
|
errno = errsv;
|
|
|
|
return res;
|
|
}
|
|
|
|
int
|
|
ensure_file (const char *path,
|
|
mode_t mode)
|
|
{
|
|
struct stat buf;
|
|
|
|
/* We check this ahead of time, otherwise
|
|
the create file will fail in the read-only
|
|
case with EROFD instead of EEXIST */
|
|
if (stat (path, &buf) == 0 &&
|
|
S_ISREG (buf.st_mode))
|
|
return 0;
|
|
|
|
if (create_file (path, mode, NULL) != 0 && errno != EEXIST)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define BUFSIZE 8192
|
|
/* Sets errno on error (!= 0), ENOSPC on short write */
|
|
int
|
|
copy_file_data (int sfd,
|
|
int dfd)
|
|
{
|
|
char buffer[BUFSIZE];
|
|
ssize_t bytes_read;
|
|
|
|
while (TRUE)
|
|
{
|
|
bytes_read = read (sfd, buffer, BUFSIZE);
|
|
if (bytes_read == -1)
|
|
{
|
|
if (errno == EINTR)
|
|
continue;
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (bytes_read == 0)
|
|
break;
|
|
|
|
if (write_to_fd (dfd, buffer, bytes_read) != 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Sets errno on error (!= 0), ENOSPC on short write */
|
|
int
|
|
copy_file (const char *src_path,
|
|
const char *dst_path,
|
|
mode_t mode)
|
|
{
|
|
int sfd;
|
|
int dfd;
|
|
int res;
|
|
int errsv;
|
|
|
|
sfd = open (src_path, O_CLOEXEC | O_RDONLY);
|
|
if (sfd == -1)
|
|
return -1;
|
|
|
|
dfd = creat (dst_path, mode);
|
|
if (dfd == -1)
|
|
{
|
|
errsv = errno;
|
|
close (sfd);
|
|
errno = errsv;
|
|
return -1;
|
|
}
|
|
|
|
res = copy_file_data (sfd, dfd);
|
|
|
|
errsv = errno;
|
|
close (sfd);
|
|
close (dfd);
|
|
errno = errsv;
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
/* Sets errno on error (== NULL) */
|
|
char *
|
|
load_file_at (int dirfd,
|
|
const char *path)
|
|
{
|
|
int fd;
|
|
cleanup_free char *data = NULL;
|
|
ssize_t data_read;
|
|
ssize_t data_len;
|
|
ssize_t res;
|
|
int errsv;
|
|
|
|
fd = openat (dirfd, path, O_CLOEXEC | O_RDONLY);
|
|
if (fd == -1)
|
|
return NULL;
|
|
|
|
data_read = 0;
|
|
data_len = 4080;
|
|
data = xmalloc (data_len);
|
|
|
|
do
|
|
{
|
|
if (data_len >= data_read + 1)
|
|
{
|
|
data_len *= 2;
|
|
data = xrealloc (data, data_len);
|
|
}
|
|
|
|
do
|
|
res = read (fd, data + data_read, data_len - data_read - 1);
|
|
while (res < 0 && errno == EINTR);
|
|
|
|
if (res < 0)
|
|
{
|
|
errsv = errno;
|
|
close (fd);
|
|
errno = errsv;
|
|
return NULL;
|
|
}
|
|
|
|
data_read += res;
|
|
}
|
|
while (res > 0);
|
|
|
|
data[data_read] = 0;
|
|
|
|
errsv = errno;
|
|
close (fd);
|
|
errno = errsv;
|
|
|
|
return steal_pointer (&data);
|
|
}
|
|
|
|
/* Sets errno on error (< 0) */
|
|
int
|
|
get_file_mode (const char *pathname)
|
|
{
|
|
struct stat buf;
|
|
|
|
if (stat (pathname, &buf) != 0)
|
|
return -1;
|
|
|
|
return buf.st_mode & S_IFMT;
|
|
}
|
|
|
|
/* Sets errno on error (!= 0) */
|
|
int
|
|
mkdir_with_parents (const char *pathname,
|
|
int mode,
|
|
bool create_last)
|
|
{
|
|
cleanup_free char *fn = NULL;
|
|
char *p;
|
|
struct stat buf;
|
|
|
|
if (pathname == NULL || *pathname == '\0')
|
|
{
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
fn = xstrdup (pathname);
|
|
|
|
p = fn;
|
|
while (*p == '/')
|
|
p++;
|
|
|
|
do
|
|
{
|
|
while (*p && *p != '/')
|
|
p++;
|
|
|
|
if (!*p)
|
|
p = NULL;
|
|
else
|
|
*p = '\0';
|
|
|
|
if (!create_last && p == NULL)
|
|
break;
|
|
|
|
if (stat (fn, &buf) != 0)
|
|
{
|
|
if (mkdir (fn, mode) == -1 && errno != EEXIST)
|
|
return -1;
|
|
}
|
|
else if (!S_ISDIR (buf.st_mode))
|
|
{
|
|
errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
|
|
if (p)
|
|
{
|
|
*p++ = '/';
|
|
while (*p && *p == '/')
|
|
p++;
|
|
}
|
|
}
|
|
while (p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
raw_clone (unsigned long flags,
|
|
void *child_stack)
|
|
{
|
|
#if defined(__s390__) || defined(__CRIS__)
|
|
/* On s390 and cris the order of the first and second arguments
|
|
* of the raw clone() system call is reversed. */
|
|
return (int) syscall(__NR_clone, child_stack, flags);
|
|
#else
|
|
return (int) syscall(__NR_clone, flags, child_stack);
|
|
#endif
|
|
}
|
|
|
|
int
|
|
pivot_root (const char * new_root, const char * put_old)
|
|
{
|
|
#ifdef __NR_pivot_root
|
|
return syscall(__NR_pivot_root, new_root, put_old);
|
|
#else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
#endif
|
|
}
|