hacking the server
[reposerve] / server / src / repo.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <sys/stat.h>
6 #include <git2.h>
7 #include "repo.h"
8 #include "md5.h"
9
10 static int add_file(const char *name, const char *path);
11 static void calc_chksum(FILE *fp, uint32_t *sum);
12 static int proc_tree(git_repository *repo, git_tree *tree, const char *path);
13
14 static struct repo_file *files;
15 static int num_files, max_files;
16 static const char *repo_path;
17
18 int repo_init(const char *path)
19 {
20         int res = -1;
21         git_repository *repo = 0;
22         git_object *tree;
23
24         git_libgit2_init();
25
26         if(git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_NO_SEARCH, 0) != 0) {
27                 fprintf(stderr, "repo_init: failed to open repository: %s: %s\n", path,
28                                 git_error_last()->message);
29                 goto end;
30         }
31
32         files = 0;
33         num_files = max_files = 0;
34         repo_path = path;
35
36         if(git_revparse_single(&tree, repo, "HEAD^{tree}") != 0) {
37                 fprintf(stderr, "repo_init: %s\n", git_error_last()->message);
38                 goto end;
39         }
40         if(proc_tree(repo, (git_tree*)tree, 0) == -1) {
41                 goto end;
42         }
43         git_object_free(tree);
44
45         res = 0;
46 end:
47         if(res != 0) {
48                 free(files);
49                 files = 0;
50                 num_files = max_files = 0;
51         }
52         if(repo) git_repository_free(repo);
53         git_libgit2_shutdown();
54         return res;
55 }
56
57 void repo_cleanup(void)
58 {
59         int i;
60         for(i=0; i<num_files; i++) {
61                 free(files[i].fullpath);
62         }
63         free(files);
64         files = 0;
65         num_files = max_files = 0;
66 }
67
68 int repo_num_files(void)
69 {
70         return num_files;
71 }
72
73 struct repo_file *repo_file(int idx)
74 {
75         if(idx < 0 || idx >= num_files) return 0;
76         return files + idx;
77 }
78
79 static int add_file(const char *name, const char *path)
80 {
81         struct repo_file *file;
82         int pathlen;
83         struct stat st;
84         FILE *fp;
85
86         if(num_files >= max_files) {
87                 int newmax = max_files ? max_files * 2 : 16;
88                 void *newfiles = realloc(files, newmax * sizeof *files);
89                 if(!newfiles) {
90                         fprintf(stderr, "repo_init: failed to add file\n");
91                         return -1;
92                 }
93                 files = newfiles;
94                 max_files = newmax;
95         }
96
97         pathlen = strlen(repo_path) + strlen(name) + 1;
98         if(path) {
99                 pathlen += strlen(path) + 1;
100         }
101
102         file = files + num_files;
103         if(!(file->fullpath = malloc(pathlen + 1))) {
104                 fprintf(stderr, "repo_init: failed to allocate file path buffer\n");
105                 return -1;
106         }
107         if(path) {
108                 sprintf(file->fullpath, "%s/%s/%s", repo_path, path, name);
109         } else {
110                 sprintf(file->fullpath, "%s/%s", repo_path, name);
111         }
112         file->path = file->fullpath + strlen(repo_path) + 1;
113
114         /* attempt to open the file */
115         if(!(fp = fopen(file->fullpath, "rb"))) {
116                 fprintf(stderr, "repo_init: failed to open file: %s: %s\n", file->fullpath, strerror(errno));
117                 free(file->fullpath);
118                 return -1;
119         }
120
121         /* get size & modification time */
122         fstat(fileno(fp), &st);
123         file->size = st.st_size;
124         file->mtime = st.st_mtime;
125
126         /* calculate md5 checksum */
127         calc_chksum(fp, file->chksum);
128
129         fclose(fp);
130
131         num_files++;
132         return 0;
133 }
134
135 static void calc_chksum(FILE *fp, uint32_t *sum)
136 {
137         static char buf[4096];
138         int sz;
139         struct md5_state md;
140
141         md5_begin(&md);
142         while((sz = fread(buf, 1, sizeof buf, fp)) > 0) {
143                 md5_msg(&md, buf, sz);
144         }
145         md5_end(&md);
146
147         memcpy(sum, md.sum, sizeof md.sum);
148 }
149
150 static int proc_tree(git_repository *repo, git_tree *tree, const char *path)
151 {
152         int i, count = git_tree_entrycount(tree);
153         const git_tree_entry *ent;
154         git_object_t type;
155         git_object *obj;
156         const char *name;
157
158         for(i=0; i<count; i++) {
159                 ent = git_tree_entry_byindex(tree, i);
160                 type = git_tree_entry_type(ent);
161                 name = git_tree_entry_name(ent);
162
163                 switch(type) {
164                 case GIT_OBJECT_BLOB:
165                         if(add_file(name, path) == -1) {
166                                 return -1;
167                         }
168                         break;
169
170                 case GIT_OBJECT_TREE:
171                         git_tree_entry_to_object(&obj, repo, ent);
172                         if(path) {
173                                 char *pathbuf = malloc(strlen(path) + strlen(name) + 2);
174                                 if(!pathbuf) {
175                                         fprintf(stderr, "repo_init: failed to allocate memory\n");
176                                         return -1;
177                                 }
178                                 sprintf(pathbuf, "%s/%s", path, name);
179                                 proc_tree(repo, (git_tree*)obj, pathbuf);
180                                 free(pathbuf);
181                         } else {
182                                 proc_tree(repo, (git_tree*)obj, name);
183                         }
184                         git_object_free(obj);
185                         break;
186
187                 default:
188                         break;
189                 }
190         }
191
192         return 0;
193 }