diff options
author | Vicent Martà <tanoku@gmail.com> | 2012-02-10 20:16:42 +0100 |
---|---|---|
committer | Vicent Martà <tanoku@gmail.com> | 2012-02-10 20:16:42 +0100 |
commit | f19e3ca28835eab8dbef62915c475caa18f355fe (patch) | |
tree | 65018936e22b2fea693bc57a6433e14e4d127df5 | |
parent | 18e5b8547d075afc53c2b20ba15ef7c09cb5efd6 (diff) | |
download | libgit2-f19e3ca28835eab8dbef62915c475caa18f355fe.tar.gz |
odb: Proper symlink hashing
-rw-r--r-- | src/blob.c | 65 | ||||
-rw-r--r-- | src/odb.c | 42 | ||||
-rw-r--r-- | src/odb.h | 25 |
3 files changed, 107 insertions, 25 deletions
diff --git a/src/blob.c b/src/blob.c index 7497ba7bf..4e95bd9cb 100644 --- a/src/blob.c +++ b/src/blob.c @@ -68,10 +68,7 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path) { int error = GIT_SUCCESS; - int islnk = 0; - int fd = 0; git_buf full_path = GIT_BUF_INIT; - char buffer[2048]; git_off_t size; git_odb_stream *stream = NULL; struct stat st; @@ -92,39 +89,59 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat goto cleanup; } - islnk = S_ISLNK(st.st_mode); size = st.st_size; error = git_repository_odb__weakptr(&odb, repo); if (error < GIT_SUCCESS) goto cleanup; - if (!islnk) { - if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) { - error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr -); - goto cleanup; - } - } - if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS) goto cleanup; - while (size > 0) { + if (S_ISLNK(st.st_mode)) { + char *link_data; ssize_t read_len; - if (!islnk) - read_len = p_read(fd, buffer, sizeof(buffer)); - else - read_len = p_readlink(full_path.ptr, buffer, sizeof(buffer)); + link_data = git__malloc(size); + if (!link_data) { + error = GIT_ENOMEM; + goto cleanup; + } + + read_len = p_readlink(full_path.ptr, link_data, size); - if (read_len < 0) { - error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file"); + if (read_len != (ssize_t)size) { + error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink"); + free(link_data); goto cleanup; } - stream->write(stream, buffer, read_len); - size -= read_len; + stream->write(stream, link_data, size); + free(link_data); + + } else { + int fd; + char buffer[2048]; + + if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) { + error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr); + goto cleanup; + } + + while (size > 0) { + ssize_t read_len = p_read(fd, buffer, sizeof(buffer)); + + if (read_len < 0) { + error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file"); + p_close(fd); + goto cleanup; + } + + stream->write(stream, buffer, read_len); + size -= read_len; + } + + p_close(fd); } error = stream->finalize_write(oid, stream); @@ -132,11 +149,9 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat cleanup: if (stream) stream->free(stream); - if (!islnk && fd) - p_close(fd); + git_buf_free(&full_path); - return error == GIT_SUCCESS ? GIT_SUCCESS : - git__rethrow(error, "Failed to create blob"); + return error; } @@ -145,6 +145,48 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) return GIT_SUCCESS; } +int git_odb__hashlink(git_oid *out, const char *path) +{ + struct stat st; + int error; + git_off_t size; + + error = p_lstat(path, &st); + if (error < 0) + return git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno)); + + size = st.st_size; + + if (!git__is_sizet(size)) + return git__throw(GIT_EOSERR, "File size overflow for 32-bit systems"); + + if (S_ISLNK(st.st_mode)) { + char *link_data; + ssize_t read_len; + + link_data = git__malloc(size); + if (link_data == NULL) + return GIT_ENOMEM; + + read_len = p_readlink(path, link_data, size + 1); + if (read_len != (ssize_t)size) + return git__throw(GIT_EOSERR, "Failed to read symlink data"); + + error = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB); + free(link_data); + } else { + int fd; + + if ((fd = p_open(path, O_RDONLY)) < 0) + return git__throw(GIT_ENOTFOUND, "Could not open '%s'", path); + + error = git_odb__hashfd(out, fd, (size_t)size, GIT_OBJ_BLOB); + p_close(fd); + } + + return error; +} + int git_odb_hashfile(git_oid *out, const char *path, git_otype type) { int fd, error; @@ -39,7 +39,32 @@ struct git_odb { git_cache cache; }; +/* + * Hash a git_rawobj internally. + * The `git_rawobj` is supposed to be previously initialized + */ int git_odb__hashobj(git_oid *id, git_rawobj *obj); + +/* + * Hash an open file descriptor. + * This is a performance call when the contents of a fd need to be hashed, + * but the fd is already open and we have the size of the contents. + * + * Saves us some `stat` calls. + * + * The fd is never closed, not even on error. It must be opened and closed + * by the caller + */ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type); +/* + * Hash a `path`, assuming it could be a POSIX symlink: if the path is a symlink, + * then the raw contents of the symlink will be hashed. Otherwise, this will + * fallback to `git_odb__hashfd`. + * + * The hash type for this call is always `GIT_OBJ_BLOB` because symlinks may only + * point to blobs. + */ +int git_odb__hashlink(git_oid *out, const char *path); + #endif |