+ size_t cdlen = strlen (tmpdir) + strlen (checksum) + 10;
+ char cachedir[cdlen];
+ snprintf (cachedir, cdlen, "%s/guestfs.%s", tmpdir, checksum);
+
+ /* Make the temporary directory world readable */
+ if (chmod (tmpcd, 0755) == -1) {
+ error (g, "chmod %s: %m", tmpcd);
+ }
+
+ /* Try to rename the temporary directory to its non-temporary name */
+ if (rename (tmpcd, cachedir) == -1) {
+ /* If the cache directory now exists, we may have been racing with another
+ * libguestfs process. Check the new directory and use it if it's valid. */
+ if (errno == ENOTEMPTY || errno == EEXIST) {
+ /* Appliance cache consists of 2 files and a symlink in the cache
+ * directory. Delete them first. */
+ DIR *dir = opendir (tmpcd);
+ if (dir == NULL) {
+ error (g, "opendir %s: %m", tmpcd);
+ return -1;
+ }
+
+ int fd = dirfd (dir);
+ if (fd == -1) {
+ error (g, "dirfd: %m");
+ closedir (dir);
+ return -1;
+ }
+
+ struct dirent *dirent;
+ for (;;) {
+ errno = 0;
+ dirent = readdir (dir);
+
+ if (dirent == NULL) {
+ break;
+ }
+
+ /* Check that dirent is a file so we don't try to delete . and .. */
+ struct stat st;
+ if (fstatat (fd, dirent->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1) {
+ error (g, "fstatat %s: %m", dirent->d_name);
+ return -1;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ if (unlinkat (fd, dirent->d_name, 0) == -1) {
+ error (g, "unlinkat %s: %m", dirent->d_name);
+ closedir (dir);
+ return -1;
+ }
+ }
+ }
+
+ if (errno != 0) {
+ error (g, "readdir %s: %m", tmpcd);
+ closedir (dir);
+ return -1;
+ }
+
+ closedir (dir);
+
+ /* Delete the temporary cache directory itself. */
+ if (rmdir (tmpcd) == -1) {
+ error (g, "rmdir %s: %m", tmpcd);
+ return -1;
+ }
+
+ /* Check the new cache directory, and return it if valid */
+ return check_for_cached_appliance (g, supermin_path, checksum,
+ kernel, initrd, appliance);
+ }
+
+ else {
+ error (g, _("error renaming temporary cache directory: %m"));
+ return -1;
+ }
+ }
+