static int futils__rmdir_empty_parent(void *opaque, const char *path) { futils__rmdir_data *data = opaque; int error = 0; if (strlen(path) <= data->baselen) error = GIT_ITEROVER; else if (p_rmdir(path) < 0) { int en = errno; if (en == ENOENT || en == ENOTDIR) { /* do nothing */ } else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0 && en == EBUSY) { error = git_path_set_error(errno, path, "rmdir"); } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { error = GIT_ITEROVER; } else { error = git_path_set_error(errno, path, "rmdir"); } } return error; }
static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) { int error = 0; futils__rmdir_data *data = opaque; struct stat st; 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) 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) error = futils__rm_first_parent(path, data->base); else futils__error_cannot_rmdir( path->ptr, "parent is not directory"); } else error = git_path_set_error(errno, path->ptr, "rmdir"); } else if (S_ISDIR(st.st_mode)) { data->depth++; error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data); data->depth--; if (error < 0) return error; if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0) return error; if ((error = p_rmdir(path->ptr)) < 0) { if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 && (errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY)) error = 0; else error = git_path_set_error(errno, path->ptr, "rmdir"); } } else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) { if (p_unlink(path->ptr) < 0) error = git_path_set_error(errno, path->ptr, "remove"); } else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0) error = futils__error_cannot_rmdir(path->ptr, "still present"); return error; }
int git_path_lstat(const char *path, struct stat *st) { if (p_lstat(path, st) == 0) return 0; return git_path_set_error(errno, path, "stat"); }
int git_futils_open_ro(const char *path) { int fd = p_open(path, O_RDONLY); if (fd < 0) return git_path_set_error(errno, path, "open"); return fd; }
int git_futils_truncate(const char *path, int mode) { int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode); if (fd < 0) return git_path_set_error(errno, path, "open"); close(fd); return 0; }
static int futils__rmdir_empty_parent(void *opaque, git_buf *path) { futils__rmdir_data *data = opaque; int error = 0; if (git_buf_len(path) <= data->baselen) error = GIT_ITEROVER; else if (p_rmdir(git_buf_cstr(path)) < 0) { int en = errno; if (en == ENOENT || en == ENOTDIR) { /* do nothing */ } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { error = GIT_ITEROVER; } else { error = git_path_set_error(errno, git_buf_cstr(path), "rmdir"); } } return error; }
int git_futils_readbuffer_updated( git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated) { git_file fd; struct stat st; bool changed = false; assert(buf && path && *path); if (updated != NULL) *updated = 0; if (p_stat(path, &st) < 0) return git_path_set_error(errno, path, "stat"); 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; } /* * If we were given a time and/or a size, we only want to read the file * if it has been modified. */ if (size && *size != (size_t)st.st_size) changed = true; if (mtime && *mtime != st.st_mtime) changed = true; if (!size && !mtime) changed = true; if (!changed) { 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; } p_close(fd); if (updated != NULL) *updated = 1; return 0; }
int git_futils_readbuffer_updated( git_buf *out, const char *path, git_oid *checksum, int *updated) { int error; git_file fd; struct stat st; git_buf buf = GIT_BUF_INIT; git_oid checksum_new; assert(out && path && *path); if (updated != NULL) *updated = 0; if (p_stat(path, &st) < 0) return git_path_set_error(errno, path, "stat"); 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; } 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; } p_close(fd); if (checksum) { if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) { git_buf_free(&buf); return error; } /* * If we were given a checksum, we only want to use it if it's different */ if (!git_oid__cmp(checksum, &checksum_new)) { git_buf_free(&buf); if (updated) *updated = 0; return 0; } git_oid_cpy(checksum, &checksum_new); } /* * If we're here, the file did change, or the user didn't have an old version */ if (updated != NULL) *updated = 1; git_buf_swap(out, &buf); git_buf_free(&buf); return 0; }
int git_attr_file__load( git_attr_file **out, git_repository *repo, git_attr_file_entry *entry, git_attr_file_source source, git_attr_file_parser parser) { int error = 0; git_blob *blob = NULL; git_buf content = GIT_BUF_INIT; const char *data = NULL; git_attr_file *file; struct stat st; *out = NULL; switch (source) { case GIT_ATTR_FILE__IN_MEMORY: /* in-memory attribute file doesn't need data */ break; case GIT_ATTR_FILE__FROM_INDEX: { git_oid id; if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 || (error = git_blob_lookup(&blob, repo, &id)) < 0) return error; data = git_blob_rawcontent(blob); break; } case GIT_ATTR_FILE__FROM_FILE: { int fd; if (p_stat(entry->fullpath, &st) < 0) return git_path_set_error(errno, entry->fullpath, "stat"); if (S_ISDIR(st.st_mode)) return GIT_ENOTFOUND; /* For open or read errors, return ENOTFOUND to skip item */ /* TODO: issue warning when warning API is available */ if ((fd = git_futils_open_ro(entry->fullpath)) < 0) return GIT_ENOTFOUND; error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size); p_close(fd); if (error < 0) return GIT_ENOTFOUND; data = content.ptr; break; } default: giterr_set(GITERR_INVALID, "Unknown file source %d", source); return -1; } if ((error = git_attr_file__new(&file, entry, source)) < 0) goto cleanup; if (parser && (error = parser(repo, file, data)) < 0) { git_attr_file__free(file); goto cleanup; } /* write cache breaker */ if (source == GIT_ATTR_FILE__FROM_INDEX) git_oid_cpy(&file->cache_data.oid, git_blob_id(blob)); else if (source == GIT_ATTR_FILE__FROM_FILE) git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st); /* else always cacheable */ *out = file; cleanup: git_blob_free(blob); git_buf_free(&content); return error; }