int git_futils_mmap_ro_file(git_map *out, const char *path) { git_file fd = git_futils_open_ro(path); git_off_t len; int result; if (fd < 0) return fd; if ((len = git_futils_filesize(fd)) < 0) { result = -1; goto out; } if (!git__is_sizet(len)) { giterr_set(GITERR_OS, "file `%s` too large to mmap", path); result = -1; goto out; } result = git_futils_mmap_ro(out, fd, 0, (size_t)len); out: p_close(fd); return result; }
static int write_file_stream( git_oid *oid, git_odb *odb, const char *path, git_off_t file_size) { int fd, error; char buffer[4096]; git_odb_stream *stream = NULL; ssize_t read_len = -1, written = 0; if ((error = git_odb_open_wstream( &stream, odb, (size_t)file_size, GIT_OBJ_BLOB)) < 0) return error; if ((fd = git_futils_open_ro(path)) < 0) { git_odb_stream_free(stream); return -1; } while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) { error = git_odb_stream_write(stream, buffer, read_len); written += read_len; } p_close(fd); if (written != file_size || read_len < 0) { giterr_set(GITERR_OS, "Failed to read file into stream"); error = -1; } if (!error) error = git_odb_stream_finalize_write(oid, stream); git_odb_stream_free(stream); return error; }
static int read_header_loose(git_rawobj *out, git_buf *loc) { unsigned char obj[1024]; int fd, obj_len, error; assert(out && loc); if (git_buf_oom(loc)) return -1; out->data = NULL; if ((error = fd = git_futils_open_ro(loc->ptr)) < 0 || (error = obj_len = p_read(fd, obj, sizeof(obj))) < 0) goto done; if (!is_zlib_compressed_data(obj, (size_t)obj_len)) error = read_header_loose_packlike(out, obj, (size_t)obj_len); else error = read_header_loose_standard(out, obj, (size_t)obj_len); if (!error && !git_object_typeisloose(out->type)) { giterr_set(GITERR_ZLIB, "failed to read loose object header"); error = -1; goto done; } done: if (fd >= 0) p_close(fd); return error; }
static int diff_file_content_load_workdir_file( git_diff_file_content *fc, git_buf *path, git_diff_options *diff_opts) { int error = 0; git_filter_list *fl = NULL; git_file fd = git_futils_open_ro(git_buf_cstr(path)); git_buf raw = GIT_BUF_INIT; if (fd < 0) return fd; if (!fc->file->size && !(fc->file->size = git_futils_filesize(fd))) goto cleanup; if ((diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0 && diff_file_content_binary_by_size(fc)) goto cleanup; if ((error = git_filter_list_load( &fl, fc->repo, NULL, fc->file->path, GIT_FILTER_TO_ODB, GIT_FILTER_ALLOW_UNSAFE)) < 0) goto cleanup; /* if there are no filters, try to mmap the file */ if (fl == NULL) { if (!(error = git_futils_mmap_ro( &fc->map, fd, 0, (size_t)fc->file->size))) { fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA; goto cleanup; } /* if mmap failed, fall through to try readbuffer below */ giterr_clear(); } if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size))) { git_buf out = GIT_BUF_INIT; error = git_filter_list_apply_to_data(&out, fl, &raw); if (out.ptr != raw.ptr) git_buf_dispose(&raw); if (!error) { fc->map.len = out.size; fc->map.data = out.ptr; fc->flags |= GIT_DIFF_FLAG__FREE_DATA; } } cleanup: git_filter_list_free(fl); p_close(fd); return error; }
static int diff_file_content_load_workdir_file( git_diff_file_content *fc, git_buf *path) { int error = 0; git_vector filters = GIT_VECTOR_INIT; git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT; git_file fd = git_futils_open_ro(git_buf_cstr(path)); if (fd < 0) return fd; if (!fc->file->size && !(fc->file->size = git_futils_filesize(fd))) goto cleanup; if (diff_file_content_binary_by_size(fc)) goto cleanup; if ((error = git_filters_load( &filters, fc->repo, fc->file->path, GIT_FILTER_TO_ODB)) < 0) goto cleanup; /* error >= is a filter count */ if (error == 0) { if (!(error = git_futils_mmap_ro( &fc->map, fd, 0, (size_t)fc->file->size))) fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA; else /* fall through to try readbuffer below */ giterr_clear(); } if (error != 0) { error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size); if (error < 0) goto cleanup; if (!filters.length) git_buf_swap(&filtered, &raw); else error = git_filters_apply(&filtered, &raw, &filters); if (!error) { fc->map.len = git_buf_len(&filtered); fc->map.data = git_buf_detach(&filtered); fc->flags |= GIT_DIFF_FLAG__FREE_DATA; } git_buf_free(&raw); git_buf_free(&filtered); } cleanup: git_filters_free(&filters); p_close(fd); 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 ((fd = git_futils_open_ro(path)) < 0) return fd; if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) { p_close(fd); 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) { p_close(fd); return 0; } if (mtime != NULL) *mtime = st.st_mtime; if (size != NULL) *size = (size_t)st.st_size; 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_odb_hashfile(git_oid *out, const char *path, git_otype type) { git_off_t size; int result, fd = git_futils_open_ro(path); if (fd < 0) return fd; if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) { giterr_set(GITERR_OS, "File size overflow for 32-bit systems"); p_close(fd); return -1; } result = git_odb__hashfd(out, fd, (size_t)size, type); p_close(fd); return result; }
int git_odb__hashlink(git_oid *out, const char *path) { struct stat st; git_off_t size; int result; if (git_path_lstat(path, &st) < 0) return -1; size = st.st_size; if (!git__is_sizet(size)) { giterr_set(GITERR_OS, "File size overflow for 32-bit systems"); return -1; } if (S_ISLNK(st.st_mode)) { char *link_data; ssize_t read_len; link_data = (char*) git__malloc((size_t)(size + 1)); GITERR_CHECK_ALLOC(link_data); read_len = p_readlink(path, link_data, (size_t)size); link_data[size] = '\0'; if (read_len != (ssize_t)size) { giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path); git__free(link_data); return -1; } result = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB); git__free(link_data); } else { int fd = git_futils_open_ro(path); if (fd < 0) return -1; result = git_odb__hashfd(out, fd, (size_t)size, GIT_OBJ_BLOB); p_close(fd); } return result; }
int git_hashsig_create_fromfile( git_hashsig **out, const char *path, git_hashsig_option_t opts) { char buf[4096]; ssize_t buflen = 0; int error = 0, fd; hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT; git_hashsig *sig = hashsig_alloc(opts); GITERR_CHECK_ALLOC(sig); if ((fd = git_futils_open_ro(path)) < 0) { git__free(sig); return fd; } while (!error) { if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) { if ((error = (int)buflen) < 0) giterr_set(GITERR_OS, "Read error on '%s' calculating similarity hashes", path); break; } error = hashsig_add_hashes(sig, buf, buflen, &prog); } p_close(fd); if (!error) error = hashsig_finalize_hashes(sig); if (!error) *out = sig; else git_hashsig_free(sig); return error; }
static int write_file_stream( git_oid *oid, git_odb *odb, const char *path, git_off_t file_size) { int fd, error; char buffer[4096]; git_odb_stream *stream = NULL; if ((error = git_odb_open_wstream( &stream, odb, (size_t)file_size, GIT_OBJ_BLOB)) < 0) return error; if ((fd = git_futils_open_ro(path)) < 0) { stream->free(stream); return -1; } while (!error && file_size > 0) { ssize_t read_len = p_read(fd, buffer, sizeof(buffer)); if (read_len < 0) { giterr_set( GITERR_OS, "Failed to create blob. Can't read whole file"); error = -1; } else if (!(error = stream->write(stream, buffer, read_len))) file_size -= read_len; } p_close(fd); if (!error) error = stream->finalize_write(oid, stream); stream->free(stream); return error; }
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; }
int git_attr_file__load( git_attr_file **out, git_repository *repo, git_attr_session *attr_session, 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; git_attr_file *file; struct stat st; bool nonexistent = false; *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; /* Do not assume that data straight from the ODB is NULL-terminated; * copy the contents of a file to a buffer to work on */ git_buf_put(&content, git_blob_rawcontent(blob), git_blob_rawsize(blob)); break; } case GIT_ATTR_FILE__FROM_FILE: { int fd = -1; /* For open or read errors, pretend that we got ENOTFOUND. */ /* TODO: issue warning when warning API is available */ if (p_stat(entry->fullpath, &st) < 0 || S_ISDIR(st.st_mode) || (fd = git_futils_open_ro(entry->fullpath)) < 0 || (error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0) nonexistent = true; if (fd >= 0) p_close(fd); 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; /* store the key of the attr_reader; don't bother with cache * invalidation during the same attr reader session. */ if (attr_session) file->session_key = attr_session->key; if (parser && (error = parser(repo, file, git_buf_cstr(&content))) < 0) { git_attr_file__free(file); goto cleanup; } /* write cache breakers */ if (nonexistent) file->nonexistent = 1; else 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; }
int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated) { git_file fd; size_t len; struct stat st; assert(buf && path && *path); if (updated != NULL) *updated = 0; if ((fd = git_futils_open_ro(path)) < 0) return fd; if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) { p_close(fd); giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path); return -1; } /* * If we were given a time, we only want to read the file if it * has been modified. */ if (mtime != NULL && *mtime >= st.st_mtime) { p_close(fd); return 0; } if (mtime != NULL) *mtime = st.st_mtime; len = (size_t) st.st_size; git_buf_clear(buf); if (git_buf_grow(buf, len + 1) < 0) { p_close(fd); return -1; } buf->ptr[len] = '\0'; while (len > 0) { ssize_t read_size = p_read(fd, buf->ptr, len); if (read_size < 0) { p_close(fd); giterr_set(GITERR_OS, "Failed to read descriptor for '%s'", path); return -1; } len -= read_size; buf->size += read_size; } p_close(fd); if (updated != NULL) *updated = 1; return 0; }