]>
Commit | Line | Data |
---|---|---|
575f4974 LT |
1 | #include "cache.h" |
2 | #include <sys/wait.h> | |
3 | ||
4 | static const char receive_pack_usage[] = "git-receive-pack [--unpack=executable] <git-dir> [heads]"; | |
5 | ||
6 | static const char *unpacker = "git-unpack-objects"; | |
7 | ||
8 | static int path_match(const char *path, int nr, char **match) | |
9 | { | |
10 | int i; | |
11 | int pathlen = strlen(path); | |
12 | ||
13 | for (i = 0; i < nr; i++) { | |
14 | char *s = match[i]; | |
15 | int len = strlen(s); | |
16 | ||
17 | if (!len || len > pathlen) | |
18 | continue; | |
19 | if (memcmp(path + pathlen - len, s, len)) | |
20 | continue; | |
21 | if (pathlen > len && path[pathlen - len - 1] != '/') | |
22 | continue; | |
23 | *s = 0; | |
24 | return 1; | |
25 | } | |
26 | return 0; | |
27 | } | |
28 | ||
29 | static void safe_write(int fd, const void *buf, unsigned n) | |
30 | { | |
31 | while (n) { | |
32 | int ret = write(fd, buf, n); | |
33 | if (ret > 0) { | |
34 | buf += ret; | |
35 | n -= ret; | |
36 | continue; | |
37 | } | |
38 | if (!ret) | |
39 | die("write error (disk full?)"); | |
40 | if (errno == EAGAIN || errno == EINTR) | |
41 | continue; | |
42 | die("write error (%s)", strerror(errno)); | |
43 | } | |
44 | } | |
45 | ||
46 | /* | |
47 | * If we buffered things up above (we don't, but we should), | |
48 | * we'd flush it here | |
49 | */ | |
50 | static void flush_safe(int fd) | |
51 | { | |
52 | } | |
53 | ||
54 | /* | |
55 | * Write a packetized stream, where each line is preceded by | |
56 | * its length (including the header) as a 4-byte hex number. | |
57 | * A length of 'zero' means end of stream (and a length of 1-3 | |
58 | * would be an error). | |
59 | */ | |
60 | #define hex(a) (hexchar[(a) & 15]) | |
61 | static void packet_write(const char *fmt, ...) | |
62 | { | |
63 | static char buffer[1000]; | |
64 | static char hexchar[] = "0123456789abcdef"; | |
65 | va_list args; | |
66 | unsigned n; | |
67 | ||
68 | va_start(args, fmt); | |
69 | n = vsnprintf(buffer + 4, sizeof(buffer) - 4, fmt, args); | |
70 | va_end(args); | |
71 | if (n >= sizeof(buffer)-4) | |
72 | die("protocol error: impossibly long line"); | |
73 | n += 4; | |
74 | buffer[0] = hex(n >> 12); | |
75 | buffer[1] = hex(n >> 8); | |
76 | buffer[2] = hex(n >> 4); | |
77 | buffer[3] = hex(n); | |
78 | safe_write(1, buffer, n); | |
79 | } | |
80 | ||
81 | static void show_ref(const char *path, unsigned char *sha1) | |
82 | { | |
83 | packet_write("%s %s\n", sha1_to_hex(sha1), path); | |
84 | } | |
85 | ||
86 | static int read_ref(const char *path, unsigned char *sha1) | |
87 | { | |
88 | int ret = -1; | |
89 | int fd = open(path, O_RDONLY); | |
90 | ||
91 | if (fd >= 0) { | |
92 | char buffer[60]; | |
93 | if (read(fd, buffer, sizeof(buffer)) >= 40) | |
94 | ret = get_sha1_hex(buffer, sha1); | |
95 | close(fd); | |
96 | } | |
97 | return ret; | |
98 | } | |
99 | ||
100 | static void write_head_info(const char *base, int nr, char **match) | |
101 | { | |
102 | DIR *dir = opendir(base); | |
103 | ||
104 | if (dir) { | |
105 | struct dirent *de; | |
106 | int baselen = strlen(base); | |
107 | char *path = xmalloc(baselen + 257); | |
108 | memcpy(path, base, baselen); | |
109 | ||
110 | while ((de = readdir(dir)) != NULL) { | |
111 | char sha1[20]; | |
112 | struct stat st; | |
113 | int namelen; | |
114 | ||
115 | if (de->d_name[0] == '.') | |
116 | continue; | |
117 | namelen = strlen(de->d_name); | |
118 | if (namelen > 255) | |
119 | continue; | |
120 | memcpy(path + baselen, de->d_name, namelen+1); | |
121 | if (lstat(path, &st) < 0) | |
122 | continue; | |
123 | if (S_ISDIR(st.st_mode)) { | |
124 | path[baselen + namelen] = '/'; | |
125 | path[baselen + namelen + 1] = 0; | |
126 | write_head_info(path, nr, match); | |
127 | continue; | |
128 | } | |
129 | if (read_ref(path, sha1) < 0) | |
130 | continue; | |
131 | if (nr && !path_match(path, nr, match)) | |
132 | continue; | |
133 | show_ref(path, sha1); | |
134 | } | |
135 | free(path); | |
136 | closedir(dir); | |
137 | } | |
138 | } | |
139 | ||
140 | /* | |
141 | * This is all pretty stupid, but we use this packetized line | |
142 | * format to make a streaming format possible without ever | |
143 | * over-running the read buffers. That way we'll never read | |
144 | * into what might be the pack data (which should go to another | |
145 | * process entirely). | |
146 | * | |
147 | * The writing side could use stdio, but since the reading | |
148 | * side can't, we stay with pure read/write interfaces. | |
149 | */ | |
150 | static void safe_read(int fd, void *buffer, unsigned size) | |
151 | { | |
152 | int n = 0; | |
153 | ||
154 | while (n < size) { | |
155 | int ret = read(0, buffer + n, size - n); | |
156 | if (ret < 0) { | |
157 | if (errno == EINTR || errno == EAGAIN) | |
158 | continue; | |
159 | die("read error (%s)", strerror(errno)); | |
160 | } | |
161 | if (!ret) | |
162 | die("unexpected EOF"); | |
163 | n += ret; | |
164 | } | |
165 | } | |
166 | ||
167 | static int safe_read_line(char *buffer, unsigned size) | |
168 | { | |
169 | int n, len; | |
170 | ||
171 | safe_read(0, buffer, 4); | |
172 | ||
173 | len = 0; | |
174 | for (n = 0; n < 4; n++) { | |
175 | unsigned char c = buffer[n]; | |
176 | len <<= 4; | |
177 | if (c >= '0' && c <= '9') { | |
178 | len += c - '0'; | |
179 | continue; | |
180 | } | |
181 | if (c >= 'a' && c <= 'f') { | |
182 | len += c - 'a' + 10; | |
183 | continue; | |
184 | } | |
185 | if (c >= 'A' && c <= 'F') { | |
186 | len += c - 'A' + 10; | |
187 | continue; | |
188 | } | |
189 | die("protocol error: bad line length character"); | |
190 | } | |
191 | if (!len) | |
192 | return 0; | |
193 | if (len < 4 || len >= size) | |
194 | die("protocol error: bad line length %d", len); | |
195 | safe_read(0, buffer + 4, len - 4); | |
196 | buffer[len] = 0; | |
197 | return len; | |
198 | } | |
199 | ||
200 | struct line { | |
201 | struct line *next; | |
202 | char data[0]; | |
203 | }; | |
204 | ||
205 | struct line *commands = NULL; | |
206 | ||
207 | /* | |
208 | * This gets called after(if) we've successfully | |
209 | * unpacked the data payload. | |
210 | */ | |
211 | static void execute_commands(void) | |
212 | { | |
213 | struct line *line = commands; | |
214 | ||
215 | while (line) { | |
216 | printf("%s", line->data); | |
217 | line = line->next; | |
218 | } | |
219 | } | |
220 | ||
221 | static void read_head_info(void) | |
222 | { | |
223 | struct line **p = &commands; | |
224 | for (;;) { | |
225 | static char line[1000]; | |
226 | int len = safe_read_line(line, sizeof(line)); | |
227 | struct line *n; | |
228 | if (!len) | |
229 | break; | |
230 | n = xmalloc(sizeof(struct line) + len); | |
231 | n->next = NULL; | |
232 | memcpy(n->data, line + 4, len - 3); | |
233 | *p = n; | |
234 | p = &n->next; | |
235 | } | |
236 | } | |
237 | ||
238 | static void unpack(void) | |
239 | { | |
240 | pid_t pid = fork(); | |
241 | ||
242 | if (pid < 0) | |
243 | die("unpack fork failed"); | |
244 | if (!pid) { | |
245 | char *const envp[] = { "GIT_DIR=.", NULL }; | |
246 | execle(unpacker, unpacker, NULL, envp); | |
247 | die("unpack execute failed"); | |
248 | } | |
249 | ||
250 | for (;;) { | |
251 | int status, code; | |
252 | int retval = waitpid(pid, &status, 0); | |
253 | ||
254 | if (retval < 0) { | |
255 | if (errno == EINTR) | |
256 | continue; | |
257 | die("waitpid failed (%s)", strerror(retval)); | |
258 | } | |
259 | if (retval != pid) | |
260 | die("waitpid is confused"); | |
261 | if (WIFSIGNALED(status)) | |
262 | die("%s died of signal %d", unpacker, WTERMSIG(status)); | |
263 | if (!WIFEXITED(status)) | |
264 | die("%s died out of really strange complications", unpacker); | |
265 | code = WEXITSTATUS(status); | |
266 | if (code) | |
267 | die("%s exited with error code %d", unpacker, code); | |
268 | return; | |
269 | } | |
270 | } | |
271 | ||
272 | int main(int argc, char **argv) | |
273 | { | |
274 | int i, nr_heads = 0; | |
275 | const char *dir = NULL; | |
276 | char **heads = NULL; | |
277 | ||
278 | argv++; | |
279 | for (i = 1; i < argc; i++) { | |
280 | const char *arg = *argv++; | |
281 | ||
282 | if (*arg == '-') { | |
283 | if (!strncmp(arg, "--unpack=", 9)) { | |
284 | unpacker = arg+9; | |
285 | continue; | |
286 | } | |
287 | /* Do flag handling here */ | |
288 | usage(receive_pack_usage); | |
289 | } | |
290 | dir = arg; | |
291 | heads = argv; | |
292 | nr_heads = argc - i - 1; | |
293 | break; | |
294 | } | |
295 | if (!dir) | |
296 | usage(receive_pack_usage); | |
297 | ||
298 | /* chdir to the directory. If that fails, try appending ".git" */ | |
299 | if (chdir(dir) < 0) { | |
300 | static char path[PATH_MAX]; | |
301 | snprintf(path, sizeof(path), "%s.git", dir); | |
302 | if (chdir(path) < 0) | |
303 | die("unable to cd to %s", dir); | |
304 | } | |
305 | ||
306 | /* If we have a ".git" directory, chdir to it */ | |
307 | chdir(".git"); | |
308 | ||
309 | if (access("objects", X_OK) < 0 || access("refs/heads", X_OK) < 0) | |
310 | die("%s doesn't appear to be a git directory", dir); | |
311 | write_head_info("refs/", nr_heads, heads); | |
312 | ||
313 | /* EOF */ | |
314 | safe_write(1, "0000", 4); | |
315 | flush_safe(1); | |
316 | ||
317 | read_head_info(); | |
318 | unpack(); | |
319 | execute_commands(); | |
320 | return 0; | |
321 | } |