vendor/libgit2/src/fileops.c in rugged-0.19.0 vs vendor/libgit2/src/fileops.c in rugged-0.21.0

- old
+ new

@@ -4,10 +4,11 @@ * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" #include "fileops.h" +#include "global.h" #include <ctype.h> #if GIT_WIN32 #include "win32/findfile.h" #endif @@ -16,14 +17,17 @@ return git_futils_mkdir( file_path, NULL, mode, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR); } -int git_futils_mktmp(git_buf *path_out, const char *filename) +int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode) { int fd; + mode_t mask; + p_umask(mask = p_umask(0)); + git_buf_sets(path_out, filename); git_buf_puts(path_out, "_git2_XXXXXX"); if (git_buf_oom(path_out)) return -1; @@ -32,10 +36,16 @@ giterr_set(GITERR_OS, "Failed to create temporary file '%s'", path_out->ptr); return -1; } + if (p_chmod(path_out->ptr, (mode & ~mask))) { + giterr_set(GITERR_OS, + "Failed to set permissions on file '%s'", path_out->ptr); + return -1; + } + return fd; } int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode) { @@ -53,26 +63,16 @@ return fd; } int git_futils_creat_locked(const char *path, const mode_t mode) { - int fd; - -#ifdef GIT_WIN32 - wchar_t buf[GIT_WIN_PATH]; - - git__utf8_to_16(buf, GIT_WIN_PATH, path); - fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | + int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY | O_CLOEXEC, mode); -#else - fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | - O_EXCL | O_BINARY | O_CLOEXEC, mode); -#endif if (fd < 0) { giterr_set(GITERR_OS, "Failed to create locked file '%s'", path); - return -1; + return errno == EEXIST ? GIT_ELOCKED : -1; } return fd; } @@ -85,15 +85,12 @@ } int git_futils_open_ro(const char *path) { int fd = p_open(path, O_RDONLY); - if (fd < 0) { - if (errno == ENOENT || errno == ENOTDIR) - fd = GIT_ENOTFOUND; - giterr_set(GITERR_OS, "Failed to open '%s'", path); - } + if (fd < 0) + return git_path_set_error(errno, path, "open"); return fd; } git_off_t git_futils_filesize(git_file fd) { @@ -108,11 +105,11 @@ } mode_t git_futils_canonical_mode(mode_t raw_mode) { if (S_ISREG(raw_mode)) - return S_IFREG | GIT_CANONICAL_PERMS(raw_mode); + return S_IFREG | GIT_PERMS_CANONICAL(raw_mode); else if (S_ISLNK(raw_mode)) return S_IFLNK; else if (S_ISGITLINK(raw_mode)) return S_IFGITLINK; else if (S_ISDIR(raw_mode)) @@ -133,10 +130,11 @@ /* p_read loops internally to read len bytes */ read_size = p_read(fd, buf->ptr, len); if (read_size != (ssize_t)len) { giterr_set(GITERR_OS, "Failed to read descriptor"); + git_buf_free(buf); return -1; } buf->ptr[read_size] = '\0'; buf->size = read_size; @@ -154,15 +152,20 @@ assert(buf && path && *path); if (updated != NULL) *updated = 0; - if ((fd = git_futils_open_ro(path)) < 0) - return fd; + if (p_stat(path, &st) < 0) + return git_path_set_error(errno, path, "stat"); - if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) { - p_close(fd); + + if (S_ISDIR(st.st_mode)) { + giterr_set(GITERR_INVALID, "requested file is a directory"); + return GIT_ENOTFOUND; + } + + if (!git__is_sizet(st.st_size+1)) { giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path); return -1; } /* @@ -175,19 +178,21 @@ changed = true; if (!size && !mtime) changed = true; if (!changed) { - p_close(fd); return 0; } if (mtime != NULL) *mtime = st.st_mtime; if (size != NULL) *size = (size_t)st.st_size; + if ((fd = git_futils_open_ro(path)) < 0) + return fd; + if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) { p_close(fd); return -1; } @@ -220,10 +225,11 @@ } if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) { giterr_set(GITERR_OS, "Could not write to '%s'", path); (void)p_close(fd); + return error; } if ((error = p_close(fd)) < 0) giterr_set(GITERR_OS, "Error while closing '%s'", path); @@ -345,15 +351,15 @@ *tail = '\0'; st.st_mode = 0; /* make directory */ if (p_mkdir(make_path.ptr, mode) < 0) { - int tmp_errno = errno; + int tmp_errno = giterr_system_last(); /* ignore error if directory already exists */ if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { - errno = tmp_errno; + giterr_system_set(tmp_errno); giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); goto done; } /* with exclusive create, existing dir is an error */ @@ -396,13 +402,15 @@ typedef struct { const char *base; size_t baselen; uint32_t flags; - int error; + int depth; } futils__rmdir_data; +#define FUTILS_MAX_DEPTH 100 + static int futils__error_cannot_rmdir(const char *path, const char *filemsg) { if (filemsg) giterr_set(GITERR_OS, "Could not remove directory. File '%s' %s", path, filemsg); @@ -437,78 +445,83 @@ return error; } static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) { - struct stat st; + int error = 0; futils__rmdir_data *data = opaque; + struct stat st; - if ((data->error = p_lstat_posixly(path->ptr, &st)) < 0) { + if (data->depth > FUTILS_MAX_DEPTH) + error = futils__error_cannot_rmdir( + path->ptr, "directory nesting too deep"); + + else if ((error = p_lstat_posixly(path->ptr, &st)) < 0) { if (errno == ENOENT) - data->error = 0; + error = 0; else if (errno == ENOTDIR) { /* asked to remove a/b/c/d/e and a/b is a normal file */ if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0) - data->error = futils__rm_first_parent(path, data->base); + error = futils__rm_first_parent(path, data->base); else futils__error_cannot_rmdir( path->ptr, "parent is not directory"); } else - futils__error_cannot_rmdir(path->ptr, "cannot access"); + error = git_path_set_error(errno, path->ptr, "rmdir"); } else if (S_ISDIR(st.st_mode)) { - int error = git_path_direach(path, futils__rmdir_recurs_foreach, data); + data->depth++; + + error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data); + + data->depth--; + if (error < 0) - return (error == GIT_EUSER) ? data->error : error; + return error; - data->error = p_rmdir(path->ptr); + if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0) + return error; - if (data->error < 0) { + if ((error = p_rmdir(path->ptr)) < 0) { if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 && (errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY)) - data->error = 0; + error = 0; else - futils__error_cannot_rmdir(path->ptr, NULL); + error = git_path_set_error(errno, path->ptr, "rmdir"); } } else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) { - data->error = p_unlink(path->ptr); - - if (data->error < 0) - futils__error_cannot_rmdir(path->ptr, "cannot be removed"); + if (p_unlink(path->ptr) < 0) + error = git_path_set_error(errno, path->ptr, "remove"); } else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0) - data->error = futils__error_cannot_rmdir(path->ptr, "still present"); + error = futils__error_cannot_rmdir(path->ptr, "still present"); - return data->error; + return error; } static int futils__rmdir_empty_parent(void *opaque, git_buf *path) { futils__rmdir_data *data = opaque; - int error; + int error = 0; if (git_buf_len(path) <= data->baselen) - return GIT_ITEROVER; + error = GIT_ITEROVER; - error = p_rmdir(git_buf_cstr(path)); - - if (error) { + else if (p_rmdir(git_buf_cstr(path)) < 0) { int en = errno; if (en == ENOENT || en == ENOTDIR) { - giterr_clear(); - error = 0; + /* do nothing */ } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { - giterr_clear(); error = GIT_ITEROVER; } else { - futils__error_cannot_rmdir(git_buf_cstr(path), NULL); + error = git_path_set_error(errno, git_buf_cstr(path), "rmdir"); } } return error; } @@ -522,248 +535,32 @@ /* build path and find "root" where we should start calling mkdir */ if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0) return -1; + memset(&data, 0, sizeof(data)); data.base = base ? base : ""; data.baselen = base ? strlen(base) : 0; data.flags = flags; - data.error = 0; error = futils__rmdir_recurs_foreach(&data, &fullpath); /* remove now-empty parents if requested */ - if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) { + if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) error = git_path_walk_up( &fullpath, base, futils__rmdir_empty_parent, &data); - if (error == GIT_ITEROVER) - error = 0; + if (error == GIT_ITEROVER) { + giterr_clear(); + error = 0; } git_buf_free(&fullpath); return error; } -int git_futils_cleanupdir_r(const char *path) -{ - int error; - git_buf fullpath = GIT_BUF_INIT; - futils__rmdir_data data; - - if ((error = git_buf_put(&fullpath, path, strlen(path))) < 0) - goto clean_up; - - data.base = ""; - data.baselen = 0; - data.flags = GIT_RMDIR_REMOVE_FILES; - data.error = 0; - - if (!git_path_exists(path)) { - giterr_set(GITERR_OS, "Path does not exist: %s" , path); - error = GIT_ERROR; - goto clean_up; - } - - if (!git_path_isdir(path)) { - giterr_set(GITERR_OS, "Path is not a directory: %s" , path); - error = GIT_ERROR; - goto clean_up; - } - - error = git_path_direach(&fullpath, futils__rmdir_recurs_foreach, &data); - if (error == GIT_EUSER) - error = data.error; - -clean_up: - git_buf_free(&fullpath); - return error; -} - - -static int git_futils_guess_system_dirs(git_buf *out) -{ -#ifdef GIT_WIN32 - return git_win32__find_system_dirs(out); -#else - return git_buf_sets(out, "/etc"); -#endif -} - -static int git_futils_guess_global_dirs(git_buf *out) -{ -#ifdef GIT_WIN32 - return git_win32__find_global_dirs(out); -#else - return git_buf_sets(out, getenv("HOME")); -#endif -} - -static int git_futils_guess_xdg_dirs(git_buf *out) -{ -#ifdef GIT_WIN32 - return git_win32__find_xdg_dirs(out); -#else - const char *env = NULL; - - if ((env = getenv("XDG_CONFIG_HOME")) != NULL) - return git_buf_joinpath(out, env, "git"); - else if ((env = getenv("HOME")) != NULL) - return git_buf_joinpath(out, env, ".config/git"); - - git_buf_clear(out); - return 0; -#endif -} - -typedef int (*git_futils_dirs_guess_cb)(git_buf *out); - -static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] = - { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; - -static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = { - git_futils_guess_system_dirs, - git_futils_guess_global_dirs, - git_futils_guess_xdg_dirs, -}; - -static int git_futils_check_selector(git_futils_dir_t which) -{ - if (which < GIT_FUTILS_DIR__MAX) - return 0; - giterr_set(GITERR_INVALID, "config directory selector out of range"); - return -1; -} - -int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which) -{ - assert(out); - - *out = NULL; - - GITERR_CHECK_ERROR(git_futils_check_selector(which)); - - if (!git_buf_len(&git_futils__dirs[which])) - GITERR_CHECK_ERROR( - git_futils__dir_guess[which](&git_futils__dirs[which])); - - *out = &git_futils__dirs[which]; - return 0; -} - -int git_futils_dirs_get_str(char *out, size_t outlen, git_futils_dir_t which) -{ - const git_buf *path = NULL; - - GITERR_CHECK_ERROR(git_futils_check_selector(which)); - GITERR_CHECK_ERROR(git_futils_dirs_get(&path, which)); - - if (!out || path->size >= outlen) { - giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path"); - return GIT_EBUFS; - } - - git_buf_copy_cstr(out, outlen, path); - return 0; -} - -#define PATH_MAGIC "$PATH" - -int git_futils_dirs_set(git_futils_dir_t which, const char *search_path) -{ - const char *expand_path = NULL; - git_buf merge = GIT_BUF_INIT; - - GITERR_CHECK_ERROR(git_futils_check_selector(which)); - - if (search_path != NULL) - expand_path = strstr(search_path, PATH_MAGIC); - - /* init with default if not yet done and needed (ignoring error) */ - if ((!search_path || expand_path) && - !git_buf_len(&git_futils__dirs[which])) - git_futils__dir_guess[which](&git_futils__dirs[which]); - - /* if $PATH is not referenced, then just set the path */ - if (!expand_path) - return git_buf_sets(&git_futils__dirs[which], search_path); - - /* otherwise set to join(before $PATH, old value, after $PATH) */ - if (expand_path > search_path) - git_buf_set(&merge, search_path, expand_path - search_path); - - if (git_buf_len(&git_futils__dirs[which])) - git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, - merge.ptr, git_futils__dirs[which].ptr); - - expand_path += strlen(PATH_MAGIC); - if (*expand_path) - git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path); - - git_buf_swap(&git_futils__dirs[which], &merge); - git_buf_free(&merge); - - return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0; -} - -void git_futils_dirs_free(void) -{ - int i; - for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i) - git_buf_free(&git_futils__dirs[i]); -} - -static int git_futils_find_in_dirlist( - git_buf *path, const char *name, git_futils_dir_t which, const char *label) -{ - size_t len; - const char *scan, *next = NULL; - const git_buf *syspath; - - GITERR_CHECK_ERROR(git_futils_dirs_get(&syspath, which)); - - for (scan = git_buf_cstr(syspath); scan; scan = next) { - for (next = strchr(scan, GIT_PATH_LIST_SEPARATOR); - next && next > scan && next[-1] == '\\'; - next = strchr(next + 1, GIT_PATH_LIST_SEPARATOR)) - /* find unescaped separator or end of string */; - - len = next ? (size_t)(next++ - scan) : strlen(scan); - if (!len) - continue; - - GITERR_CHECK_ERROR(git_buf_set(path, scan, len)); - GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name)); - - if (git_path_exists(path->ptr)) - return 0; - } - - git_buf_clear(path); - giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name); - return GIT_ENOTFOUND; -} - -int git_futils_find_system_file(git_buf *path, const char *filename) -{ - return git_futils_find_in_dirlist( - path, filename, GIT_FUTILS_DIR_SYSTEM, "system"); -} - -int git_futils_find_global_file(git_buf *path, const char *filename) -{ - return git_futils_find_in_dirlist( - path, filename, GIT_FUTILS_DIR_GLOBAL, "global"); -} - -int git_futils_find_xdg_file(git_buf *path, const char *filename) -{ - return git_futils_find_in_dirlist( - path, filename, GIT_FUTILS_DIR_XDG, "global/xdg"); -} - int git_futils_fake_symlink(const char *old, const char *new) { int retcode = GIT_ERROR; int fd = git_futils_creat_withpath(new, 0755, 0644); if (fd >= 0) { @@ -804,15 +601,12 @@ if ((ifd = git_futils_open_ro(from)) < 0) return ifd; if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) { - if (errno == ENOENT || errno == ENOTDIR) - ofd = GIT_ENOTFOUND; - giterr_set(GITERR_OS, "Failed to open '%s' for writing", to); p_close(ifd); - return ofd; + return git_path_set_error(errno, to, "open for writing"); } return cp_by_fd(ifd, ofd, true); } @@ -884,22 +678,22 @@ if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 && from->ptr[git_path_basename_offset(from)] == '.') return 0; - if (git_buf_joinpath( - &info->to, info->to_root, from->ptr + info->from_prefix) < 0) - return -1; + if ((error = git_buf_joinpath( + &info->to, info->to_root, from->ptr + info->from_prefix)) < 0) + return error; - if (p_lstat(info->to.ptr, &to_st) < 0) { - if (errno != ENOENT && errno != ENOTDIR) { - giterr_set(GITERR_OS, - "Could not access %s while copying files", info->to.ptr); - return -1; - } - } else + if (!(error = git_path_lstat(info->to.ptr, &to_st))) exists = true; + else if (error != GIT_ENOTFOUND) + return error; + else { + giterr_clear(); + error = 0; + } if ((error = git_path_lstat(from->ptr, &from_st)) < 0) return error; if (S_ISDIR(from_st.st_mode)) { @@ -913,11 +707,11 @@ if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0) error = _cp_r_mkdir(info, from); /* recurse onto target directory */ if (!error && (!exists || S_ISDIR(to_st.st_mode))) - error = git_path_direach(from, _cp_r_callback, info); + error = git_path_direach(from, 0, _cp_r_callback, info); if (oldmode != 0) info->dirmode = oldmode; return error; @@ -928,11 +722,11 @@ return 0; if (p_unlink(info->to.ptr) < 0) { giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'", info->to.ptr); - return -1; + return GIT_EEXISTS; } } /* Done if this isn't a regular file or a symlink */ if (!S_ISREG(from_st.st_mode) && @@ -944,17 +738,19 @@ if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 && (error = _cp_r_mkdir(info, from)) < 0) return error; /* make symlink or regular file */ - if (S_ISLNK(from_st.st_mode)) + if (info->flags & GIT_CPDIR_LINK_FILES) { + error = p_link(from->ptr, info->to.ptr); + } else if (S_ISLNK(from_st.st_mode)) { error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size); - else { + } else { mode_t usemode = from_st.st_mode; if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0) - usemode = (usemode & 0111) ? 0777 : 0666; + usemode = GIT_PERMS_FOR_WRITE(usemode); error = git_futils_cp(from->ptr, info->to.ptr, usemode); } return error; @@ -971,10 +767,11 @@ cp_r_info info; if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */ return -1; + memset(&info, 0, sizeof(info)); info.to_root = to; info.flags = flags; info.dirmode = dirmode; info.from_prefix = path.size; git_buf_init(&info.to, 0); @@ -1008,14 +805,12 @@ /* if the stamp is NULL, then always reload */ if (stamp == NULL) return 1; - if (p_stat(path, &st) < 0) { - giterr_set(GITERR_OS, "Could not stat '%s'", path); + if (p_stat(path, &st) < 0) return GIT_ENOTFOUND; - } if (stamp->mtime == (git_time_t)st.st_mtime && stamp->size == (git_off_t)st.st_size && stamp->ino == (unsigned int)st.st_ino) return 0; @@ -1034,6 +829,19 @@ if (source) memcpy(target, source, sizeof(*target)); else memset(target, 0, sizeof(*target)); +} + + +void git_futils_filestamp_set_from_stat( + git_futils_filestamp *stamp, struct stat *st) +{ + if (st) { + stamp->mtime = (git_time_t)st->st_mtime; + stamp->size = (git_off_t)st->st_size; + stamp->ino = (unsigned int)st->st_ino; + } else { + memset(stamp, 0, sizeof(*stamp)); + } }