#include "common.h" #include "fileops.h" #include <ctype.h> int git_futils_mv_atomic(const char *from, const char *to) { #ifdef GIT_WIN32 /* * Win32 POSIX compilance my ass. If the destination * file exists, the `rename` call fails. This is as * close as it gets with the Win32 API. */ return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR; #else /* Don't even try this on Win32 */ if (!link(from, to)) { p_unlink(from); return GIT_SUCCESS; } if (!rename(from, to)) return GIT_SUCCESS; return GIT_ERROR; #endif } int git_futils_mkpath2file(const char *file_path) { const int mode = 0755; /* or 0777 ? */ int error = GIT_SUCCESS; char target_folder_path[GIT_PATH_MAX]; error = git_path_dirname_r(target_folder_path, sizeof(target_folder_path), file_path); if (error < GIT_SUCCESS) return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path); /* Does the containing folder exist? */ if (git_futils_isdir(target_folder_path)) { git_path_join(target_folder_path, target_folder_path, ""); /* Ensure there's a trailing slash */ /* Let's create the tree structure */ error = git_futils_mkdir_r(target_folder_path, mode); if (error < GIT_SUCCESS) return error; /* The callee already takes care of setting the correct error message. */ } return GIT_SUCCESS; } int git_futils_mktmp(char *path_out, const char *filename) { int fd; strcpy(path_out, filename); strcat(path_out, "_git2_XXXXXX"); #if defined(_MSC_VER) /* FIXME: there may be race conditions when multi-threading * with the library */ if (_mktemp_s(path_out, GIT_PATH_MAX) != 0) return git__throw(GIT_EOSERR, "Failed to make temporary file %s", path_out); fd = p_creat(path_out, 0744); #else fd = mkstemp(path_out); #endif return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out); } int git_futils_creat_withpath(const char *path, int mode) { if (git_futils_mkpath2file(path) < GIT_SUCCESS) return git__throw(GIT_EOSERR, "Failed to create file %s", path); return p_creat(path, mode); } int git_futils_creat_locked(const char *path, int mode) { int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create locked file. Could not open %s", path); } int git_futils_creat_locked_withpath(const char *path, int mode) { if (git_futils_mkpath2file(path) < GIT_SUCCESS) return git__throw(GIT_EOSERR, "Failed to create locked file %s", path); return git_futils_creat_locked(path, mode); } int git_futils_isdir(const char *path) { #ifdef GIT_WIN32 DWORD attr = GetFileAttributes(path); if (attr == INVALID_FILE_ATTRIBUTES) return GIT_ERROR; return (attr & FILE_ATTRIBUTE_DIRECTORY) ? GIT_SUCCESS : GIT_ERROR; #else struct stat st; if (p_stat(path, &st) < GIT_SUCCESS) return GIT_ERROR; return S_ISDIR(st.st_mode) ? GIT_SUCCESS : GIT_ERROR; #endif } int git_futils_isfile(const char *path) { struct stat st; int stat_error; assert(path); stat_error = p_stat(path, &st); if (stat_error < GIT_SUCCESS) return -1; if (!S_ISREG(st.st_mode)) return -1; return 0; } int git_futils_exists(const char *path) { assert(path); return access(path, F_OK); } git_off_t git_futils_filesize(git_file fd) { struct stat sb; if (p_fstat(fd, &sb)) return GIT_ERROR; return sb.st_size; } int git_futils_readbuffer(git_fbuffer *obj, const char *path) { git_file fd; size_t len; git_off_t size; unsigned char *buff; assert(obj && path && *path); if ((fd = p_open(path, O_RDONLY)) < 0) return git__throw(GIT_ERROR, "Failed to open %s for reading", path); if (((size = git_futils_filesize(fd)) < 0) || !git__is_sizet(size+1)) { p_close(fd); return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path); } len = (size_t) size; if ((buff = git__malloc(len + 1)) == NULL) { p_close(fd); return GIT_ENOMEM; } if (p_read(fd, buff, len) < 0) { p_close(fd); free(buff); return git__throw(GIT_ERROR, "Failed to read file `%s`", path); } buff[len] = '\0'; p_close(fd); obj->data = buff; obj->len = len; return GIT_SUCCESS; } void git_futils_freebuffer(git_fbuffer *obj) { assert(obj); free(obj->data); obj->data = NULL; } int git_futils_mv_withpath(const char *from, const char *to) { if (git_futils_mkpath2file(to) < GIT_SUCCESS) return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */ return git_futils_mv_atomic(from, to); /* The callee already takes care of setting the correct error message. */ } int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len) { return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin); } void git_futils_mmap_free(git_map *out) { p_munmap(out); } int git_futils_direach( char *path, size_t path_sz, int (*fn)(void *, char *), void *arg) { size_t wd_len = strlen(path); DIR *dir; struct dirent *de; if (!wd_len || path_sz < wd_len + 2) return git__throw(GIT_EINVALIDARGS, "Failed to process `%s` tree structure. Path is either empty or buffer size is too short", path); while (path[wd_len - 1] == '/') wd_len--; path[wd_len++] = '/'; path[wd_len] = '\0'; dir = opendir(path); if (!dir) return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path); while ((de = readdir(dir)) != NULL) { size_t de_len; int result; /* always skip '.' and '..' */ if (de->d_name[0] == '.') { if (de->d_name[1] == '\0') continue; if (de->d_name[1] == '.' && de->d_name[2] == '\0') continue; } de_len = strlen(de->d_name); if (path_sz < wd_len + de_len + 1) { closedir(dir); return git__throw(GIT_ERROR, "Failed to process `%s` tree structure. Buffer size is too short", path); } strcpy(path + wd_len, de->d_name); result = fn(arg, path); if (result < GIT_SUCCESS) { closedir(dir); return result; /* The callee is reponsible for setting the correct error message */ } if (result > 0) { closedir(dir); return result; } } closedir(dir); return GIT_SUCCESS; } int git_futils_mkdir_r(const char *path, int mode) { int error, root_path_offset; char *pp, *sp; char *path_copy = git__strdup(path); if (path_copy == NULL) return GIT_ENOMEM; error = GIT_SUCCESS; pp = path_copy; root_path_offset = git_path_root(pp); if (root_path_offset > 0) pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */ while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) { if (sp != pp && git_futils_isdir(path_copy) < GIT_SUCCESS) { *sp = 0; error = p_mkdir(path_copy, mode); /* Do not choke while trying to recreate an existing directory */ if (errno == EEXIST) error = GIT_SUCCESS; *sp = '/'; } pp = sp + 1; } if (*pp != '\0' && error == GIT_SUCCESS) { error = p_mkdir(path, mode); if (errno == EEXIST) error = GIT_SUCCESS; } free(path_copy); if (error < GIT_SUCCESS) return git__throw(error, "Failed to recursively create `%s` tree structure", path); return GIT_SUCCESS; } int git_futils_cmp_path(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2) { int len = len1 < len2 ? len1 : len2; int cmp; cmp = memcmp(name1, name2, len); if (cmp) return cmp; if (len1 < len2) return ((!isdir1 && !isdir2) ? -1 : (isdir1 ? '/' - name2[len1] : name2[len1] - '/')); if (len1 > len2) return ((!isdir1 && !isdir2) ? 1 : (isdir2 ? name1[len2] - '/' : '/' - name1[len2])); return 0; }