Add xdg_app_overlay_symlink_tree helper

This will be used during exports
tingping/wmclass
Alexander Larsson 2015-01-09 10:30:43 +01:00
parent 721abb5375
commit e2c8d59316
2 changed files with 165 additions and 0 deletions

View File

@ -7,6 +7,11 @@
#include "libgsystem.h"
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
const char *
@ -83,3 +88,157 @@ xdg_app_find_deploy_dir_for_ref (const char *ref,
return deploy;
}
static void
set_error_from_errno (GError **error,
gint saved_errno)
{
g_set_error_literal (error,
G_IO_ERROR,
g_io_error_from_errno (saved_errno),
g_strerror (saved_errno));
errno = saved_errno;
}
static gboolean
overlay_symlink_tree_dir (int source_parent_fd,
const char *source_name,
const char *source_symlink_prefix,
int destination_parent_fd,
const char *destination_name,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
int res;
int source_dfd = -1;
int destination_dfd = -1;
DIR *srcd = NULL;
struct dirent *dent;
if (source_name == NULL)
source_dfd = source_parent_fd; /* We take ownership of the passed fd and close it */
else
{
if (!gs_file_open_dir_fd_at (source_parent_fd, source_name,
&source_dfd,
cancellable, error))
goto out;
}
if (destination_name == NULL)
destination_dfd = destination_parent_fd; /* We take ownership of the passed fd and close it */
else
{
do
res = mkdirat (destination_parent_fd, destination_name, 0777);
while (G_UNLIKELY (res == -1 && errno == EINTR));
if (res == -1)
{
if (errno != EEXIST)
{
set_error_from_errno (error, errno);
goto out;
}
}
if (!gs_file_open_dir_fd_at (destination_parent_fd, destination_name,
&destination_dfd,
cancellable, error))
goto out;
}
srcd = fdopendir (source_dfd);
if (!srcd)
{
set_error_from_errno (error, errno);
goto out;
}
while ((dent = readdir (srcd)) != NULL)
{
const char *name = dent->d_name;
struct stat child_stbuf;
if (strcmp (name, ".") == 0 ||
strcmp (name, "..") == 0)
continue;
if (fstatat (source_dfd, name, &child_stbuf,
AT_SYMLINK_NOFOLLOW) != 0)
{
set_error_from_errno (error, errno);
goto out;
}
if (S_ISDIR (child_stbuf.st_mode))
{
gs_free gchar *target = g_build_filename ("..", source_symlink_prefix, name, NULL);
if (!overlay_symlink_tree_dir (source_dfd, name, target, destination_dfd, name,
cancellable, error))
goto out;
}
else
{
gs_free gchar *target = g_build_filename (source_symlink_prefix, name, NULL);
if (unlinkat (destination_dfd, name, 0) != 0 && errno != ENOENT)
{
set_error_from_errno (error, errno);
goto out;
}
if (symlinkat (target, destination_dfd, name) != 0)
{
set_error_from_errno (error, errno);
goto out;
}
}
}
ret = TRUE;
out:
if (srcd != NULL)
closedir (srcd); /* This closes source_dfd */
else if (source_dfd != -1)
close (source_dfd);
if (destination_dfd != -1)
close (destination_dfd);
return ret;
}
gboolean
xdg_app_overlay_symlink_tree (GFile *source,
GFile *destination,
const char *symlink_prefix,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
int source_dfd = -1;
int destination_dfd = -1;
if (!gs_file_open_dir_fd (source, &source_dfd, cancellable, error))
goto out;
if (!gs_file_ensure_directory (destination, TRUE, cancellable, error) ||
!gs_file_open_dir_fd (destination, &destination_dfd, cancellable, error))
{
close (source_dfd);
goto out;
}
/* The fds are closed by this call */
if (!overlay_symlink_tree_dir (source_dfd, NULL,
symlink_prefix,
destination_dfd, NULL,
cancellable, error))
goto out;
ret = TRUE;
out:
return ret;
}

View File

@ -18,4 +18,10 @@ GFile * xdg_app_find_deploy_dir_for_ref (const char *ref,
GCancellable *cancellable,
GError **error);
gboolean xdg_app_overlay_symlink_tree (GFile *source,
GFile *destination,
const char *symlink_prefix,
GCancellable *cancellable,
GError **error);
#endif /* __XDG_APP_UTILS_H__ */