diff --git a/xdg-app-utils.c b/xdg-app-utils.c index deb87e71..91c246c6 100644 --- a/xdg-app-utils.c +++ b/xdg-app-utils.c @@ -100,84 +100,77 @@ overlay_symlink_tree_dir (int source_parent_fd, { gboolean ret = FALSE; int res; - int source_dfd = -1; - int destination_dfd = -1; - DIR *srcd = NULL; + gs_dirfd_iterator_cleanup GSDirFdIterator source_iter; + gs_fd_close int destination_dfd = -1; 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_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 (!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) - { - gs_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) - { - gs_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) + if (errno != EEXIST) { gs_set_error_from_errno (error, errno); goto out; } + } - if (S_ISDIR (child_stbuf.st_mode)) + if (!gs_file_open_dir_fd_at (destination_parent_fd, destination_name, + &destination_dfd, + cancellable, error)) + goto out; + + while (TRUE) + { + gboolean is_dir = FALSE; + + if (!gs_dirfd_iterator_next_dent (&source_iter, &dent, cancellable, error)) + goto out; + + if (dent == NULL) + break; + + if (dent->d_type == DT_DIR) + is_dir = TRUE; + else if (dent->d_type == DT_UNKNOWN) { - gs_free gchar *target = g_build_filename ("..", source_symlink_prefix, name, NULL); - if (!overlay_symlink_tree_dir (source_dfd, name, target, destination_dfd, name, + struct stat stbuf; + if (fstatat (source_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) + { + int errsv = errno; + if (errsv == ENOENT) + continue; + else + { + gs_set_error_from_errno (error, errsv); + goto out; + } + } + is_dir = S_ISDIR (stbuf.st_mode); + } + + if (is_dir) + { + gs_free gchar *target = g_build_filename ("..", source_symlink_prefix, dent->d_name, NULL); + if (!overlay_symlink_tree_dir (source_iter.fd, dent->d_name, target, destination_dfd, dent->d_name, cancellable, error)) goto out; } else { - gs_free gchar *target = g_build_filename (source_symlink_prefix, name, NULL); + gs_free gchar *target = g_build_filename (source_symlink_prefix, dent->d_name, NULL); - if (unlinkat (destination_dfd, name, 0) != 0 && errno != ENOENT) + if (unlinkat (destination_dfd, dent->d_name, 0) != 0 && errno != ENOENT) { gs_set_error_from_errno (error, errno); goto out; } - if (symlinkat (target, destination_dfd, name) != 0) + if (symlinkat (target, destination_dfd, dent->d_name) != 0) { gs_set_error_from_errno (error, errno); goto out; @@ -187,12 +180,6 @@ overlay_symlink_tree_dir (int source_parent_fd, 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; } @@ -205,23 +192,14 @@ xdg_app_overlay_symlink_tree (GFile *source, GError **error) { gboolean ret = FALSE; - int source_dfd = -1; - int destination_dfd = -1; - if (!gs_file_open_dir_fd (source, &source_dfd, cancellable, error)) + if (!gs_file_ensure_directory (destination, TRUE, 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, + if (!overlay_symlink_tree_dir (AT_FDCWD, gs_file_get_path_cached (source), symlink_prefix, - destination_dfd, NULL, + AT_FDCWD, gs_file_get_path_cached (destination), cancellable, error)) goto out;