/** * Create the directory @p path, and register it for deletion when this * compilation finished. If it already exists as a directory * we succeed, but we don't register the directory for deletion. **/ int dcc_mk_tmpdir(const char *path) { struct stat buf; int ret; if (stat(path, &buf) == -1) { if (mkdir(path, 0777) == -1) { return EXIT_IO_ERROR; } if ((ret = dcc_add_cleanup(path))) { /* bailing out */ rmdir(path); return ret; } } else { /* we could stat the file successfully; if it's a directory, * all is well, but we should not it delete later, since we did * not make it. */ if (S_ISDIR(buf.st_mode)) { return 0; } else { rs_log_error("mkdir '%s' failed: %s", path, strerror(errno)); return EXIT_IO_ERROR; } } return 0; }
/* This function returns a directory-name, it does not end in a slash. */ int dcc_get_tmp_top(const char **p_ret) { #ifdef __CYGWIN32__ int ret; char *s = malloc(MAXPATHLEN+1); int f,ln; GetTempPath(MAXPATHLEN+1,s); /* Convert slashes */ for (f = 0, ln = strlen(s); f != ln; f++) if (s[f]=='\\') s[f]='/'; /* Delete trailing slashes -- but leave one slash if s is all slashes */ for (f = strlen(s)-1; f > 0 && s[f] == '/'; f--) s[f]='\0'; if ((ret = dcc_add_cleanup(s))) { free(s); return ret; } *p_ret = s; return 0; #else const char *d; d = getenv("TMPDIR"); if (!d || d[0] == '\0') { *p_ret = "/tmp"; return 0; } else { *p_ret = d; return 0; } #endif }
/* This function returns a directory-name, it does not end in a slash. */ int dcc_get_tmp_top(const char **p_ret) { #ifdef __CYGWIN32__ int ret; char *s = malloc(MAXPATHLEN+1); int f,ln; GetTempPath(MAXPATHLEN+1,s); /* Convert slashes */ for (f = 0, ln = strlen(s); f != ln; f++) if (s[f]=='\\') s[f]='/'; /* Delete trailing slashes -- but leave one slash if s is all slashes */ for (f = strlen(s)-1; f > 0 && s[f] == '/'; f--) s[f]='\0'; if ((ret = dcc_add_cleanup(s))) { free(s); return ret; } *p_ret = s; return 0; #else const char *d; char *s; int l; static const char *tmp_dir = NULL; if (!tmp_dir) { d = getenv("TMPDIR"); /* Make sure it doesn't end in a slash */ if (d && d[0] != '\0') { l = strlen(d) - 1; if (d[l] == '/') { s = strdup(d); if (s) { tmp_dir = s; /* Loop to handle multiple trailing slashes */ while (l && s[l] == '/') { s[l--] = 0; } } } else { tmp_dir = d; } } if (!tmp_dir) { /* Env var was not set, or strdup failed */ tmp_dir = "/tmp"; } } *p_ret = tmp_dir; return 0; #endif }
/* This function creates a temporary directory, to be used for * all (input) files during one compilation. * The name of the directory is stored in @p tempdir, which is * malloc'ed here. The caller is responsible for freeing it. * The format of the directory name is <TMPTOP>/distccd_<randomnumber> * Returns the new temp dir in tempdir. */ int dcc_get_new_tmpdir(char **tempdir) { int ret; const char *tmp_top; char *s; if ((ret = dcc_get_tmp_top(&tmp_top))) { return ret; } if (asprintf(&s, "%s/distccd_XXXXXX", tmp_top) == -1) return EXIT_OUT_OF_MEMORY; if ((*tempdir = mkdtemp(s)) == 0) { return EXIT_IO_ERROR; } if ((ret = dcc_add_cleanup(s))) { /* bailing out */ rmdir(s); return ret; } return 0; }
int dcc_r_many_files(int in_fd, const char *dirname, enum dcc_compress compr) { int ret = 0; unsigned int n_files; unsigned int i; char *name = 0; char *link_target = 0; char token[5]; if ((ret = dcc_r_token_int(in_fd, "NFIL", &n_files))) return ret; for (i = 0; i < n_files; ++i) { /* like dcc_r_argv */ unsigned int link_or_file_len; if ((ret = dcc_r_token_string(in_fd, "NAME", &name))) goto out_cleanup; /* FIXME: verify that name starts with '/' and doesn't contain '..'. */ if ((ret = prepend_dir_to_name(dirname, &name))) goto out_cleanup; if ((ret = dcc_r_sometoken_int(in_fd, token, &link_or_file_len))) goto out_cleanup; /* Must prepend the dirname for the file name, a link's target name. */ if (strncmp(token, "LINK", 4) == 0) { if ((ret = dcc_r_str_alloc(in_fd, link_or_file_len, &link_target))){ goto out_cleanup; } /* FIXME: verify that link_target doesn't contain '..'. * But the include server uses '..' to reference system * directories (see _MakeLinkFromMirrorToRealLocation * in include_server/compiler_defaults.py), so we'll need to * modify that first. */ if (link_target[0] == '/') { if ((ret = prepend_dir_to_name(dirname, &link_target))) { goto out_cleanup; } } if ((ret = dcc_mk_tmp_ancestor_dirs(name))) { goto out_cleanup; } if (symlink(link_target, name) != 0) { rs_log_error("failed to create path for %s: %s", name, strerror(errno)); ret = 1; goto out_cleanup; } if ((ret = dcc_add_cleanup(name))) { /* bailing out */ unlink(name); goto out_cleanup; } } else if (strncmp(token, "FILE", 4) == 0) { if ((ret = dcc_r_file(in_fd, name, link_or_file_len, compr))) { goto out_cleanup; } if ((ret = dcc_add_cleanup(name))) { /* bailing out */ unlink(name); goto out_cleanup; } } else { char buf[4 + sizeof(link_or_file_len)]; /* unexpected token */ rs_log_error("protocol derailment: expected token FILE or LINK"); /* We should explain what happened here, but we have already read * a few more bytes. */ strncpy(buf, token, 4); /* TODO(manos): this is probably not kosher */ memcpy(&buf[4], &link_or_file_len, sizeof(link_or_file_len)); dcc_explain_mismatch(buf, 12, in_fd); ret = EXIT_PROTOCOL_ERROR; goto out_cleanup; } out_cleanup: free(name); name = NULL; free(link_target); link_target = NULL; if (ret) break; } return ret; }
/** * Create a file inside the temporary directory and register it for * later cleanup, and return its name. * * The file will be reopened later, possibly in a child. But we know * that it exists with appropriately tight permissions. **/ int dcc_make_tmpnam(const char *prefix, const char *suffix, char **name_ret) { char *s = NULL; const char *tempdir; int ret; unsigned long random_bits; int fd; if ((ret = dcc_get_tmp_top(&tempdir))) return ret; if (access(tempdir, W_OK|X_OK) == -1) { rs_log_error("can't use TMPDIR \"%s\": %s", tempdir, strerror(errno)); return EXIT_IO_ERROR; } random_bits = (unsigned long) getpid() << 16; # if HAVE_GETTIMEOFDAY { struct timeval tv; gettimeofday(&tv, NULL); random_bits ^= tv.tv_usec << 16; random_bits ^= tv.tv_sec; } # else random_bits ^= time(NULL); # endif #if 0 random_bits = 0; /* FOR TESTING */ #endif do { free(s); if (asprintf(&s, "%s/%s_%08lx%s", tempdir, prefix, random_bits & 0xffffffffUL, suffix) == -1) return EXIT_OUT_OF_MEMORY; /* Note that if the name already exists as a symlink, this * open call will fail. * * The permissions are tight because nobody but this process * and our children should do anything with it. */ fd = open(s, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd == -1) { /* try again */ rs_trace("failed to create %s: %s", s, strerror(errno)); random_bits += 7777; /* fairly prime */ continue; } if (close(fd) == -1) { /* huh? */ rs_log_warning("failed to close %s: %s", s, strerror(errno)); return EXIT_IO_ERROR; } break; } while (1); if ((ret = dcc_add_cleanup(s))) { /* bailing out */ unlink(s); free(s); return ret; } *name_ret = s; return 0; }
int dcc_r_many_files(int in_fd, const char *dirname, enum dcc_compress compr) { int ret = 0; unsigned int n_files; unsigned int i; char *name = NULL; char *link_target = NULL; char token[5]; #ifdef XCODE_INTEGRATION /* NOTE: If this function is ever used for something besides pump * mode support for receiving things to be compiled, then it should * take another argument to include/exclude this fixup. */ char *xci_name, *xci_link_target, *extension; #endif if ((ret = dcc_r_token_int(in_fd, "NFIL", &n_files))) return ret; for (i = 0; i < n_files; ++i) { /* like dcc_r_argv */ unsigned int link_or_file_len; if ((ret = dcc_r_token_string(in_fd, "NAME", &name))) goto out_cleanup; #ifdef XCODE_INTEGRATION xci_name = dcc_xci_unmask_developer_dir(name); if (xci_name) { free(name); name = xci_name; xci_name = NULL; } else { ret = EXIT_OUT_OF_MEMORY; goto out_cleanup; } #endif /* FIXME: verify that name starts with '/' and doesn't contain '..'. */ if ((ret = prepend_dir_to_name(dirname, &name))) goto out_cleanup; if ((ret = dcc_r_sometoken_int(in_fd, token, &link_or_file_len))) goto out_cleanup; /* Must prepend the dirname for the file name, a link's target name. */ if (strncmp(token, "LINK", 4) == 0) { if ((ret = dcc_r_str_alloc(in_fd, link_or_file_len, &link_target))){ goto out_cleanup; } rs_trace("got '%s'", link_target); #ifdef XCODE_INTEGRATION xci_link_target = dcc_xci_unmask_developer_dir(link_target); if (xci_link_target) { free(link_target); link_target = xci_link_target; xci_link_target = NULL; } else { ret = EXIT_OUT_OF_MEMORY; goto out_cleanup; } #endif /* FIXME: verify that link_target doesn't contain '..'. * But the include server uses '..' to reference system * directories (see _MakeLinkFromMirrorToRealLocation * in include_server/compiler_defaults.py), so we'll need to * modify that first. */ if (link_target[0] == '/') { if ((ret = prepend_dir_to_name(dirname, &link_target))) { goto out_cleanup; } } if ((ret = dcc_mk_tmp_ancestor_dirs(name))) { goto out_cleanup; } if (symlink(link_target, name) != 0) { rs_log_error("failed to create path for %s: %s", name, strerror(errno)); ret = 1; goto out_cleanup; } if ((ret = dcc_add_cleanup(name))) { /* bailing out */ unlink(name); goto out_cleanup; } } else if (strncmp(token, "FILE", 4) == 0) { if ((ret = dcc_r_file(in_fd, name, link_or_file_len, compr))) { goto out_cleanup; } #ifdef XCODE_INTEGRATION if ((extension = dcc_find_extension(name)) && !strcmp(extension, ".hmap") && (ret = dcc_xci_headermap_fix(name, dirname))) { unlink(name); goto out_cleanup; } #endif if ((ret = dcc_add_cleanup(name))) { /* bailing out */ unlink(name); goto out_cleanup; } } else { char buf[4 + sizeof(link_or_file_len)]; /* unexpected token */ rs_log_error("protocol derailment: expected token FILE or LINK"); /* We should explain what happened here, but we have already read * a few more bytes. */ strncpy(buf, token, 4); /* TODO(manos): this is probably not kosher */ memcpy(&buf[4], &link_or_file_len, sizeof(link_or_file_len)); dcc_explain_mismatch(buf, 12, in_fd); ret = EXIT_PROTOCOL_ERROR; goto out_cleanup; } out_cleanup: if (name) { free(name); name = NULL; } if (link_target) { free(link_target); link_target = NULL; } if (ret) break; } return ret; }