static gboolean export_dir (int source_parent_fd, const char *source_name, const char *source_relpath, int destination_parent_fd, const char *destination_name, const char *required_prefix, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int res; g_auto(GLnxDirFdIterator) source_iter = {0}; glnx_fd_close int destination_dfd = -1; struct dirent *dent; if (!glnx_dirfd_iterator_init_at (source_parent_fd, source_name, FALSE, &source_iter, error)) goto out; do res = mkdirat (destination_parent_fd, destination_name, 0777); while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1) { if (errno != EEXIST) { glnx_set_error_from_errno (error); goto out; } } if (!gs_file_open_dir_fd_at (destination_parent_fd, destination_name, &destination_dfd, cancellable, error)) goto out; while (TRUE) { struct stat stbuf; g_autofree char *source_printable = NULL; if (!glnx_dirfd_iterator_next_dent (&source_iter, &dent, cancellable, error)) goto out; if (dent == NULL) break; if (fstatat (source_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOENT) continue; else { glnx_set_error_from_errno (error); goto out; } } if (S_ISDIR (stbuf.st_mode)) { g_autofree gchar *child_relpath = g_build_filename(source_relpath, dent->d_name, NULL); if (!export_dir (source_iter.fd, dent->d_name, child_relpath, destination_dfd, dent->d_name, required_prefix, cancellable, error)) goto out; } else if (S_ISREG (stbuf.st_mode)) { source_printable = g_build_filename (source_relpath, dent->d_name, NULL); if (!xdg_app_has_name_prefix (dent->d_name, required_prefix)) { g_print ("Not exporting %s, wrong prefix\n", source_printable); continue; } g_print ("Exporting %s\n", source_printable); if (!glnx_file_copy_at (source_iter.fd, dent->d_name, &stbuf, destination_dfd, dent->d_name, GLNX_FILE_COPY_NOXATTRS, cancellable, error)) goto out; } else { source_printable = g_build_filename (source_relpath, dent->d_name, NULL); g_print ("Not exporting non-regular file %s\n", source_printable); } } ret = TRUE; out: return ret; }
static gboolean export_dir (int source_parent_fd, const char *source_name, const char *source_relpath, int destination_parent_fd, const char *destination_name, const char *required_prefix, GCancellable *cancellable, GError **error) { int res; g_auto(GLnxDirFdIterator) source_iter = {0}; glnx_fd_close int destination_dfd = -1; struct dirent *dent; if (!glnx_dirfd_iterator_init_at (source_parent_fd, source_name, FALSE, &source_iter, error)) return FALSE; do res = mkdirat (destination_parent_fd, destination_name, 0755); while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1) { if (errno != EEXIST) { glnx_set_error_from_errno (error); return FALSE; } } if (!gs_file_open_dir_fd_at (destination_parent_fd, destination_name, &destination_dfd, cancellable, error)) return FALSE; while (TRUE) { struct stat stbuf; g_autofree char *source_printable = NULL; if (!glnx_dirfd_iterator_next_dent (&source_iter, &dent, cancellable, error)) return FALSE; if (dent == NULL) break; if (fstatat (source_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOENT) continue; else { glnx_set_error_from_errno (error); return FALSE; } } /* Don't export any hidden files or backups */ if (g_str_has_prefix (dent->d_name, ".") || g_str_has_suffix (dent->d_name, "~")) continue; if (S_ISDIR (stbuf.st_mode)) { g_autofree gchar *child_relpath = g_build_filename(source_relpath, dent->d_name, NULL); if (!export_dir (source_iter.fd, dent->d_name, child_relpath, destination_dfd, dent->d_name, required_prefix, cancellable, error)) return FALSE; } else if (S_ISREG (stbuf.st_mode)) { source_printable = g_build_filename (source_relpath, dent->d_name, NULL); if (!xdg_app_has_name_prefix (dent->d_name, required_prefix)) { g_print ("Not exporting %s, wrong prefix\n", source_printable); continue; } g_print ("Exporting %s\n", source_printable); if (!glnx_file_copy_at (source_iter.fd, dent->d_name, &stbuf, destination_dfd, dent->d_name, GLNX_FILE_COPY_NOXATTRS, cancellable, error)) return FALSE; } else { source_printable = g_build_filename (source_relpath, dent->d_name, NULL); g_print ("Not exporting non-regular file %s\n", source_printable); } } /* Try to remove the directory, as we don't want to export empty directories. * However, don't fail if the unlink fails due to the directory not being empty */ do res = unlinkat (destination_parent_fd, destination_name, AT_REMOVEDIR); while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1) { if (errno != ENOTEMPTY) { glnx_set_error_from_errno (error); return FALSE; } } return TRUE; }