Skip to content

Commit f19e3ca

Browse files
author
Vicent Martí
committed
odb: Proper symlink hashing
1 parent 18e5b85 commit f19e3ca

File tree

3 files changed

+107
-25
lines changed

3 files changed

+107
-25
lines changed

src/blob.c

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,7 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
6868
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
6969
{
7070
int error = GIT_SUCCESS;
71-
int islnk = 0;
72-
int fd = 0;
7371
git_buf full_path = GIT_BUF_INIT;
74-
char buffer[2048];
7572
git_off_t size;
7673
git_odb_stream *stream = NULL;
7774
struct stat st;
@@ -92,51 +89,69 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
9289
goto cleanup;
9390
}
9491

95-
islnk = S_ISLNK(st.st_mode);
9692
size = st.st_size;
9793

9894
error = git_repository_odb__weakptr(&odb, repo);
9995
if (error < GIT_SUCCESS)
10096
goto cleanup;
10197

102-
if (!islnk) {
103-
if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) {
104-
error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr
105-
);
106-
goto cleanup;
107-
}
108-
}
109-
11098
if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
11199
goto cleanup;
112100

113-
while (size > 0) {
101+
if (S_ISLNK(st.st_mode)) {
102+
char *link_data;
114103
ssize_t read_len;
115104

116-
if (!islnk)
117-
read_len = p_read(fd, buffer, sizeof(buffer));
118-
else
119-
read_len = p_readlink(full_path.ptr, buffer, sizeof(buffer));
105+
link_data = git__malloc(size);
106+
if (!link_data) {
107+
error = GIT_ENOMEM;
108+
goto cleanup;
109+
}
110+
111+
read_len = p_readlink(full_path.ptr, link_data, size);
120112

121-
if (read_len < 0) {
122-
error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
113+
if (read_len != (ssize_t)size) {
114+
error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink");
115+
free(link_data);
123116
goto cleanup;
124117
}
125118

126-
stream->write(stream, buffer, read_len);
127-
size -= read_len;
119+
stream->write(stream, link_data, size);
120+
free(link_data);
121+
122+
} else {
123+
int fd;
124+
char buffer[2048];
125+
126+
if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) {
127+
error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr);
128+
goto cleanup;
129+
}
130+
131+
while (size > 0) {
132+
ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
133+
134+
if (read_len < 0) {
135+
error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
136+
p_close(fd);
137+
goto cleanup;
138+
}
139+
140+
stream->write(stream, buffer, read_len);
141+
size -= read_len;
142+
}
143+
144+
p_close(fd);
128145
}
129146

130147
error = stream->finalize_write(oid, stream);
131148

132149
cleanup:
133150
if (stream)
134151
stream->free(stream);
135-
if (!islnk && fd)
136-
p_close(fd);
152+
137153
git_buf_free(&full_path);
138154

139-
return error == GIT_SUCCESS ? GIT_SUCCESS :
140-
git__rethrow(error, "Failed to create blob");
155+
return error;
141156
}
142157

src/odb.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,48 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
145145
return GIT_SUCCESS;
146146
}
147147

148+
int git_odb__hashlink(git_oid *out, const char *path)
149+
{
150+
struct stat st;
151+
int error;
152+
git_off_t size;
153+
154+
error = p_lstat(path, &st);
155+
if (error < 0)
156+
return git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno));
157+
158+
size = st.st_size;
159+
160+
if (!git__is_sizet(size))
161+
return git__throw(GIT_EOSERR, "File size overflow for 32-bit systems");
162+
163+
if (S_ISLNK(st.st_mode)) {
164+
char *link_data;
165+
ssize_t read_len;
166+
167+
link_data = git__malloc(size);
168+
if (link_data == NULL)
169+
return GIT_ENOMEM;
170+
171+
read_len = p_readlink(path, link_data, size + 1);
172+
if (read_len != (ssize_t)size)
173+
return git__throw(GIT_EOSERR, "Failed to read symlink data");
174+
175+
error = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);
176+
free(link_data);
177+
} else {
178+
int fd;
179+
180+
if ((fd = p_open(path, O_RDONLY)) < 0)
181+
return git__throw(GIT_ENOTFOUND, "Could not open '%s'", path);
182+
183+
error = git_odb__hashfd(out, fd, (size_t)size, GIT_OBJ_BLOB);
184+
p_close(fd);
185+
}
186+
187+
return error;
188+
}
189+
148190
int git_odb_hashfile(git_oid *out, const char *path, git_otype type)
149191
{
150192
int fd, error;

src/odb.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,32 @@ struct git_odb {
3939
git_cache cache;
4040
};
4141

42+
/*
43+
* Hash a git_rawobj internally.
44+
* The `git_rawobj` is supposed to be previously initialized
45+
*/
4246
int git_odb__hashobj(git_oid *id, git_rawobj *obj);
47+
48+
/*
49+
* Hash an open file descriptor.
50+
* This is a performance call when the contents of a fd need to be hashed,
51+
* but the fd is already open and we have the size of the contents.
52+
*
53+
* Saves us some `stat` calls.
54+
*
55+
* The fd is never closed, not even on error. It must be opened and closed
56+
* by the caller
57+
*/
4358
int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type);
4459

60+
/*
61+
* Hash a `path`, assuming it could be a POSIX symlink: if the path is a symlink,
62+
* then the raw contents of the symlink will be hashed. Otherwise, this will
63+
* fallback to `git_odb__hashfd`.
64+
*
65+
* The hash type for this call is always `GIT_OBJ_BLOB` because symlinks may only
66+
* point to blobs.
67+
*/
68+
int git_odb__hashlink(git_oid *out, const char *path);
69+
4570
#endif

0 commit comments

Comments
 (0)