├── .gitignore ├── LICENSE ├── Makefile ├── README ├── lib ├── .gitignore ├── Makefile ├── buffering.c ├── buffering.h ├── common.c ├── common.h ├── index.c ├── index.h ├── ini.c ├── ini.h ├── loose.c ├── loose.h ├── pack.c ├── pack.h ├── protocol.c ├── protocol.h ├── zlib-handler.c └── zlib-handler.h └── src ├── Makefile ├── cat-file.c ├── cat-file.h ├── clone.c ├── clone.h ├── clone_http.c ├── clone_ssh.c ├── hash-object.c ├── hash-object.h ├── index-pack.c ├── index-pack.h ├── init.c ├── init.h ├── log.c ├── log.h ├── ogit.c ├── ogit.h ├── remote.c ├── remote.h ├── tests ├── .gitignore ├── Makefile └── ogit_test.sh ├── update-index.c └── update-index.h /.gitignore: -------------------------------------------------------------------------------- 1 | ogit 2 | *.debug 3 | *.full 4 | *.o 5 | *.core 6 | .depend* 7 | cscope.out 8 | tags 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | SPDX-License-Identifier: BSD-2-Clause 2 | 3 | Copyright (c) 2018, Farhan Khan 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | SUBDIR= lib src 3 | 4 | .include 5 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | OpenGit 2 | Open Source replacement for GPL git 3 | 4 | Written for and dedicated to the BSD systems 5 | Using the BSD-2-Clause license 6 | 7 | Farhan Khan 8 | PGP KeyID: 1312 89CE 663E 1EB2 179C 1C83 C41D 2281 F8DA C0DE 9 | 10 | Objectives 11 | * Laterally compatible with GPL git. You should be able to symlink git to 12 | ogit and no one should be the wiser. 13 | * Code clarity (not the case at the moment) 14 | * BSD-2-Clause licensed 15 | 16 | At the moment, the objective is a read-only version, akin to svnlite, but 17 | there is some overlap in writing objects and updating the index. 18 | 19 | 🍉✌️ 20 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | *.pico 2 | *.po 3 | *.a 4 | *.so 5 | *.[0-9] 6 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | 2 | LIB= ogit 3 | SHLIB_MAJOR= 0 4 | SHLIB_MINOR= 0 5 | SRCS= buffering.c common.c index.c ini.c loose.c pack.c protocol.c \ 6 | zlib-handler.c 7 | 8 | .if defined(NDEBUG) 9 | CFLAGS+= -DNDEBUG -Wall -Wunreachable-code -Werror -fPIC 10 | CC= gcc 11 | .endif 12 | CFLAGS+= -Wall 13 | 14 | .include 15 | -------------------------------------------------------------------------------- /lib/buffering.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include "buffering.h" 31 | #include "common.h" 32 | 33 | ssize_t 34 | buf_read(int fd, void *buf, size_t count, read_handler read_handler, void *arg) 35 | { 36 | count = read(fd, buf, count); 37 | if (read_handler) 38 | read_handler(buf, count, arg); 39 | 40 | return (count); 41 | } 42 | -------------------------------------------------------------------------------- /lib/buffering.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef __BUFFERING_H 29 | #define __BUFFERING_H 30 | 31 | #include 32 | 33 | typedef int read_handler(void *, size_t, void *); 34 | 35 | ssize_t buf_read(int fd, void *buf, size_t count, read_handler read_handler, 36 | void *arg); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /lib/common.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "common.h" 40 | #include "pack.h" 41 | #include "loose.h" 42 | #include "zlib-handler.h" 43 | 44 | /* 45 | Gets the path of the .git directory 46 | If found, returns 0 and sets dotgitpath 47 | If not found, returns 1 48 | If errors, returns -1 49 | */ 50 | 51 | char repodir[PATH_MAX]; 52 | char dotgitpath[PATH_MAX]; 53 | 54 | const char *object_name[] = { 55 | NULL, 56 | "commit", 57 | "tree", 58 | "blob", 59 | "tag", 60 | NULL, 61 | "obj_ofs_delta", 62 | "obj_ref_delta" 63 | }; 64 | 65 | /* 66 | * Description: Recovers a tree object and iterates through it. It will also 67 | * identify the object type. 68 | * Arguments: 1) treesha is a char[HASH_SIZE] 69 | * 2) tree_handler is what the function should do with the resultant data 70 | */ 71 | void 72 | iterate_tree(struct decompressed_object *decompressed_object, tree_handler tree_handler, void *args) 73 | { 74 | long offset = 0; 75 | long space; 76 | uint8_t *shabin; 77 | char shastr[HASH_SIZE+1], mode[7]; 78 | char *filename; 79 | uint8_t type; 80 | 81 | while(offsetsize) { 82 | /* Get the file mode */ 83 | strlcpy(mode, (char *)decompressed_object->data+offset, 7); 84 | if (mode[5] != ' ') 85 | space = 6; 86 | else 87 | space = 5; 88 | offset = offset + space; 89 | mode[space] = '\0'; 90 | 91 | filename = (char *)decompressed_object->data+offset+1; 92 | offset = offset + strlen((char *)decompressed_object->data+offset) + 1; 93 | 94 | /* Get the SHA, convert it to a string */ 95 | shabin = decompressed_object->data + offset; 96 | sha_bin_to_str(shabin, shastr); 97 | shastr[HASH_SIZE] = '\0'; 98 | 99 | /* Determine the type */ 100 | CONTENT_HANDLER(shastr, get_type_loose_cb, get_type_pack_cb, &type); 101 | 102 | if (tree_handler) 103 | tree_handler(mode, type, shastr, filename, args); 104 | 105 | /* Skip over the binary sha */ 106 | offset = offset + 20; 107 | } 108 | 109 | free(decompressed_object->data); 110 | } 111 | 112 | int 113 | git_repository_path() 114 | { 115 | char *d = NULL; 116 | int s; 117 | char path[PATH_MAX]; 118 | struct stat sb; 119 | int ret = 1; 120 | 121 | d = getcwd(d, PATH_MAX); 122 | if (d == NULL) { 123 | fprintf(stderr, "Unable to get current directory: %s\n", strerror(errno)); 124 | exit(errno); 125 | } 126 | 127 | /* XXX Is there a better way to do this? */ 128 | 129 | while (strncmp(d, "/\0", 2) != 0) { 130 | snprintf(path, sizeof(path), "%s/.git", d); 131 | s = stat(path, &sb); 132 | if (s == -1) { 133 | d = dirname(d); 134 | if (d == NULL) { 135 | ret = -1; 136 | break; 137 | } 138 | continue; 139 | } 140 | else if (s == 0) { 141 | ret = 0; 142 | strlcpy(dotgitpath, path, sizeof(dotgitpath)); 143 | break; 144 | } 145 | }; 146 | 147 | free(d); 148 | return (ret); 149 | } 150 | 151 | void 152 | update_branch_pointer(char repodir, char *ref, char *sha) 153 | { 154 | FILE *reffile; 155 | 156 | reffile = fopen(ref, "w"); 157 | if (reffile == NULL) { 158 | exit(-1); 159 | } 160 | } 161 | 162 | /* 163 | * Description: Converts a SHA in binary format to a string 164 | * Arguments: 1) pointer to the SHA, 2) str is a char[HASH_SIZE] 165 | * which stores the output string 166 | */ 167 | void 168 | sha_bin_to_str(uint8_t *bin, char *str) 169 | { 170 | int x; 171 | for(x=0;x> 4; 173 | str[(x*2)+1] = bin[x] & 0x0f; 174 | } 175 | 176 | for(x=0;x= '0' && str[x] <= '9') 193 | bin[x/2] = (str[x] - '0') & 0x0f; 194 | else if (str[x] >= 'a' && str[x] <= 'f') 195 | bin[x/2] = (str[x] - 'a' + 10) & 0x0f; 196 | 197 | if (str[x+1] >= '0' && str[x+1] <= '9') 198 | bin[x/2] |= (str[x+1] - '0') << 4; 199 | else if (str[x+1] >= 'a' && str[x+1] <= 'f') 200 | bin[x/2] |= (str[x+1] - 'a' + 10) << 4; 201 | } 202 | } 203 | 204 | void 205 | sha_str_to_bin_network(char *str, uint8_t *bin) 206 | { 207 | for(int x=0;x= '0' && str[x] <= '9') 209 | bin[x/2] = ((str[x] - '0') << 4) & 0xf0; 210 | else if (str[x] >= 'a' && str[x] <= 'f') 211 | bin[x/2] = ((str[x] - 'a' + 10) << 4) & 0xf0; 212 | 213 | if (str[x+1] >= '0' && str[x+1] <= '9') 214 | bin[x/2] |= (str[x+1] - '0') & 0x0f; 215 | else if (str[x+1] >= 'a' && str[x+1] <= 'f') 216 | bin[x/2] |= (str[x+1] - 'a' + 10) & 0x0f; 217 | } 218 | } 219 | 220 | /* Description: Returns number of digits in integer */ 221 | int 222 | count_digits(int check) 223 | { 224 | int digits = 1; 225 | while(check /= 10) 226 | digits++; 227 | return (digits); 228 | } 229 | 230 | 231 | /* 232 | * Parse a commit header and content 233 | * ToFree: Run the free_commitcontent() function 234 | */ 235 | void 236 | parse_commitcontent(struct commitcontent *commitcontent, char *header, long len) 237 | { 238 | char *token, *tofree, *string; 239 | int type = COMMITCONTENT_BLANK; 240 | int size; 241 | regex_t re; 242 | regmatch_t m[5]; 243 | 244 | regcomp(&re, "[author|committer] (.*) <(.*)> ([0-9]*) (-?[0-9]*)", REG_EXTENDED); 245 | 246 | commitcontent->numparent = 0; 247 | commitcontent->parent = NULL; 248 | commitcontent->message = NULL; 249 | commitcontent->lines = 0; 250 | 251 | tofree = string = strndup(header, len); 252 | 253 | while((token = strsep(&string, "\n")) != NULL) { 254 | /* Single line */ 255 | if (type == COMMITCONTENT_BLANK && token[0] != ' ') { 256 | if (!strncmp(token, "tree ", 5)) { 257 | strlcpy(commitcontent->treesha, token + 5, HASH_SIZE+1); 258 | } 259 | else if (!strncmp(token, "parent ", 7)) { 260 | commitcontent->parent = realloc(commitcontent->parent, 261 | sizeof(char *) * (commitcontent->numparent+1)); 262 | commitcontent->parent[commitcontent->numparent] = malloc(HASH_SIZE+1); 263 | strlcpy(commitcontent->parent[commitcontent->numparent], token+7, HASH_SIZE+1); 264 | commitcontent->numparent++; 265 | } 266 | else if (!strncmp(token, "author ", 7)) { 267 | if (regexec(&re, token, 5, m, 0) == REG_NOMATCH) { 268 | fprintf(stderr, "Author not matched\n"); 269 | exit(0); 270 | } 271 | commitcontent->author_name = strndup(token + m[1].rm_so, m[1].rm_eo - m[1].rm_so); 272 | commitcontent->author_email = strndup(token + m[2].rm_so, m[2].rm_eo - m[2].rm_so); 273 | commitcontent->author_time = atol(token + m[3].rm_so); 274 | commitcontent->author_tz = strndup(token + m[4].rm_so, m[4].rm_eo - m[4].rm_so); 275 | } 276 | else if (!strncmp(token, "committer ", 10)) { 277 | if (regexec(&re, token, 5, m, 0) == REG_NOMATCH) { 278 | fprintf(stderr, "Committer not matched\n"); 279 | exit(0); 280 | } 281 | commitcontent->committer_name = strndup(token + m[1].rm_so, m[1].rm_eo - m[1].rm_so); 282 | commitcontent->committer_email = strndup(token + m[2].rm_so, m[2].rm_eo - m[2].rm_so); 283 | commitcontent->committer_time = atol(token + m[3].rm_so); 284 | commitcontent->committer_tz = strndup(token + m[4].rm_so, m[4].rm_eo - m[4].rm_so); 285 | } 286 | else if (!strncmp(token, "gpgsig ", 7)) { 287 | type = COMMITCONTENT_GPGSIG; 288 | commitcontent->gpgsig = strdup(token + 7); 289 | size = strlen(token + 7); 290 | } 291 | else if (token[0] == '\0') 292 | type = COMMITCONTENT_MESSAGE; 293 | } 294 | else if (type == COMMITCONTENT_GPGSIG) { 295 | commitcontent->gpgsig = realloc(commitcontent->gpgsig, size + strlen(token) + 1); 296 | commitcontent->gpgsig[size] = '\n'; 297 | strncpy(commitcontent->gpgsig + size + 1, token + 1, strlen(token)); 298 | size += strlen(token) + 1; 299 | if (!strncmp(token, " -----END PGP SIGNATURE-----", 28)) 300 | type = COMMITCONTENT_BLANK; 301 | } 302 | else if (type == COMMITCONTENT_MESSAGE) { 303 | commitcontent->message = realloc(commitcontent->message, 304 | sizeof(char *) * (commitcontent->lines+1)); 305 | commitcontent->message[commitcontent->lines] = strdup(token); 306 | commitcontent->lines++; 307 | } 308 | } 309 | 310 | regfree(&re); 311 | free(tofree); 312 | } 313 | 314 | /* Free a commitcontent struct */ 315 | void 316 | free_commitcontent(struct commitcontent *commitcontent) 317 | { 318 | if (commitcontent->author_name) 319 | free(commitcontent->author_name); 320 | if (commitcontent->author_email) 321 | free(commitcontent->author_email); 322 | if (commitcontent->author_tz) 323 | free(commitcontent->author_tz); 324 | 325 | if (commitcontent->committer_name) 326 | free(commitcontent->committer_name); 327 | if (commitcontent->committer_name) 328 | free(commitcontent->committer_email); 329 | if (commitcontent->committer_name) 330 | free(commitcontent->committer_tz); 331 | 332 | if (commitcontent->parent) { 333 | for(int x=0;x>commitcontent->numparent;x++) 334 | free(commitcontent->parent[x]); 335 | free(commitcontent->parent); 336 | } 337 | 338 | if (commitcontent->message) { 339 | for(int x=0;x>commitcontent->lines;x++) 340 | free(commitcontent->message[x]); 341 | free(commitcontent->message); 342 | } 343 | 344 | if (commitcontent->gpgsig) 345 | free(commitcontent->gpgsig); 346 | } 347 | -------------------------------------------------------------------------------- /lib/common.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | 26 | 27 | #ifndef __COMMON_H__ 28 | #define __COMMON_H__ 29 | 30 | #include 31 | #include 32 | 33 | #define EXIT_SUCCESS 0 /* Success */ 34 | #define EXIT_INVALID_COMMAND 129 /* Invalid command */ 35 | 36 | /* Portability */ 37 | #if defined(__FreeBSD__) 38 | #include 39 | #elif defined(__OpenBSD__) 40 | #include 41 | 42 | #define SHA1_End(x, y) SHA1End(x, y) 43 | #define SHA1_Final(x, y) SHA1Final(x, y) 44 | #define SHA1_Init(x) SHA1Init(x) 45 | #define SHA1_Update(x, y, z) SHA1Update(x, y, z) 46 | #else 47 | #error "Unsure what to do for inclusion of sha1 bits in this environment" 48 | #endif 49 | 50 | /* From Documentation/technical/pack-format.txt */ 51 | #define OBJ_NONE 0 52 | #define OBJ_COMMIT 1 53 | #define OBJ_TREE 2 54 | #define OBJ_BLOB 3 55 | #define OBJ_TAG 4 56 | // Reserved 5 57 | #define OBJ_OFS_DELTA 6 58 | #define OBJ_REF_DELTA 7 59 | #define OBJ_UNKNOWN 8 // OpenGit addition 60 | 61 | #define HASH_SIZE 40 62 | 63 | /* 64 | * Used to recover a full object in a single buffer 65 | * not processed incrementally 66 | */ 67 | struct decompressed_object { 68 | unsigned char *data; 69 | unsigned long size; 70 | unsigned long deflated_size; 71 | }; 72 | 73 | #define COMMITCONTENT_BLANK 0 74 | #define COMMITCONTENT_TREE 1 75 | #define COMMITCONTENT_PARENT 2 76 | #define COMMITCONTENT_AUTHOR 3 77 | #define COMMITCONTENT_COMMITTER 4 78 | #define COMMITCONTENT_GPGSIG 5 79 | #define COMMITCONTENT_MESSAGE 99 80 | 81 | /* Data structure used to parse commit messages */ 82 | struct commitcontent { 83 | char *commitsha; 84 | char treesha[HASH_SIZE+1]; 85 | 86 | char *author_name; 87 | char *author_email; 88 | time_t author_time; 89 | char *author_tz; 90 | 91 | char *committer_name; 92 | char *committer_email; 93 | time_t committer_time; 94 | char *committer_tz; 95 | 96 | char **parent; 97 | int numparent; 98 | char *gpgsig; 99 | 100 | char **message; 101 | int lines; 102 | }; 103 | 104 | extern const char *object_name[]; 105 | 106 | #define SOURCE_PACK 0 107 | #define SOURCE_LOOSE 1 108 | 109 | #define CONTENT_HANDLER(sha, loose_handler, pack_handler, args) { \ 110 | if (loose_content_handler(sha, loose_handler, args)) \ 111 | pack_content_handler(sha, pack_handler, args); \ 112 | } while(0) 113 | 114 | // XXX This may need to be migrated to a generic "object.h" or the like 115 | #include "loose.h" 116 | #include "pack.h" 117 | #define ITERATE_TREE(treesha, tree_handler, args) { \ 118 | struct decompressed_object decompressed_object; \ 119 | CONTENT_HANDLER(treesha, buffer_cb, pack_buffer_cb, &decompressed_object); \ 120 | iterate_tree(&decompressed_object, tree_handler, args); \ 121 | } while(0) 122 | 123 | 124 | #ifndef nitems 125 | #define nitems(x) (sizeof((x)) / sizeof((x)[0])) 126 | #endif 127 | 128 | #define BIT(nr) (1 << (nr)) 129 | 130 | typedef void tree_handler(char *, uint8_t, char *, char *, void *); 131 | 132 | extern char repodir[PATH_MAX]; 133 | extern char dotgitpath[PATH_MAX]; 134 | int git_repository_path(); 135 | 136 | void iterate_tree(struct decompressed_object *decompressed_object, tree_handler tree_handler, void *args); 137 | void sha_bin_to_str(uint8_t *bin, char *str); 138 | void sha_str_to_bin(char *str, uint8_t *bin); 139 | void sha_str_to_bin_network(char *str, uint8_t *bin); 140 | int count_digits(int check); 141 | void parse_commitcontent(struct commitcontent *commitcontent, char *header, 142 | long len); 143 | void free_commitcontent(struct commitcontent *commitcontent); 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /lib/index.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "common.h" 36 | #include "index.h" 37 | 38 | /* Write and update the SHA Context */ 39 | void 40 | write_sha(SHA1_CTX *ctx, int fd, void *data, int len) 41 | { 42 | write(fd, data, len); 43 | SHA1_Update(ctx, data, len); 44 | } 45 | 46 | /* 47 | * Description: Captures the cache tree data 48 | * ToFree: Requires treeleaf->subtree to be freed 49 | */ 50 | static struct treeleaf * 51 | tree_entry(unsigned char *indexmap, off_t *offset, int ext_size) 52 | { 53 | struct treeleaf *treeleaf; 54 | unsigned char *endptr; 55 | int entry_count, trees; 56 | int x, s; 57 | 58 | treeleaf = malloc(sizeof(struct treeleaf)); 59 | 60 | treeleaf->ext_size = ext_size; 61 | /* Capture entry_count for the base */ 62 | entry_count = strtol((char *)indexmap + *offset, (char **)&endptr, 10); 63 | *offset = endptr - indexmap; 64 | 65 | /* Capture subtree count for the base */ 66 | trees = strtol((char *)indexmap + *offset, (char **)&endptr, 10); 67 | *offset = endptr - indexmap; 68 | 69 | /* Assign values to treeleaf */ 70 | treeleaf->entry_count = entry_count; 71 | treeleaf->local_tree_count = trees; 72 | 73 | /* Skip past the new line */ 74 | *offset=*offset+1; 75 | 76 | memcpy(treeleaf->sha, indexmap + *offset, HASH_SIZE/2); 77 | /* Skip past the SHA value */ 78 | *offset = *offset + HASH_SIZE/2; 79 | 80 | /* 81 | * This part captures the number of subtree data 82 | * The treecache is catured as treeleaf->local_tree_count. However, 83 | * the number of entries in the index file can be more due to the cache 84 | * also listing subtrees of the trees. Therefore, the number is, very 85 | * unfortunately, realloc()'ed in the loop. 86 | */ 87 | treeleaf->subtree = malloc(sizeof(struct subtree) * trees); 88 | for(s=0;ssubtree[s].path, (char *)indexmap + *offset, PATH_MAX); 90 | *offset = *offset + x + 1; 91 | 92 | treeleaf->subtree[s].entries = strtol((char *)indexmap + *offset, (char **)&endptr, 10); 93 | *offset = endptr - indexmap; 94 | treeleaf->subtree[s].sub_count = strtol((char *)indexmap + *offset, (char **)&endptr, 10); 95 | *offset = endptr - indexmap; 96 | 97 | /* Is this the best approach? The size of the trees grows */ 98 | trees+=treeleaf->subtree[s].sub_count; 99 | treeleaf->subtree = realloc(treeleaf->subtree, sizeof(struct subtree) * trees); 100 | 101 | /* Skip past the new line */ 102 | *offset=*offset+1; 103 | 104 | memcpy(treeleaf->subtree[s].sha, indexmap + *offset, HASH_SIZE/2); 105 | *offset = *offset + HASH_SIZE/2; 106 | } 107 | 108 | /* Assign the total number of subtrees in the cache */ 109 | treeleaf->total_tree_count = trees; 110 | 111 | return (treeleaf); 112 | } 113 | 114 | /* 115 | * Must free: dircleaf 116 | */ 117 | #if 0 118 | static struct dircleaf * 119 | read_dirc(unsigned char *indexmap, off_t *offset, int entries) 120 | { 121 | struct dircleaf *dircleaf; 122 | struct dircentry *dircentry; 123 | struct dircextentry *dircextentry; 124 | 125 | dircentry = (struct dircentry *)((char *)indexmap + *offset); 126 | dircleaf = malloc(sizeof(struct dircleaf) * entries); 127 | 128 | for(int i=0;iflags) & DIRC_EXT_FLAG) { 131 | dircextentry = (struct dircextentry *)((char *)indexmap + *offset); 132 | dircleaf[i].isextended = true; 133 | dircleaf[i].flags2 = dircextentry->flags & ~0x0fff; 134 | strlcpy(dircleaf[i].name, dircextentry->name, strlen(dircextentry->name)+1); 135 | 136 | *offset += (DIRCEXTENTRYSIZE + strlen(dircextentry->name)) & ~0x7; 137 | } 138 | else { 139 | dircleaf[i].isextended = false; 140 | strlcpy(dircleaf[i].name, dircentry->name, strlen(dircentry->name)+1); 141 | *offset += (DIRCENTRYSIZE + strlen(dircentry->name)) & ~0x7; 142 | } 143 | dircleaf[i].ctime_sec = dircentry->ctime_sec; 144 | dircleaf[i].ctime_nsec = dircentry->ctime_nsec; 145 | dircleaf[i].mtime_sec = dircentry->mtime_sec; 146 | dircleaf[i].mtime_nsec = dircentry->mtime_nsec; 147 | dircleaf[i].dev = dircentry->dev; 148 | dircleaf[i].ino = dircentry->ino; 149 | dircleaf[i].mode = dircentry->mode; 150 | dircleaf[i].uid = dircentry->uid; 151 | dircleaf[i].gid = dircentry->gid; 152 | dircleaf[i].size = dircentry->size; 153 | memcpy(dircleaf[i].sha, dircentry->sha, 20); 154 | /* 155 | * This line of code is commented out but left in. This is because git's 156 | * documentation erroneously has a 2-byte flag. However in practice this 157 | * is not used in git version 2.0 158 | */ 159 | // dircleaf[i].flags = dircentry->flags; 160 | } 161 | 162 | return (dircleaf); 163 | } 164 | #endif 165 | 166 | /* 167 | * Description: Reads and index file and stores the result in the 168 | * struct indextree. 169 | * Arguments: 1. indextree must be pre-allocated 170 | * 2. indexmap is a buffer to the index file, typically an 171 | * mmap(2) of the file 172 | * 3. indexsize is the size of the index file 173 | */ 174 | void 175 | index_parse(struct indextree *indextree, unsigned char *indexmap, off_t indexsize) 176 | { 177 | struct indexhdr *indexhdr; 178 | off_t offset = 0; 179 | unsigned extsize; 180 | 181 | indexhdr = (struct indexhdr *)((char *)indexmap + offset); 182 | if (memcmp(indexhdr->sig, "DIRC", 4)) { 183 | fprintf(stderr, "Does not match???\n"); 184 | exit(0); 185 | } 186 | indextree->version = htonl(indexhdr->version); 187 | indextree->entries = htonl(indexhdr->entries); 188 | 189 | offset += sizeof(struct indexhdr); 190 | // indextree->dircleaf = read_dirc(indexmap, &offset, indextree->entries); 191 | 192 | /* 193 | * This calculation is derived from GPL git 194 | * The 'HASH_SIZE/2 - 8' is based on the trailing hash length 195 | * and adding the padding of up to 8 characters. 196 | */ 197 | while (offset <= indexsize - HASH_SIZE/2 - 8) { 198 | indexhdr = (struct indexhdr *)((char *)indexmap + offset); 199 | 200 | /* Capture the extension size */ 201 | offset = offset + 4; 202 | memcpy(&extsize, indexmap+offset, 4); 203 | extsize = htonl(extsize); 204 | 205 | /* Jump past the 4-byte size and NULL character */ 206 | offset = offset + 5; 207 | 208 | /* 209 | * GNU git pre-converted "TREE" to a 4-byte value and uses a 210 | * switch-case. That might be every so slightly more efficient. 211 | */ 212 | if (!memcmp(indexhdr->sig, "TREE", 4)) 213 | /* Skip over the extension size and newline */ 214 | indextree->treeleaf = tree_entry(indexmap, &offset, extsize); 215 | else { 216 | fprintf(stderr, "Unknown data at the end of the index, exiting.\n"); 217 | exit(0); 218 | } 219 | } 220 | } 221 | 222 | /* 223 | * Writes the tree cache portion of the index file 224 | * Requires a populated indextree and index file descriptor 225 | * ToFree; Nothing 226 | */ 227 | static void 228 | write_tree(SHA1_CTX *indexctx, struct indextree *indextree, int indexfd) 229 | { 230 | int x; 231 | char tmp[100]; 232 | struct treeleaf *treeleaf = indextree->treeleaf; 233 | 234 | write_sha(indexctx, indexfd, "TREE", 4); 235 | 236 | x = htonl(treeleaf->ext_size); 237 | write_sha(indexctx, indexfd, &x, 4); 238 | 239 | write_sha(indexctx, indexfd, "\x00", 1); 240 | x = snprintf(tmp, 100, "%u %u\n", treeleaf->entry_count, treeleaf->local_tree_count); 241 | write_sha(indexctx, indexfd, tmp, x); 242 | 243 | write_sha(indexctx, indexfd, treeleaf->sha, HASH_SIZE/2); 244 | 245 | for(int s = 0;stotal_tree_count;s++) { 246 | x = snprintf(tmp, 100, "%s", treeleaf->subtree[s].path); 247 | write_sha(indexctx, indexfd, tmp, x); 248 | write_sha(indexctx, indexfd, "\x00", 1); 249 | x = snprintf(tmp, 100, "%u %u\n", treeleaf->subtree[s].entries, treeleaf->subtree[s].sub_count); 250 | write_sha(indexctx, indexfd, tmp, x); 251 | write_sha(indexctx, indexfd, treeleaf->subtree[s].sha, HASH_SIZE/2); 252 | } 253 | } 254 | 255 | /* 256 | * Writes the index file 257 | * Requires a populated indextree and index file descriptor 258 | * ToFree: Nothing 259 | */ 260 | void 261 | index_write(struct indextree *indextree, int indexfd) 262 | { 263 | struct dircleaf *dircleaf = indextree->dircleaf; 264 | SHA1_CTX indexctx; 265 | uint8_t sha[20]; 266 | uint32_t convert; 267 | char null = 0x00; 268 | int padding; 269 | uint32_t fourbyte; 270 | uint16_t twobyte; 271 | uint8_t onebyte; 272 | 273 | SHA1_Init(&indexctx); 274 | 275 | /* DIRC signature */ 276 | write_sha(&indexctx, indexfd, "DIRC", 4); 277 | /* Write version */ 278 | convert = htonl(indextree->version); 279 | write_sha(&indexctx, indexfd, &convert, 4); 280 | /* Write version */ 281 | convert = htonl(indextree->entries); 282 | write_sha(&indexctx, indexfd, &convert, 4); 283 | 284 | for(int i=0;ientries;i++) { 285 | fourbyte = htonl(dircleaf[i].ctime_sec); 286 | write_sha(&indexctx, indexfd, &fourbyte, 4); 287 | 288 | fourbyte = htonl(dircleaf[i].ctime_nsec); 289 | write_sha(&indexctx, indexfd, &fourbyte, 4); 290 | 291 | fourbyte = htonl(dircleaf[i].mtime_sec); 292 | write_sha(&indexctx, indexfd, &fourbyte, 4); 293 | 294 | fourbyte = htonl(dircleaf[i].mtime_nsec); 295 | write_sha(&indexctx, indexfd, &fourbyte, 4); 296 | 297 | fourbyte = htonl(dircleaf[i].dev); 298 | write_sha(&indexctx, indexfd, &fourbyte, 4); 299 | 300 | fourbyte = htonl(dircleaf[i].ino); 301 | write_sha(&indexctx, indexfd, &fourbyte, 4); 302 | 303 | fourbyte = htonl(dircleaf[i].mode); 304 | write_sha(&indexctx, indexfd, &fourbyte, 4); 305 | 306 | fourbyte = htonl(dircleaf[i].uid); 307 | write_sha(&indexctx, indexfd, &fourbyte, 4); 308 | 309 | fourbyte = htonl(dircleaf[i].gid); 310 | write_sha(&indexctx, indexfd, &dircleaf[i].gid, 4); 311 | 312 | fourbyte = htonl(dircleaf[i].size); 313 | write_sha(&indexctx, indexfd, &fourbyte, 4); 314 | 315 | for(int v=0;v> 4); 317 | write_sha(&indexctx, indexfd, &onebyte, 1); 318 | } 319 | twobyte = strlen(dircleaf[i].name); 320 | twobyte = htons(twobyte); 321 | write_sha(&indexctx, indexfd, &twobyte, 2); 322 | 323 | write_sha(&indexctx, indexfd, &dircleaf[i].name, strlen(dircleaf[i].name)); 324 | 325 | /* 326 | * This determines how much null character padding to add after the filename 327 | * Another possible function: 328 | * padding = 8 - ((strlen(dircleaf[i].name) + 6) % 8); 329 | */ 330 | padding = (0x7 & ~(strlen(dircleaf[i].name) - 2)) + 1; 331 | 332 | for(int z=0;ztreeleaf) 337 | write_tree(&indexctx, indextree, indexfd); 338 | 339 | /* Write the trailing SHA */ 340 | SHA1_Final((unsigned char *)sha, &indexctx); 341 | write(indexfd, sha, HASH_SIZE/2); 342 | } 343 | 344 | void 345 | index_generate_indextree(char *mode, uint8_t type, char *sha, char *filename, void *arg) 346 | { 347 | struct indexpath *indexpath = arg; 348 | struct indextree *indextree = indexpath->indextree; 349 | char *path = indexpath->path; 350 | char *fn = path + strlen(path); 351 | 352 | if (type == OBJ_TREE) { 353 | strlcat(path, filename, PATH_MAX); 354 | strlcat(path, "/", PATH_MAX); 355 | ITERATE_TREE(sha, index_generate_indextree, indexpath); 356 | } 357 | else { 358 | struct dircleaf *curleaf; 359 | struct stat sb; 360 | int ret; 361 | strlcat(path, filename, PATH_MAX); 362 | ret = stat(indexpath->fullpath, &sb); 363 | if (ret == -1) { 364 | fprintf(stderr, "Unable to generate index file, exiting.\n"); 365 | exit(ret); 366 | } 367 | indextree->dircleaf = realloc(indextree->dircleaf, sizeof(struct dircleaf) * (indextree->entries+1)); 368 | curleaf = &indextree->dircleaf[indextree->entries]; 369 | curleaf->isextended = 0; 370 | curleaf->ctime_sec = sb.st_ctime; 371 | curleaf->ctime_nsec = sb.st_ctim.tv_nsec; 372 | curleaf->mtime_sec = sb.st_mtime; 373 | curleaf->mtime_nsec = sb.st_mtim.tv_nsec; 374 | curleaf->dev = sb.st_dev; 375 | curleaf->ino = sb.st_ino; 376 | curleaf->mode = sb.st_mode; 377 | curleaf->uid = sb.st_uid; 378 | curleaf->gid = sb.st_gid; 379 | curleaf->size = sb.st_size; 380 | /* SHA assigner would go here */ 381 | curleaf->flags = 0x0000; 382 | curleaf->flags2 = 0x0000; 383 | 384 | sha_str_to_bin(sha, curleaf->sha); 385 | strlcpy(curleaf->name, path, PATH_MAX); 386 | 387 | indextree->entries++; 388 | } 389 | *fn = '\0'; 390 | } 391 | 392 | /* 393 | * This function recursively iterates through a TREE to produce the data 394 | * necessary to write the index. 395 | * ToFree after function: treeleaf->subtree 396 | */ 397 | void 398 | index_generate_treedata(char *mode, uint8_t type, char *sha, char *filename, void *arg) 399 | { 400 | struct indexpath *indexpath = arg; 401 | struct indextree *indextree = indexpath->indextree; 402 | struct treeleaf *treeleaf = indextree->treeleaf; 403 | struct subtree *next_tree; 404 | 405 | if (type == OBJ_TREE) { 406 | int local_position, next_position; 407 | 408 | local_position = indexpath->current_position; 409 | treeleaf->total_tree_count++; 410 | treeleaf->subtree = realloc(treeleaf->subtree, 411 | sizeof(struct subtree)*(treeleaf->total_tree_count)); 412 | next_tree=&treeleaf->subtree[treeleaf->total_tree_count-1]; 413 | next_tree->entries=0; 414 | next_tree->sub_count=0; 415 | sha_str_to_bin_network(sha, next_tree->sha); 416 | strlcpy(next_tree->path, filename, PATH_MAX); 417 | 418 | indexpath->current_position = next_position = treeleaf->total_tree_count; 419 | treeleaf->subtree[local_position-1].sub_count++; 420 | ITERATE_TREE(sha, index_generate_treedata, indexpath); 421 | indexpath->current_position = local_position; 422 | 423 | if (local_position > 0) 424 | treeleaf->subtree[local_position-1].entries += treeleaf->subtree[next_position-1].entries; 425 | else 426 | treeleaf->local_tree_count++; 427 | } 428 | else { 429 | treeleaf->entry_count++; 430 | if (indexpath->current_position > 0) { 431 | next_tree = &treeleaf->subtree[indexpath->current_position-1]; 432 | next_tree->entries++; 433 | } 434 | } 435 | } 436 | 437 | /* 438 | * Calculate the size of the tree extension 439 | * The TREE header size is calculated with two parts. 440 | * 1. The main tree part: 441 | * - The initial NULL char (1 byte) 442 | * - The length of the entry_count in ASCII (variable size) 443 | * - The space character (1 byte) 444 | * - The length of the total_tree_count in ASCII (variable size) 445 | * - The newline char (1 byte) 446 | * - The SHA in bin format (20 bytes) 447 | * 2. The subtree sections: 448 | * - The size of the tree name (variable size) 449 | * - The closing NULL character (1 byte) 450 | * - The size of the entries in ASCII (variable size) 451 | * - The space character (1 byte) 452 | * - The size of the subtrees in ASCII (variable size) 453 | * - The newline char (1 byte) 454 | * - The SHA in bin format (20 bytes) 455 | * I slightly rearranged these additions out of order, but the sum is the same 456 | * */ 457 | void 458 | index_calculate_tree_ext_size(struct treeleaf *treeleaf) 459 | { 460 | treeleaf->ext_size += count_digits(treeleaf->entry_count) + count_digits(treeleaf->total_tree_count); 461 | treeleaf->ext_size += EXT_SIZE_FIXED; 462 | 463 | for(int r=0;rtotal_tree_count;r++) { 464 | treeleaf->ext_size += strlen(treeleaf->subtree[r].path); 465 | treeleaf->ext_size += count_digits(treeleaf->subtree[r].entries); 466 | treeleaf->ext_size += count_digits(treeleaf->subtree[r].sub_count); 467 | treeleaf->ext_size += EXT_SIZE_FIXED; 468 | } 469 | } 470 | -------------------------------------------------------------------------------- /lib/index.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef INDEX_H 29 | #define INDEX_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include "common.h" 35 | 36 | /* Header source Documentation/technical/index-format.txt */ 37 | 38 | #define NULLCHAR 1 39 | #define SPACECHAR 1 40 | #define NEWLINE 1 41 | #define EXT_SIZE_FIXED NULLCHAR + SPACECHAR + NEWLINE + HASH_SIZE/2 42 | 43 | struct indexhdr { 44 | char sig[4]; /* Cache type */ 45 | uint32_t version; /* Version Number */ 46 | uint32_t entries; /* Number of extensions */ 47 | }; 48 | 49 | struct dircentry { 50 | uint32_t ctime_sec; 51 | uint32_t ctime_nsec; 52 | uint32_t mtime_sec; 53 | uint32_t mtime_nsec; 54 | uint32_t dev; 55 | uint32_t ino; 56 | uint32_t mode; 57 | uint32_t uid; 58 | uint32_t gid; 59 | uint32_t size; 60 | uint8_t sha[HASH_SIZE/2]; 61 | uint16_t flags; 62 | #define DIRCENTRYSIZE 70 63 | char name[1]; 64 | } __packed; 65 | 66 | #define DIRC_EXT_FLAG BIT(14) 67 | 68 | struct dircextentry { 69 | uint32_t ctime_sec; 70 | uint32_t ctime_nsec; 71 | uint32_t mtime_sec; 72 | uint32_t mtime_nsec; 73 | uint32_t dev; 74 | uint32_t ino; 75 | uint32_t mode; 76 | uint32_t uid; 77 | uint32_t gid; 78 | uint32_t size; 79 | uint8_t sha[20]; 80 | uint16_t flags; 81 | uint16_t flags2; 82 | #define DIRCEXTENTRYSIZE 72 83 | char name[1]; 84 | } __packed; 85 | 86 | struct dircleaf { 87 | bool isextended; 88 | uint32_t ctime_sec; 89 | uint32_t ctime_nsec; 90 | uint32_t mtime_sec; 91 | uint32_t mtime_nsec; 92 | uint32_t dev; 93 | uint32_t ino; 94 | uint32_t mode; 95 | uint32_t uid; 96 | uint32_t gid; 97 | uint32_t size; 98 | uint8_t sha[HASH_SIZE/2]; 99 | uint16_t flags; 100 | uint16_t flags2; /* Only for the extended type */ 101 | char name[PATH_MAX]; 102 | }; 103 | 104 | struct subtree { 105 | char path[PATH_MAX]; 106 | uint8_t sha[HASH_SIZE/2]; 107 | int entries; 108 | int sub_count; 109 | }; 110 | 111 | struct treeleaf { 112 | int ext_size; 113 | uint8_t sha[HASH_SIZE/2]; 114 | unsigned int entry_count; 115 | unsigned int local_tree_count; 116 | unsigned int total_tree_count; 117 | struct subtree *subtree; 118 | }; 119 | 120 | #define INDEX_VERSION_2 0x2 121 | 122 | struct indextree { 123 | int version; 124 | int entries; 125 | struct dircleaf *dircleaf; 126 | struct treeleaf *treeleaf; 127 | }; 128 | 129 | /* 130 | * This is used to facilitate converting 131 | * a tree object to an indextree 132 | */ 133 | struct indexpath { 134 | struct indextree *indextree; 135 | char *fullpath; 136 | char *path; 137 | 138 | int current_position; 139 | }; 140 | 141 | void index_parse(struct indextree *indextree, unsigned char *indexmap, off_t indexsize); 142 | void index_write(struct indextree *indextree, int indexfd); 143 | void index_generate_indextree(char *mode, uint8_t type, char *sha, char *filename, void *arg); 144 | void index_generate_treedata(char *mode, uint8_t type, char *sha, char *filename, void *arg); 145 | void index_calculate_tree_ext_size(struct treeleaf *treeleaf); 146 | 147 | #endif 148 | -------------------------------------------------------------------------------- /lib/ini.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "common.h" 38 | #include "ini.h" 39 | 40 | static regex_t re_core_header; 41 | static regex_t re_remote_header; 42 | static regex_t re_variable; 43 | 44 | struct section *sections = NULL; 45 | 46 | int 47 | config_parser() 48 | { 49 | FILE* fp; 50 | char line[1000]; 51 | regmatch_t pmatch[10]; 52 | struct section *current_section = sections; 53 | struct section *new_section; 54 | char ini_file[PATH_MAX]; 55 | int sz; 56 | 57 | char tmp[1000]; 58 | char tmpvar[1000]; 59 | char *tmpval; 60 | 61 | ini_init_regex(); 62 | 63 | snprintf(ini_file, PATH_MAX, "%s/config", dotgitpath); 64 | fp = fopen(ini_file, "r"); 65 | if (!fp) { 66 | printf("Unable to open file: %s\n", ini_file); 67 | return (-1); 68 | } 69 | 70 | while (fgets(line, sizeof(line), fp) != NULL) { 71 | line[strlen(line)-1] = '\0'; // chomp() 72 | 73 | if (regexec(&re_core_header, line, 2, pmatch, 0) != REG_NOMATCH || 74 | regexec(&re_remote_header, line, 4, pmatch, 0) != REG_NOMATCH) { 75 | new_section = calloc(1, sizeof(struct section)); 76 | new_section->logallrefupdates = 0xFF; 77 | 78 | strlcpy(tmp, line + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so); 79 | if (strncmp(tmp, "core", 4) == 0) { 80 | new_section->type = CORE; 81 | } 82 | else if (strncmp(tmp, "remote", 6) == 0) { 83 | new_section->type = REMOTE; 84 | sz = pmatch[2].rm_eo - pmatch[2].rm_so; 85 | new_section->repo_name = malloc(sz + 1); 86 | strlcpy(new_section->repo_name, line + pmatch[2].rm_so, 87 | sz + 1); 88 | } 89 | 90 | new_section->next = NULL; 91 | if (sections == NULL) { 92 | current_section = sections = new_section; 93 | } else { 94 | current_section->next = new_section; 95 | current_section = new_section; 96 | } 97 | 98 | continue; 99 | } 100 | /* Capture variables */ 101 | else if (regexec(&re_variable, line, 3, pmatch, 0) != REG_NOMATCH) { 102 | 103 | if (current_section == NULL) { 104 | fprintf(stderr, 105 | "Parsing error, file did not start with a " 106 | "section header\n"); 107 | exit(1); 108 | } 109 | 110 | strlcpy(tmpvar, 111 | line + pmatch[1].rm_so, 112 | pmatch[1].rm_eo - pmatch[1].rm_so); 113 | 114 | tmpval = malloc(pmatch[2].rm_eo - pmatch[2].rm_so + 1); 115 | strlcpy(tmpval, 116 | line + pmatch[2].rm_so, 117 | pmatch[2].rm_eo - pmatch[2].rm_so); 118 | 119 | tmpval[pmatch[2].rm_eo - pmatch[2].rm_so] = '\0'; 120 | 121 | /* Matches for Core */ 122 | if (!strncmp(tmpvar, "repositoryformatversion", 23)) { 123 | current_section->repositoryformatversion = atoi(tmpval); 124 | free(tmpval); 125 | } 126 | else if (!strncmp("filemode", tmpvar, 8)) { 127 | if (!strncmp(tmpval, "true", 4)) 128 | current_section->filemode = TRUE; 129 | else if (!strncmp(tmpval, "false", 5)) 130 | current_section->filemode = FALSE; 131 | free(tmpval); 132 | } 133 | else if (!strncmp(tmpvar, "bare", 4)) { 134 | if (!strncmp(tmpval, "true", 4)) 135 | current_section->bare = TRUE; 136 | else if (!strncmp(tmpval, "false", 5)) 137 | current_section->bare = TRUE; 138 | free(tmpval); 139 | } 140 | else if (!strncmp("logallrefupdates", tmpvar, 16)) { 141 | if (!strncmp(tmpval, "true", 4)) 142 | current_section->logallrefupdates = TRUE; 143 | else if (!strncmp(tmpval, "false", 5)) 144 | current_section->logallrefupdates = FALSE; 145 | free(tmpval); 146 | } 147 | /* Matches for Remote */ 148 | else if (strncmp("url", tmpvar, 3) == 0) 149 | current_section->url = tmpval; 150 | else if (strncmp("fetch", tmpvar, 5) == 0) 151 | current_section->fetch = tmpval; 152 | else 153 | free(tmpval); 154 | continue; 155 | } 156 | } 157 | 158 | return (0); 159 | } 160 | 161 | void 162 | ini_write_config(int fd, struct section *sections) 163 | { 164 | struct section *cur_section = sections; 165 | 166 | while (cur_section) { 167 | if (cur_section->type == CORE) { 168 | dprintf(fd, "[core]\n"); 169 | if (cur_section->repositoryformatversion != 0xFF) 170 | dprintf(fd, "\trepositoryformatversion = %d\n", 171 | cur_section->repositoryformatversion); 172 | if (cur_section->filemode) 173 | dprintf(fd, "\tfilemode = %s\n", 174 | (cur_section->filemode == TRUE ? "true" : "false")); 175 | if (cur_section->bare) 176 | dprintf(fd, "\tbare = %s\n", 177 | (cur_section->bare == TRUE ? "true" : "false")); 178 | if (cur_section->logallrefupdates != 0xFF) 179 | dprintf(fd, "\tlogallrefupdates = %s\n", 180 | (cur_section->logallrefupdates == TRUE ? "true" : "false")); 181 | } 182 | else if (cur_section->type == REMOTE) { 183 | dprintf(fd, "[remote \"%s\"]\n", cur_section->repo_name); 184 | if (cur_section->url) 185 | dprintf(fd, "\turl = %s\n", cur_section->url); 186 | if (cur_section->fetch) 187 | dprintf(fd, "\tfetch = %s\n", cur_section->fetch); 188 | } 189 | else if (cur_section->type == BRANCH) { 190 | dprintf(fd, "[branch \"%s\"]\n", cur_section->repo_name); 191 | if (cur_section->remote) 192 | dprintf(fd, "\tremote = %s\n", cur_section->remote); 193 | if (cur_section->merge) 194 | dprintf(fd, "\tmerge = %s\n", cur_section->merge); 195 | } 196 | cur_section = cur_section->next; 197 | } 198 | } 199 | 200 | void 201 | ini_init_regex() 202 | { 203 | regcomp(&re_core_header, "^\\[(core)\\]", REG_EXTENDED); 204 | regcomp(&re_remote_header, "^\\[(remote) \"([a-zA-Z0-9_]+)\"\\]", REG_EXTENDED); 205 | regcomp(&re_variable, "([A-Za-z0-9_]+)[\\s ]*=[\\s ]*([A-Za-z0-9_$&+,:;=?@#|'<>.^*()%!-/]+)", REG_EXTENDED); 206 | } 207 | -------------------------------------------------------------------------------- /lib/ini.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef _INI_H_ 29 | #define _INI_H_ 30 | 31 | enum section_types { 32 | CORE = 1, 33 | REMOTE = 2, 34 | BRANCH = 3, 35 | OTHER = 99 36 | }; 37 | 38 | enum boolean { 39 | NOT_SET = 0, 40 | TRUE = 1, 41 | FALSE = 2 42 | }; 43 | 44 | // XXX Could this be made a union? 45 | struct section { 46 | enum section_types type; 47 | 48 | /* Used by main */ 49 | int repositoryformatversion; 50 | enum boolean filemode; 51 | enum boolean bare; 52 | enum boolean logallrefupdates; 53 | 54 | /* Used by remote */ 55 | char * repo_name; 56 | char * url; 57 | char * fetch; 58 | 59 | /* Used by branch */ 60 | char * remote; 61 | char * merge; 62 | 63 | /* Other */ 64 | char * other_header_name; 65 | char * other_variable; 66 | char * other_value; 67 | 68 | struct section *next; 69 | }; 70 | 71 | extern struct section *sections; 72 | 73 | int config_parser(); 74 | void ini_init_regex(); 75 | void ini_write_config(int fd, struct section *sections); 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /lib/loose.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "zlib-handler.h" 38 | #include "common.h" 39 | #include "loose.h" 40 | #include "pack.h" 41 | 42 | /* 43 | * Provides a generic way to parse loose content 44 | * This is used to parse data in multiple ways. 45 | * Similar to pack_content_handler 46 | */ 47 | int 48 | loose_content_handler(char *sha, inflated_handler inflated_handler, void *iarg) 49 | { 50 | char objectpath[PATH_MAX]; 51 | int objectfd; 52 | 53 | snprintf(objectpath, sizeof(objectpath), "%s/objects/%c%c/%s", dotgitpath, sha[0], sha[1], sha+2); 54 | objectfd = open(objectpath, O_RDONLY); 55 | if (objectfd == -1) 56 | return (1); 57 | 58 | deflate_caller(objectfd, NULL, NULL, inflated_handler, iarg); 59 | 60 | return (0); 61 | } 62 | 63 | /* 64 | * Description: Recover lose object headers 65 | */ 66 | int 67 | loose_get_headers(unsigned char *buf, int size, void *arg) 68 | { 69 | struct loosearg *loosearg = arg; 70 | unsigned char *endptr; 71 | int hdr_offset = 0; 72 | 73 | // Is there a cleaner way to do this? 74 | if (!memcmp(buf, "commit ", 7)) { 75 | loosearg->type = OBJ_COMMIT; 76 | loosearg->size = strtol((char *)buf + 7, (char **)&endptr, 10); 77 | hdr_offset = 8 + (endptr - (buf+7)); 78 | } 79 | else if (!memcmp(buf, "tree ", 5)) { 80 | loosearg->type = OBJ_TREE; 81 | loosearg->size = strtol((char *)buf + 5, (char **)&endptr, 10); 82 | hdr_offset = 6 + (endptr - (buf+5)); 83 | } 84 | else if (!memcmp(buf, "blob ", 5)) { 85 | loosearg->type = OBJ_BLOB; 86 | loosearg->size = strtol((char *)buf + 5, (char **)&endptr, 10); 87 | hdr_offset = 1 + (endptr - buf); 88 | } 89 | else if (!memcmp(buf, "tag", 3)) { 90 | loosearg->type = OBJ_TAG; 91 | hdr_offset = 3; 92 | } 93 | else if (!memcmp(buf, "obj_ofs_delta", 13)) { 94 | loosearg->type = OBJ_REF_DELTA; 95 | hdr_offset = 15; 96 | } 97 | else if (!memcmp(buf, "obj_ref_delta", 13)) { 98 | loosearg->type = OBJ_REF_DELTA; 99 | hdr_offset = 15; 100 | } else { 101 | loosearg->type = OBJ_UNKNOWN; 102 | } 103 | 104 | return (hdr_offset); 105 | } 106 | 107 | 108 | 109 | 110 | /* 111 | * Description: Just gets the type of a loose object 112 | * Handler for loose_content_handler 113 | */ 114 | unsigned char * 115 | get_type_loose_cb(unsigned char *buf, int size, int __unused deflated_bytes, void *arg) 116 | { 117 | uint8_t *type = arg; 118 | struct loosearg loosearg; 119 | 120 | loose_get_headers(buf, size, &loosearg); 121 | *type = loosearg.type; 122 | return (buf); 123 | } 124 | -------------------------------------------------------------------------------- /lib/loose.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | 29 | #ifndef LOOSE_H 30 | #define LOOSE_H 31 | 32 | #include "zlib-handler.h" 33 | 34 | struct loosearg { 35 | int fd; 36 | uint8_t cmd; 37 | int step; 38 | long sent; 39 | 40 | int type; 41 | long size; 42 | 43 | char *sha; 44 | struct decompressed_object decompressed_object; 45 | }; 46 | 47 | int loose_get_headers(unsigned char *buf, int size, void *arg); 48 | int loose_content_handler(char *sha, inflated_handler inflated_handler, void *iarg); 49 | unsigned char *get_type_loose_cb(unsigned char *buf, int size, int __unused deflated_bytes, void *arg); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /lib/pack.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include "buffering.h" 41 | #include "zlib-handler.h" 42 | #include "pack.h" 43 | #include "common.h" 44 | #include "ini.h" 45 | 46 | /* 47 | * This function is a wrapper for write(1) and also updates 48 | * a SHA context the data 49 | */ 50 | ssize_t 51 | sha_write(int fd, const void *buf, size_t nbytes, SHA1_CTX *idxctx) 52 | { 53 | SHA1_Update(idxctx, buf, nbytes); 54 | return (write(fd, buf, nbytes)); 55 | } 56 | 57 | void 58 | write_pack_cb(int packfd, struct objectinfo *objectinfo, void *pargs) 59 | { 60 | struct writer_args *writer_args = pargs; 61 | if (objectinfo->ptype != OBJ_OFS_DELTA) { 62 | lseek(packfd, objectinfo->offset + objectinfo->used, SEEK_SET); 63 | deflate_caller(packfd, NULL, NULL, write_cb, writer_args); 64 | } 65 | else { 66 | pack_delta_content(packfd, objectinfo, NULL); 67 | write(writer_args->fd, objectinfo->data, objectinfo->isize); 68 | free(objectinfo->data); 69 | free(objectinfo->deltas); 70 | } 71 | } 72 | 73 | void 74 | get_type_pack_cb(int packfd, struct objectinfo *objectinfo, void *pargs) 75 | { 76 | uint8_t *type = pargs; 77 | *type = objectinfo->ftype; 78 | } 79 | 80 | 81 | int 82 | read_sha_update(void *buf, size_t count, void *arg) 83 | { 84 | if (!arg) 85 | return (1); 86 | SHA1_CTX *context = arg; 87 | SHA1_Update(context, buf, count); 88 | 89 | return (0); 90 | } 91 | 92 | int 93 | sortindexentry(const void *a, const void *b) 94 | { 95 | struct index_entry *x = (struct index_entry *)a; 96 | struct index_entry *y = (struct index_entry *)b; 97 | return (memcmp(x->digest, y->digest, 20)); 98 | } 99 | 100 | unsigned long 101 | readvint(unsigned char **datap, unsigned char *top) 102 | { 103 | unsigned char *data = *datap; 104 | unsigned long opcode, size = 0; 105 | int i = 0; 106 | do { 107 | opcode = *data++; 108 | size |= (opcode & 0x7f) << i; 109 | i += 7; 110 | } while (opcode & BIT(7) && data < top); 111 | *datap = data; 112 | return (size); 113 | } 114 | 115 | void 116 | applypatch(struct decompressed_object *base, struct decompressed_object *delta, struct objectinfo *objectinfo) 117 | { 118 | unsigned long size; 119 | unsigned char *data, *top; 120 | unsigned char *out, opcode; 121 | unsigned long cp_off, cp_size; 122 | 123 | data = delta->data; 124 | top = delta->data + delta->size; 125 | 126 | size = readvint(&data, top); 127 | if (size != base->size) { 128 | fprintf(stderr, "Error, bad original set\n"); 129 | exit(0); 130 | } 131 | 132 | size = readvint(&data, top); 133 | 134 | objectinfo->data = malloc(size); 135 | objectinfo->isize = size; 136 | out = objectinfo->data; 137 | 138 | while (data < top) { 139 | opcode = *data++; 140 | if (opcode & BIT(7)) { 141 | cp_off = 0; 142 | cp_size = 0; 143 | 144 | /* Offset in base */ 145 | if (opcode & BIT(0)) 146 | cp_off |= (*data++ << 0); 147 | if (opcode & BIT(1)) 148 | cp_off |= (*data++ << 8); 149 | if (opcode & BIT(2)) 150 | cp_off |= (*data++ << 16); 151 | if (opcode & BIT(3)) 152 | cp_off |= (*data++ << 24); 153 | 154 | /* Length to copy */ 155 | if (opcode & BIT(4)) 156 | cp_size |= (*data++ << 0); 157 | if (opcode & BIT(5)) 158 | cp_size |= (*data++ << 8); 159 | if (opcode & BIT(6)) 160 | cp_size |= (*data++ << 16); 161 | 162 | if (cp_size == 0) 163 | cp_size = 0x10000; 164 | 165 | if (cp_off + cp_size > base->size) { // || cp_size > size) { 166 | fprintf(stderr, "Bad length: First\n"); 167 | exit(0); 168 | } 169 | memcpy(out, (char *) base->data + cp_off, cp_size); 170 | out += cp_size; 171 | } else if (opcode) { 172 | memcpy(out, data, opcode); 173 | out += opcode; 174 | data += opcode; 175 | } else { 176 | fprintf(stderr, "Unexpected opcode 0x00, exiting.\n"); 177 | exit(0); 178 | } 179 | } 180 | } 181 | 182 | /* 183 | * Identifies the header information of each object in the pack file. 184 | * If will identify the following: 185 | * - The object type in objectinfo.ptype 186 | * - If its an OBJ_OFS_DELTA: 187 | * - The real objectype in objectinfo.ftype 188 | * - The delta offsets to reconstruct the object 189 | * - The start of the objects offset (not the header) 190 | */ 191 | 192 | int 193 | pack_get_object_meta(int packfd, int offset, struct packfileinfo *packfileinfo, 194 | struct index_entry *index_entry, 195 | SHA1_CTX *packctx, SHA1_CTX *idxctx) 196 | { 197 | int x; 198 | struct objectinfo objectinfo; 199 | struct index_generate_arg index_generate_arg; 200 | char hdr[32]; 201 | int hdrlen; 202 | // Bytef tmpref[2]; 203 | struct two_darg two_darg; 204 | 205 | for (x = 0; x < packfileinfo->nobjects; x++) { 206 | objectinfo.crc = 0x00; 207 | lseek(packfd, offset, SEEK_SET); 208 | pack_object_header(packfd, offset, &objectinfo, packctx); 209 | 210 | switch (objectinfo.ptype) { 211 | case OBJ_REF_DELTA: 212 | fprintf(stderr, "OBJ_REF_DELTA: currently not implemented. Exiting.\n"); 213 | exit(0); 214 | // offset += objectinfo.used; 215 | // lseek(packfd, offset, SEEK_SET); 216 | // buf_read(packfd, tmpref, 2, read_sha_update, packctx); 217 | // objectinfo.crc = crc32(objectinfo.crc, tmpref, 2); 218 | // buf_read(packfd, index_entry[x].digest, 20, 219 | // read_sha_update, packctx); 220 | // offset += 22; /* 20 bytes + 2 for the header */ 221 | // break; 222 | case OBJ_OFS_DELTA: 223 | SHA1_Init(&index_generate_arg.shactx); 224 | pack_delta_content(packfd, &objectinfo, packctx); 225 | hdrlen = snprintf(hdr, sizeof(hdr), "%s %lu", 226 | object_name[objectinfo.ftype], 227 | objectinfo.isize) + 1; 228 | SHA1_Update(&index_generate_arg.shactx, hdr, hdrlen); 229 | SHA1_Update(&index_generate_arg.shactx, 230 | objectinfo.data, objectinfo.isize); 231 | SHA1_Final(index_entry[x].digest, 232 | &index_generate_arg.shactx); 233 | /* The next two are allocated in pack_delta_content */ 234 | free(objectinfo.data); 235 | free(objectinfo.deltas); 236 | 237 | offset = objectinfo.offset + objectinfo.used + 238 | objectinfo.ofshdrsize + objectinfo.deflated_size; 239 | break; 240 | case OBJ_COMMIT: 241 | case OBJ_TREE: 242 | case OBJ_BLOB: 243 | case OBJ_TAG: 244 | default: 245 | offset += objectinfo.used; 246 | lseek(packfd, offset, SEEK_SET); 247 | index_generate_arg.bytes = 0; 248 | SHA1_Init(&index_generate_arg.shactx); 249 | 250 | hdrlen = snprintf(hdr, sizeof(hdr), "%s %lu", 251 | object_name[objectinfo.ftype], 252 | objectinfo.psize) + 1; 253 | SHA1_Update(&index_generate_arg.shactx, hdr, hdrlen); 254 | two_darg.crc = &objectinfo.crc; 255 | two_darg.sha = packctx; 256 | deflate_caller(packfd, zlib_update_crc_sha, &two_darg, 257 | pack_get_index_bytes_cb, &index_generate_arg); 258 | 259 | SHA1_Final(index_entry[x].digest, 260 | &index_generate_arg.shactx); 261 | offset += index_generate_arg.bytes; 262 | break; 263 | } 264 | 265 | index_entry[x].crc = objectinfo.crc; 266 | index_entry[x].offset = objectinfo.offset; 267 | } 268 | 269 | return (offset); 270 | } 271 | 272 | /* 273 | * Writes the header of the index file. 274 | */ 275 | inline void 276 | write_index_header(int idxfd, SHA1_CTX *idxctx) 277 | { 278 | sha_write(idxfd, "\377tOc", 4, idxctx); 279 | sha_write(idxfd, "\x00\x00\x00\x02", 4, idxctx); 280 | } 281 | 282 | inline void 283 | write_hash_count(int idxfd, struct index_entry *index_entry, 284 | SHA1_CTX *idxctx) 285 | { 286 | int hashnum; 287 | int reversed; 288 | int x; 289 | 290 | hashnum = 0; 291 | 292 | for (x=0;x<256;x++) { 293 | while (index_entry[hashnum].digest[0] == x) 294 | hashnum++; 295 | reversed = htonl(hashnum); 296 | sha_write(idxfd, &reversed, 4, idxctx); 297 | } 298 | } 299 | 300 | inline void 301 | write_hashes(int idxfd, struct packfileinfo *packfileinfo, 302 | struct index_entry *index_entry, SHA1_CTX *idxctx) 303 | { 304 | int x; 305 | 306 | for (x=0;xnobjects; x++) 307 | sha_write(idxfd, index_entry[x].digest, 20, idxctx); 308 | } 309 | 310 | inline void 311 | write_crc_table(int idxfd, struct packfileinfo *packfileinfo, 312 | struct index_entry *index_entry, SHA1_CTX *idxctx) 313 | { 314 | uint32_t crc32tmp; 315 | int x; 316 | 317 | for (x=0;xnobjects;x++) { 318 | crc32tmp = htonl(index_entry[x].crc); 319 | sha_write(idxfd, &crc32tmp, 4, idxctx); 320 | } 321 | } 322 | 323 | inline void 324 | write_32bit_table(int idxfd, struct packfileinfo *packfileinfo, 325 | struct index_entry *index_entry, SHA1_CTX *idxctx) 326 | { 327 | uint64_t offsettmp; 328 | int x; 329 | 330 | for (x = 0; x < packfileinfo->nobjects; x++) { 331 | offsettmp = htonl(index_entry[x].offset); 332 | sha_write(idxfd, &offsettmp, 4, idxctx); 333 | } 334 | } 335 | 336 | inline void 337 | write_checksums(int idxfd, struct packfileinfo *packfileinfo, SHA1_CTX *idxctx) 338 | { 339 | sha_write(idxfd, packfileinfo->sha, 20, idxctx); 340 | SHA1_Final(packfileinfo->ctx, idxctx); 341 | sha_write(idxfd, packfileinfo->ctx, 20, idxctx); 342 | } 343 | 344 | /* 345 | * Builds the idx file, combines the functions above. 346 | */ 347 | void 348 | pack_build_index(int idxfd, struct packfileinfo *packfileinfo, 349 | struct index_entry *index_entry, SHA1_CTX *idxctx) 350 | { 351 | /* Write pack header */ 352 | write_index_header(idxfd, idxctx); 353 | /* Writing hash count */ 354 | write_hash_count(idxfd, index_entry, idxctx); 355 | /* Writing hashes */ 356 | write_hashes(idxfd, packfileinfo, index_entry, idxctx); 357 | /* Write the crc32 table */ 358 | write_crc_table(idxfd, packfileinfo, index_entry, idxctx); 359 | /* Write the 32-bit offset table */ 360 | write_32bit_table(idxfd, packfileinfo, index_entry, idxctx); 361 | /* Currently does not write large files */ 362 | /* Write the SHA1 checksum of the corresponding packfile */ 363 | write_checksums(idxfd, packfileinfo, idxctx); 364 | } 365 | 366 | /* 367 | * This function reassembles an OBJ_OFS_DELTA object. It requires that the 368 | * objectinfo variable has already passed through pack_object_header. 369 | * It starts by inflating the base object. Then, it loops through each delta 370 | * by offset, inflates the contents and applies the patch. 371 | * 372 | * Note: This is a memory-extensive function, as it requires a copy of the base 373 | * object and fully patched object in memory at once and I cannot think of any 374 | * way to only have one object in memory at once. 375 | * 376 | * This function also calculates the crc32 value. The code is not as clean as 377 | * it should be, but this is the best approach I had without using a file-scope 378 | * variable, which is GPL git's approach. 379 | */ 380 | void 381 | pack_delta_content(int packfd, struct objectinfo *objectinfo, SHA1_CTX *packctx) 382 | { 383 | struct decompressed_object base_object, delta_object; 384 | int q; 385 | 386 | base_object.data = NULL; 387 | base_object.size = 0; 388 | base_object.deflated_size = 0; 389 | 390 | /* 391 | * Prevent a potential condition where this 392 | * value is assigned without being used. 393 | */ 394 | delta_object.deflated_size = 0; 395 | 396 | lseek(packfd, objectinfo->ofsbase, SEEK_SET); 397 | deflate_caller(packfd, NULL, NULL, buffer_cb, &base_object); 398 | objectinfo->deflated_size = base_object.deflated_size; 399 | 400 | for (q=objectinfo->ndeltas-1;q>=0;q--) { 401 | lseek(packfd, objectinfo->deltas[q], SEEK_SET); 402 | 403 | delta_object.data = NULL; 404 | delta_object.size = 0; 405 | delta_object.deflated_size = 0; 406 | /* Only calculate the crc32 for the first iteration */ 407 | if (q == 0) { 408 | struct two_darg two_darg; 409 | two_darg.crc = &objectinfo->crc; 410 | two_darg.sha = packctx; 411 | deflate_caller(packfd, zlib_update_crc_sha, &two_darg, buffer_cb, &delta_object); 412 | } 413 | else 414 | deflate_caller(packfd, NULL, NULL, buffer_cb, &delta_object); 415 | 416 | 417 | applypatch(&base_object, &delta_object, objectinfo); 418 | free(base_object.data); 419 | free(delta_object.data); 420 | base_object.data = objectinfo->data; 421 | base_object.size = objectinfo->isize; 422 | } 423 | 424 | /* 425 | * This instance of deflated_size is the 0th in the list which 426 | * means it is the deflated_size of the current OBJ_OFS_DELTA, 427 | * not of the parent deltas. 428 | */ 429 | objectinfo->deflated_size = delta_object.deflated_size; 430 | 431 | } 432 | 433 | /* Used by index-pack to compute SHA and get offset bytes */ 434 | unsigned char * 435 | pack_get_index_bytes_cb(unsigned char *buf, int size, int deflated_bytes, void *arg) 436 | { 437 | struct index_generate_arg *index_generate_arg = arg; 438 | SHA1_Update(&index_generate_arg->shactx, buf, size); 439 | index_generate_arg->bytes += deflated_bytes; 440 | return (buf); 441 | } 442 | 443 | /* Gets the offset and filename prefix based on the SHA */ 444 | int 445 | pack_get_packfile_offset(char *sha_str, char *filename) 446 | { 447 | DIR *d; 448 | struct dirent *dir; 449 | char packdir[PATH_MAX]; 450 | int packfd; 451 | char *file_ext; 452 | unsigned char *idxmap; 453 | struct stat sb; 454 | int offset = 0; 455 | uint8_t sha_bin[20]; 456 | int i; 457 | 458 | for (i=0;i<20;i++) 459 | sscanf(sha_str+i*2, "%2hhx", &sha_bin[i]); 460 | 461 | snprintf(packdir, sizeof(packdir), "%s/objects/pack", dotgitpath); 462 | d = opendir(packdir); 463 | 464 | /* Find hash in idx file or die */ 465 | if (d) { 466 | while ((dir = readdir(d)) != NULL) { 467 | file_ext = strrchr(dir->d_name, '.'); 468 | if (!file_ext || strncmp(file_ext, ".idx", 4)) 469 | continue; 470 | /* XXX This is wrong; we need to know the size of the buffer */ 471 | snprintf(filename, PATH_MAX, "%s/objects/pack/%s", dotgitpath, 472 | dir->d_name); 473 | 474 | packfd = open(filename, O_RDONLY); 475 | fstat(packfd, &sb); 476 | idxmap = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, packfd, 0); 477 | 478 | if (idxmap == NULL) { 479 | fprintf(stderr, "mmap(2) error, exiting.\n"); 480 | exit(0); 481 | } 482 | close(packfd); 483 | 484 | offset = pack_find_sha_offset(sha_bin, idxmap); 485 | 486 | munmap(idxmap, sb.st_size); 487 | 488 | if (offset != -1) 489 | break; 490 | } 491 | } 492 | 493 | closedir(d); 494 | 495 | return (offset); 496 | } 497 | 498 | int 499 | pack_parse_header(int packfd, struct packfileinfo *packfileinfo, SHA1_CTX *packctx) 500 | { 501 | int version; 502 | int nobjects; 503 | unsigned char buf[4]; 504 | 505 | buf_read(packfd, buf, 4, read_sha_update, packctx); 506 | 507 | if (memcmp(buf, "PACK", 4)) { 508 | fprintf(stderr, "error: bad object header. Git repository may be corrupt.\n"); 509 | exit(128); 510 | } 511 | 512 | buf_read(packfd, &version, 4, read_sha_update, packctx); 513 | packfileinfo->version = ntohl(version); 514 | if (packfileinfo->version != 2) { 515 | fprintf(stderr, "error: unsupported version: %d\n", version); 516 | exit(128); 517 | } 518 | 519 | buf_read(packfd, &nobjects, 4, read_sha_update, packctx); 520 | packfileinfo->nobjects = ntohl(nobjects); 521 | 522 | /* This hardcoded value is because we read 4*3 bytes from the header */ 523 | return (12); 524 | } 525 | 526 | /* 527 | * The function pack_object_header checks the object type of the current object. 528 | * If the type is not deltified, it will immediately return and the variable 529 | * objectinfo will contain the type as objectinfo->ftype. Additionally, we will 530 | * have the objectinfo->isize. 531 | * 532 | * If the object is deltified, it will iteratively call object_header_ofs to 533 | * locate the following values: 534 | * A. The base object type, stored in objectinto->ftype 535 | * B. The base offset, stored in objectinfo->base 536 | * C. All delta offsets, stored in objectinfo->deltas 537 | * D. Number of deltas, stored in objectinfo->ndelta 538 | * Note: The objectinfo->isize value is NOT captured 539 | * 540 | * If the application needs the objectinfo->isize or objectinfo->data, it must 541 | * run pack_delta_content. This function will consumed values B-D to produce 542 | * the final non-deltified data. 543 | */ 544 | 545 | /* Starts at a new object header, not the delta */ 546 | void 547 | object_header_ofs(int packfd, int offset, int layer, 548 | struct objectinfo *objectinfo, struct objectinfo *childinfo, 549 | SHA1_CTX *packctx) 550 | { 551 | uint8_t c; 552 | unsigned shift; 553 | unsigned long used; 554 | lseek(packfd, offset, SEEK_SET); 555 | 556 | read(packfd, &c, 1); 557 | used = 1; 558 | childinfo->psize = c & 15; 559 | childinfo->ptype = (c >> 4) & 7; 560 | 561 | shift = 4; 562 | 563 | while (c & 0x80) { 564 | read(packfd, &c, 1); 565 | childinfo->psize += (c & 0x7f) << shift; 566 | shift += 7; 567 | used++; 568 | } 569 | 570 | if (childinfo->ptype != OBJ_OFS_DELTA) { 571 | objectinfo->ftype = childinfo->ptype; 572 | objectinfo->deltas = malloc(sizeof(unsigned long) * layer); 573 | objectinfo->ofsbase = offset + used; 574 | objectinfo->ndeltas = layer; 575 | } 576 | else { 577 | unsigned long delta; 578 | unsigned long ofshdr = 1; 579 | 580 | read(packfd, &c, 1); 581 | delta = c & 0x7f; 582 | while (c & 0x80) { 583 | delta++; 584 | ofshdr++; 585 | read(packfd, &c, 1); 586 | delta = (delta << 7) + (c & 0x7f); 587 | } 588 | object_header_ofs(packfd, offset - delta, layer+1, objectinfo, childinfo, packctx); 589 | objectinfo->deltas[layer] = offset + used + ofshdr; 590 | } 591 | } 592 | 593 | void 594 | pack_object_header(int packfd, int offset, struct objectinfo *objectinfo, 595 | SHA1_CTX *packctx) 596 | { 597 | uint8_t c; 598 | unsigned shift; 599 | 600 | lseek(packfd, offset, SEEK_SET); 601 | 602 | objectinfo->offset = offset; 603 | objectinfo->used = 1; 604 | 605 | c = 0; 606 | buf_read(packfd, &c, 1, read_sha_update, packctx); 607 | 608 | objectinfo->crc = crc32(objectinfo->crc, &c, 1); 609 | objectinfo->ptype = (c >> 4) & 7; 610 | objectinfo->psize = c & 15; 611 | shift = 4; 612 | 613 | while (c & 0x80) { 614 | buf_read(packfd, &c, 1, read_sha_update, packctx); 615 | objectinfo->crc = crc32(objectinfo->crc, &c, 1); 616 | objectinfo->psize += (c & 0x7f) << shift; 617 | shift += 7; 618 | objectinfo->used++; 619 | } 620 | 621 | if (objectinfo->ptype != OBJ_OFS_DELTA && objectinfo->ptype != OBJ_REF_DELTA) { 622 | objectinfo->ftype = objectinfo->ptype; 623 | objectinfo->ndeltas = 0; 624 | } 625 | else { 626 | /* We have to dig deeper */ 627 | unsigned long delta; 628 | unsigned long ofshdrsize = 1; 629 | struct objectinfo childinfo; 630 | 631 | buf_read(packfd, &c, 1, read_sha_update, packctx); 632 | objectinfo->crc = crc32(objectinfo->crc, &c, 1); 633 | delta = c & 0x7f; 634 | 635 | while (c & 0x80) { 636 | ofshdrsize++; 637 | delta += 1; 638 | buf_read(packfd, &c, 1, read_sha_update, packctx); 639 | objectinfo->crc = crc32(objectinfo->crc, &c, 1); 640 | delta = (delta << 7) + (c & 0x7f); 641 | } 642 | 643 | objectinfo->ofshdrsize = ofshdrsize; 644 | object_header_ofs(packfd, offset - delta, 1, objectinfo, &childinfo, packctx); 645 | objectinfo->deltas[0] = offset + objectinfo->used + ofshdrsize; 646 | 647 | } 648 | 649 | return; 650 | } 651 | 652 | int 653 | pack_find_sha_offset(unsigned char *sha, unsigned char *idxmap) 654 | { 655 | struct fan *fans; 656 | struct entry *entries; 657 | //struct checksum *checksums; 658 | struct offset *offsets; 659 | int idx_offset; 660 | char idx_version; 661 | int nelements; 662 | int n; 663 | 664 | if (memcmp(idxmap, "\xff\x74\x4f\x63", 4)) { 665 | fprintf(stderr, "Header signature does not match index version 2.\n"); 666 | exit(0); 667 | } 668 | idx_offset = 4; 669 | 670 | idx_version = *(idxmap + idx_offset+3); 671 | if (idx_version != 2) { 672 | fprintf(stderr, "opengit currently only supports version 2. Exiting.\n"); 673 | exit(0); 674 | } 675 | idx_offset += 4; 676 | 677 | // Get the fan table and capture last element 678 | fans = (struct fan *)(idxmap + idx_offset); 679 | nelements = ntohl(fans->count[255]); 680 | // Move to SHA entries 681 | idx_offset += sizeof(struct fan); 682 | // Point to SHA entries 683 | entries = (struct entry *)(idxmap + idx_offset); 684 | 685 | for (n=0;nptype != OBJ_OFS_DELTA) { 776 | decompressed_object->size = 0; 777 | decompressed_object->data = NULL; 778 | decompressed_object->deflated_size = 0; 779 | lseek(packfd, objectinfo->offset + objectinfo->used, SEEK_SET); 780 | deflate_caller(packfd, NULL, NULL, buffer_cb, decompressed_object); 781 | } 782 | else { 783 | pack_delta_content(packfd, objectinfo, NULL); 784 | free(objectinfo->deltas); 785 | decompressed_object->data = objectinfo->data; 786 | decompressed_object->size = objectinfo->isize; 787 | } 788 | } 789 | -------------------------------------------------------------------------------- /lib/pack.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef PACK_H 29 | #define PACK_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include "common.h" 35 | 36 | 37 | /* 38 | Header source Documentation/technical/multi-pack-index.txt 39 | */ 40 | 41 | /* 42 | * GPL git has at least 2 things called "index" in Git. 43 | * These are for files located in .git/objects/pack/[*].idx 44 | */ 45 | 46 | // idx file headers 47 | struct offset { 48 | unsigned int addr; 49 | }; 50 | 51 | struct checksum { 52 | unsigned int val; 53 | }; 54 | 55 | struct entry { 56 | unsigned char sha[20]; 57 | }; 58 | 59 | struct fan { 60 | int count[256]; 61 | }; 62 | 63 | // pack file headers 64 | struct packfileinfo { 65 | int version; 66 | int nobjects; 67 | unsigned char sha[20]; 68 | unsigned char ctx[20]; 69 | }; 70 | 71 | struct objectinfo { 72 | unsigned long offset; // The object header from the file's start 73 | uLong crc; 74 | 75 | unsigned long psize; // Size of the object content 76 | unsigned long isize; // Inflated size 77 | unsigned long used; // Bytes the header consumes 78 | unsigned int ftype; // Final type 79 | unsigned int ptype; // Pack type 80 | 81 | /* Values used by ofs_delta objects */ 82 | unsigned long deflated_size; 83 | unsigned long ofsbase; // Offset of object + object hdr 84 | unsigned long ofshdrsize; // The sizeof the ofs hdr 85 | unsigned long *deltas; // Offset of deltas + delta hdrs 86 | int ndeltas; // Number of deltas 87 | 88 | unsigned char *data; // Pointer to inflated data 89 | 90 | }; 91 | 92 | /* Used to store object information when creating the index */ 93 | struct index_entry { 94 | int offset; 95 | int type; 96 | uLong crc; 97 | unsigned char digest[20]; 98 | }; 99 | 100 | /* Used in the callback to get index information */ 101 | struct index_generate_arg { 102 | int bytes; 103 | SHA1_CTX shactx; 104 | }; 105 | 106 | typedef void packhandler(int, struct objectinfo *, void *); 107 | 108 | ssize_t sha_write(int fd, const void *buf, size_t nbytes, SHA1_CTX *idxctx); 109 | int pack_find_sha_offset(unsigned char *sha, unsigned char *idxmap); 110 | int pack_get_packfile_offset(char *sha_str, char *filename); 111 | int pack_parse_header(int packfd, struct packfileinfo *packfileinfo, SHA1_CTX *packctx); 112 | void pack_object_header(int packfd, int offset, struct objectinfo *objectinfo, SHA1_CTX *packctx); 113 | int pack_get_object_meta(int packfd, int offset, struct packfileinfo *packfileinfo, struct index_entry *index_entry, 114 | SHA1_CTX *packctx, SHA1_CTX *idxctx); 115 | unsigned char *pack_get_index_bytes_cb(unsigned char *buf, int size, int deflated_bytes, void *arg); 116 | void pack_delta_content(int packfd, struct objectinfo *objectinfo, SHA1_CTX *packctx); 117 | void write_index_header(int idxfd, SHA1_CTX *idxctx); 118 | void write_hash_count(int idxfd, struct index_entry *index_entry, SHA1_CTX *idxctx); 119 | void write_hashes(int idxfd, struct packfileinfo *packfileinfo, struct index_entry *index_entry, SHA1_CTX *idxctx); 120 | void write_crc_table(int idxfd, struct packfileinfo *packfileinfo, struct index_entry *index_entry, SHA1_CTX *idxctx); 121 | void write_32bit_table(int idxfd, struct packfileinfo *packfileinfo, struct index_entry *index_entry, SHA1_CTX *idxctx); 122 | void write_checksums(int idxfd, struct packfileinfo *packfileinfo, SHA1_CTX *idxctx); 123 | void pack_build_index(int idxfd, struct packfileinfo *packfileinfo, struct index_entry *index_entry, SHA1_CTX *idxctx); 124 | int sortindexentry(const void *a, const void *b); 125 | int read_sha_update(void *buf, size_t count, void *arg); 126 | void pack_content_handler(char *sha, packhandler packhandler, void *args); 127 | void pack_buffer_cb(int packfd, struct objectinfo *objectinfo, void *pargs); 128 | void get_type_pack_cb(int packfd, struct objectinfo *objectinfo, void *pargs); 129 | void write_pack_cb(int packfd, struct objectinfo *objectinfo, void *pargs); 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /lib/protocol.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "common.h" 35 | #include "protocol.h" 36 | 37 | 38 | size_t 39 | proto_process_pack(int packfd, FILE *stream) 40 | { 41 | int pktsize; 42 | size_t sz; 43 | unsigned char buf[MAXPKTSIZE]; 44 | 45 | while(1) { 46 | fscanf(stream, "%04x", &pktsize); 47 | if (pktsize == 0) { 48 | #ifdef NDEBUG 49 | fprintf(stderr, "debug: end of pack stream\n"); 50 | #endif 51 | break; 52 | } 53 | 54 | #ifdef NDEBUG 55 | fprintf(stderr, "debug: packet size: %d\n", pktsize); 56 | #endif 57 | sz = fread(buf, 1, pktsize-PKTSIZELEN, stream); 58 | if (sz != pktsize - PKTSIZELEN) { 59 | fprintf(stderr, "Unable to read correct number of bytes from packet, exiting.\n"); 60 | exit(sz); 61 | } 62 | 63 | if (memcmp(buf, "NAK\n", 4)==0) { 64 | #ifdef NDEBUG 65 | fprintf(stderr, "debug: received NAK packet\n"); 66 | #endif 67 | } 68 | else if (buf[0] == 0x02) { 69 | if (buf[sz] != '\n') { 70 | buf[sz] = '\0'; 71 | printf("remote: %s", buf); 72 | } 73 | else { 74 | printf("remote: %s\n", buf); 75 | } 76 | #ifdef NDEBUG 77 | fprintf(stderr, "debug: received server msg packet\n"); 78 | #endif 79 | } 80 | else { 81 | #ifdef NDEBUG 82 | fprintf(stderr, "debug: received pack packet\n"); 83 | #endif 84 | write(packfd, buf+1, pktsize-5); 85 | } 86 | } 87 | 88 | return (0); 89 | } 90 | 91 | static int 92 | push_ref(struct smart_head *smart_head, const char *refspec) 93 | { 94 | char *ref, *sep; 95 | struct symref *symref; 96 | int ret; 97 | 98 | ref = strdup(refspec); 99 | if (ref == NULL) 100 | return (ENOMEM); 101 | 102 | symref = malloc(sizeof(*symref)); 103 | if (symref == NULL) { 104 | ret = ENOMEM; 105 | goto out; 106 | } 107 | 108 | sep = strchr(ref, ':'); 109 | if (sep == NULL) { 110 | ret = EINVAL; 111 | goto out; 112 | } 113 | 114 | *sep++ = '\0'; 115 | symref->symbol = strdup(ref); 116 | symref->path = strdup(sep); 117 | STAILQ_INSERT_HEAD(&smart_head->symrefs, symref, link); 118 | 119 | ret = 0; 120 | out: 121 | if (ret != 0) 122 | free(symref); 123 | free(ref); 124 | return (ret); 125 | } 126 | 127 | /* 128 | * Parse out smart head response data 129 | */ 130 | int 131 | proto_parse_response(char *response, struct smart_head *smart_head) 132 | { 133 | char *position; 134 | char *token, *string, *tofree; 135 | long offset; 136 | int count; 137 | 138 | position = response; 139 | sscanf(position, "%04lx", &offset); 140 | if (strncmp(position+PKTSIZELEN, "# service=", 10)==0) { 141 | // XXX Record service name 142 | position += offset; 143 | /* The first four bytes are 0000, check and skip ahead */ 144 | if (strncmp(position, PKTFLUSH, PKTSIZELEN)) 145 | return (EINVAL); 146 | position += PKTSIZELEN; 147 | sscanf(position, "%04lx", &offset); 148 | } 149 | position += PKTSIZELEN; 150 | strlcpy(smart_head->sha, position, HASH_SIZE+1); 151 | 152 | tofree = string = strndup(position+41+strlen(position+41)+1, 153 | offset-(47+strlen(position+41))); 154 | 155 | while((token = strsep(&string, " \n")) != NULL) { 156 | if (!strncmp(token, "multi_ack", 9)) 157 | smart_head->cap |= PACKPROTO_MULTI_ACK; 158 | else if (!strncmp(token, "multi_ack_detailed", 18)) 159 | smart_head->cap |= PACKPROTO_MULTI_ACK_DETAILED; 160 | else if (!strncmp(token, "no-done", 7)) 161 | smart_head->cap |= PACKPROTO_MULTI_NO_DONE; 162 | else if (!strncmp(token, "thin-pack", 9)) 163 | smart_head->cap |= PACKPROTO_THIN_PACK; 164 | else if (!strncmp(token, "side-band", 9)) 165 | smart_head->cap |= PACKPROTO_SIDE_BAND; 166 | else if (!strncmp(token, "side-band-64k", 13)) 167 | smart_head->cap |= PACKPROTO_SIDE_BAND_64K; 168 | else if (!strncmp(token, "ofs-delta", 9)) 169 | smart_head->cap |= PACKPROTO_OFS_DELTA; 170 | else if (!strncmp(token, "agent", 5)) 171 | smart_head->cap |= PACKPROTO_AGENT; 172 | else if (!strncmp(token, "shallow", 7)) 173 | smart_head->cap |= PACKPROTO_SHALLOW; 174 | else if (!strncmp(token, "deepen-since", 12)) 175 | smart_head->cap |= PACKPROTO_DEEPEN_SINCE; 176 | else if (!strncmp(token, "deepen-not", 10)) 177 | smart_head->cap |= PACKPROTO_DEEPEN_NOT; 178 | else if (!strncmp(token, "deepen-relative", 15)) 179 | smart_head->cap |= PACKPROTO_DEEPEN_RELATIVE; 180 | else if (!strncmp(token, "no-progress", 11)) 181 | smart_head->cap |= PACKPROTO_NO_PROGRESS; 182 | else if (!strncmp(token, "include-tag", 11)) 183 | smart_head->cap |= PACKPROTO_INCLUDE_TAG; 184 | else if (!strncmp(token, "report-status", 13)) 185 | smart_head->cap |= PACKPROTO_REPORT_STATUS; 186 | else if (!strncmp(token, "delete-refs", 11)) 187 | smart_head->cap |= PACKPROTO_DELETE_REFS; 188 | else if (!strncmp(token, "quiet", 5)) 189 | smart_head->cap |= PACKPROTO_QUIET; 190 | else if (!strncmp(token, "atomic", 6)) 191 | smart_head->cap |= PACKPROTO_ATOMIC; 192 | else if (!strncmp(token, "push-options", 12)) 193 | smart_head->cap |= PACKPROTO_PUSH_OPTIONS; 194 | else if (!strncmp(token, "allow-tip-sha1-in-want", 22)) 195 | smart_head->cap |= PACKPROTO_ALLOW_TIP_SHA1_IN_WANT; 196 | else if (!strncmp(token, "allow-reachable-sha1-in-want", 28)) 197 | smart_head->cap |= PACKPROTO_ALLOW_REACHABLE_SHA1_IN_WANT; 198 | else if (!strncmp(token, "push-cert", 9)) 199 | smart_head->cap |= PACKPROTO_PUSH_CERT; 200 | else if (!strncmp(token, "filter", 6)) 201 | smart_head->cap |= PACKPROTO_FILTER; 202 | else if (!strncmp(token, "symref", 6)) 203 | push_ref(smart_head, strstr(token, "=") + 1); 204 | } 205 | free(tofree); 206 | 207 | position += offset - PKTSIZELEN; 208 | 209 | /* Iterate through the refs */ 210 | count = 0; 211 | while(strncmp(position, PKTFLUSH, PKTSIZELEN)) { 212 | smart_head->refs = realloc(smart_head->refs, sizeof(struct smart_head) * (count+1)); 213 | sscanf(position, "%04lx", &offset); 214 | strlcpy(smart_head->refs[count].sha, position+PKTSIZELEN, HASH_SIZE+1); 215 | 216 | smart_head->refs[count].path = strndup(position+PKTSIZELEN+41, 217 | offset-(PKTSIZELEN+42)); 218 | 219 | position += offset; 220 | count++; 221 | } 222 | smart_head->refcount = count; 223 | return (0); 224 | } 225 | -------------------------------------------------------------------------------- /lib/protocol.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef __PROTOCOL_H 29 | #define __PROTOCOL_H 30 | 31 | #include 32 | 33 | /* Specifies the pack protocol capabilities */ 34 | #define PACKPROTO_MULTI_ACK BIT(0) 35 | #define PACKPROTO_MULTI_ACK_DETAILED BIT(1) 36 | #define PACKPROTO_MULTI_NO_DONE BIT(2) 37 | #define PACKPROTO_THIN_PACK BIT(3) 38 | #define PACKPROTO_SIDE_BAND BIT(4) 39 | #define PACKPROTO_SIDE_BAND_64K BIT(5) 40 | #define PACKPROTO_OFS_DELTA BIT(6) 41 | #define PACKPROTO_AGENT BIT(7) 42 | #define PACKPROTO_SHALLOW BIT(8) 43 | #define PACKPROTO_DEEPEN_SINCE BIT(9) 44 | #define PACKPROTO_DEEPEN_NOT BIT(10) 45 | #define PACKPROTO_DEEPEN_RELATIVE BIT(11) 46 | #define PACKPROTO_NO_PROGRESS BIT(12) 47 | #define PACKPROTO_INCLUDE_TAG BIT(13) 48 | #define PACKPROTO_REPORT_STATUS BIT(14) 49 | #define PACKPROTO_DELETE_REFS BIT(15) 50 | #define PACKPROTO_QUIET BIT(16) 51 | #define PACKPROTO_ATOMIC BIT(17) 52 | #define PACKPROTO_PUSH_OPTIONS BIT(18) 53 | #define PACKPROTO_ALLOW_TIP_SHA1_IN_WANT BIT(19) 54 | #define PACKPROTO_ALLOW_REACHABLE_SHA1_IN_WANT BIT(20) 55 | #define PACKPROTO_PUSH_CERT BIT(21) 56 | #define PACKPROTO_FILTER BIT(22) 57 | 58 | /* Protocol state */ 59 | #define STATE_NEWLINE 0 60 | #define STATE_NAK 1 61 | #define STATE_REMOTE 2 62 | #define STATE_PACK 3 63 | #define STATE_UNKNOWN 9 64 | 65 | #define PKTSIZELEN 4 66 | #define MAXPKTSIZE 65516 67 | #define PKTFLUSH "0000" 68 | 69 | struct symref { 70 | char *symbol; 71 | char *path; 72 | 73 | STAILQ_ENTRY(symref) link; 74 | }; 75 | 76 | struct ref { 77 | char sha[HASH_SIZE]; 78 | char *path; 79 | 80 | STAILQ_ENTRY(symref) link; 81 | }; 82 | 83 | struct smart_head { 84 | char sha[HASH_SIZE+1]; 85 | char headname[1024]; 86 | uint32_t cap; 87 | int refcount; 88 | struct ref *refs; 89 | 90 | STAILQ_HEAD(, symref) symrefs; 91 | }; 92 | 93 | int proto_parse_response(char *response, struct smart_head *smart_head); 94 | size_t proto_process_pack(int packfd, FILE *stream); 95 | 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /lib/zlib-handler.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "common.h" 35 | #include "zlib-handler.h" 36 | 37 | unsigned char * 38 | buffer_cb(unsigned char *buf, int size, int deflated_size, void *arg) 39 | { 40 | struct decompressed_object *decompressed_object = arg; 41 | 42 | decompressed_object->data = realloc(decompressed_object->data, decompressed_object->size + size); 43 | memcpy(decompressed_object->data + decompressed_object->size, buf, size); 44 | decompressed_object->size += size; 45 | decompressed_object->deflated_size += deflated_size; 46 | 47 | return (buf); 48 | } 49 | 50 | int 51 | zlib_update_sha(unsigned char *data, int use, void *darg) 52 | { 53 | SHA1_CTX *ctxv = darg; 54 | if (ctxv) 55 | SHA1_Update(ctxv, data, use); 56 | return (0); 57 | } 58 | 59 | int 60 | zlib_update_crc(unsigned char *data, int use, void *darg) 61 | { 62 | uLong *crcv = darg; 63 | if (crcv) 64 | *crcv = crc32(*crcv, data, use); 65 | return (0); 66 | } 67 | 68 | int 69 | zlib_update_crc_sha(unsigned char *data, int use, void *darg) 70 | { 71 | struct two_darg *two_darg = darg; 72 | 73 | zlib_update_crc(data, use, two_darg->crc); 74 | zlib_update_sha(data, use, two_darg->sha); 75 | 76 | return (0); 77 | } 78 | 79 | 80 | unsigned char * 81 | write_cb(unsigned char *buf, int size, int __unused deflate_bytes, void *arg) 82 | { 83 | struct writer_args *writer_args = arg; 84 | 85 | writer_args->sent += write(writer_args->fd, buf, size); 86 | 87 | return (buf); 88 | } 89 | 90 | /* 91 | * This function's objective is to deflate arbitrary data. It takes five 92 | * arguments: 93 | * - sourcefd: The file descriptor of the source 94 | * - deflated handler and darg: Used to process the deflated bytes beyond 95 | * deflating. The darg variable is the argument for the handler function. 96 | * This is used when calculating the checksum of the packfile. 97 | * - inflated handler and iarg: Similar to deflated handler, this processes 98 | * the inflated bytes with iarg as the variable. 99 | * This is used to know where to store the inflated data, whether in a 100 | * buffer, calculate a value with them or write them somewhere 101 | */ 102 | int 103 | deflate_caller(int sourcefd, deflated_handler deflated_handler, void *darg, 104 | inflated_handler inflated_handler, void *arg) { 105 | Bytef in[CHUNK]; 106 | Bytef out[CHUNK]; 107 | unsigned have; 108 | z_stream strm; 109 | int ret; 110 | int input_len; 111 | int use; 112 | int burn; 113 | 114 | strm.zalloc = Z_NULL; 115 | strm.zfree = Z_NULL; 116 | strm.opaque = Z_NULL; 117 | strm.avail_in = 0; 118 | strm.next_in = Z_NULL; 119 | ret = inflateInit(&strm); 120 | if (ret != Z_OK) 121 | return (ret); 122 | 123 | do { 124 | burn = 0; 125 | strm.avail_in = input_len = read(sourcefd, in, CHUNK); 126 | if (strm.avail_in == -1) { 127 | (void)inflateEnd(&strm); 128 | perror("read from source file"); 129 | return (Z_ERRNO); 130 | } 131 | if (strm.avail_in == 0) 132 | break; 133 | strm.next_in = in; 134 | 135 | do { 136 | strm.avail_out = CHUNK; 137 | strm.next_out = out; 138 | ret = inflate(&strm, Z_NO_FLUSH); 139 | 140 | switch (ret) { 141 | case Z_NEED_DICT: 142 | ret = Z_DATA_ERROR; 143 | case Z_DATA_ERROR: 144 | case Z_MEM_ERROR: 145 | (void)inflateEnd(&strm); 146 | return (ret); 147 | } 148 | have = CHUNK - strm.avail_out; 149 | 150 | // Return value of 0 code means exit 151 | use = input_len - strm.avail_in; 152 | if (deflated_handler) 153 | deflated_handler(in+burn, use, darg); 154 | burn+=use; 155 | input_len -= use; 156 | if (inflated_handler(out, have, use, arg) == NULL) 157 | goto end_inflation; 158 | 159 | } while (strm.avail_out == 0); 160 | } while (ret != Z_STREAM_END); 161 | 162 | end_inflation: 163 | (void)inflateEnd(&strm); 164 | return (ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR); 165 | } 166 | 167 | int 168 | zlib_deliver_loose_object_content(unsigned char *buf, int size, void *data) 169 | { 170 | return (0); 171 | } 172 | 173 | int 174 | inflate_caller(int sourcefd, deflated_handler deflated_handler, void *darg, 175 | inflated_handler inflated_handler, void *arg) { 176 | unsigned char in[CHUNK]; 177 | unsigned char out[CHUNK]; 178 | unsigned have; 179 | z_stream strm; 180 | int ret; 181 | int input_len; 182 | int use; 183 | int burn; 184 | 185 | strm.zalloc = Z_NULL; 186 | strm.zfree = Z_NULL; 187 | strm.opaque = Z_NULL; 188 | strm.avail_in = 0; 189 | strm.next_in = Z_NULL; 190 | ret = inflateInit(&strm); 191 | if (ret != Z_OK) 192 | return (ret); 193 | 194 | do { 195 | burn = 0; 196 | strm.avail_in = input_len = read(sourcefd, in, CHUNK); 197 | if (strm.avail_in == -1) { 198 | (void)inflateEnd(&strm); 199 | perror("read from source file"); 200 | return (Z_ERRNO); 201 | } 202 | if (strm.avail_in == 0) 203 | break; 204 | strm.next_in = in; 205 | 206 | do { 207 | strm.avail_out = CHUNK; 208 | strm.next_out = out; 209 | ret = inflate(&strm, Z_NO_FLUSH); 210 | 211 | switch (ret) { 212 | case Z_NEED_DICT: 213 | ret = Z_DATA_ERROR; 214 | case Z_DATA_ERROR: 215 | case Z_MEM_ERROR: 216 | (void)inflateEnd(&strm); 217 | return (ret); 218 | } 219 | have = CHUNK - strm.avail_out; 220 | 221 | // Return value of 0 code means exit 222 | use = input_len - strm.avail_in; 223 | if (deflated_handler) 224 | deflated_handler(in+burn, use, darg); 225 | burn+=use; 226 | input_len -= use; 227 | if (inflated_handler(out, have, use, arg) == NULL) 228 | goto end_inflation; 229 | 230 | } while (strm.avail_out == 0); 231 | } while (ret != Z_STREAM_END); 232 | 233 | end_inflation: 234 | (void)inflateEnd(&strm); 235 | return (ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR); 236 | } 237 | -------------------------------------------------------------------------------- /lib/zlib-handler.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef __ZLIB_HANDLER__H 29 | #define __ZLIB_HANDLER__H 30 | 31 | /* Used to process both the crc and ctx data */ 32 | struct two_darg { 33 | void *crc; 34 | void *sha; 35 | }; 36 | 37 | struct writer_args { 38 | int fd; 39 | long sent; 40 | }; 41 | 42 | #define CHUNK 16384 43 | 44 | typedef int deflated_handler(unsigned char *, int, void *); 45 | typedef unsigned char *inflated_handler(unsigned char *, int, int, void *); 46 | 47 | int deflate_caller(int sourcefd, deflated_handler deflated_handler, void *darg, 48 | inflated_handler inflated_handler, void *arg); 49 | unsigned char *write_cb(unsigned char *buf, int size, int __unused deflate_bytes, void *arg); 50 | unsigned char *buffer_cb(unsigned char *buf, int size, int deflate_size, void *arg); 51 | int zlib_update_sha(unsigned char *data, int use, void *darg); 52 | int zlib_update_crc(unsigned char *data, int use, void *darg); 53 | int zlib_update_crc_sha(unsigned char *data, int use, void *darg); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | MAN= 3 | .if defined(NDEBUG) 4 | # Debugging compiles in gcc because it works better with valgrind 5 | CC= gcc 6 | CFLAGS+= -DNDEBUG -g -Wall -Wunreachable-code -Werror -O0 7 | .endif 8 | CFLAGS+= -Wall -I${.CURDIR}/.. 9 | # Currently statically linking against libogit.a library 10 | LDADD+= ${.OBJDIR}/../lib/libogit.a 11 | LDFLAGS+= -lmd -lz -lfetch 12 | 13 | PROG= ogit 14 | 15 | SRCS= ogit.c remote.c init.c hash-object.c update-index.c log.c \ 16 | cat-file.c clone.c clone_http.c clone_ssh.c index-pack.c 17 | 18 | CLEANFILES+= ${PROG}.core 19 | 20 | # These tests rely on Kyua/atf bits 21 | .if .FreeBSD 22 | HAS_TESTS= 23 | # XXX TODO: MK_TESTS 24 | SUBDIR.yes+= tests 25 | .endif 26 | 27 | .include 28 | -------------------------------------------------------------------------------- /src/cat-file.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include "lib/zlib-handler.h" 43 | #include "lib/buffering.h" 44 | #include "lib/common.h" 45 | #include "lib/loose.h" 46 | #include "lib/pack.h" 47 | #include "lib/ini.h" 48 | #include "cat-file.h" 49 | 50 | static struct option long_options[] = 51 | { 52 | {NULL, 0, NULL, 0} 53 | }; 54 | 55 | int 56 | cat_file_usage(int type) 57 | { 58 | fprintf(stderr, "usage: git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | | --textconv | --filters) [--path=] \n"); 59 | fprintf(stderr, "\n"); 60 | exit(type); 61 | } 62 | 63 | /* Just print object type */ 64 | void 65 | cat_file_print_type_by_id(int object_type) 66 | { 67 | if (object_type <= 0 || object_type >= 8) { 68 | fprintf(stderr, "Unknown type, exiting.\n"); 69 | exit(128); 70 | } 71 | printf("%s\n", object_name[object_type]); 72 | } 73 | 74 | /* Handles loose object the callback, used by cat_file_get_content */ 75 | unsigned char * 76 | cat_loose_object_cb(unsigned char *buf, int size, int deflated_bytes, void *arg) 77 | { 78 | struct loosearg *loosearg = arg; 79 | int hdr_offset; 80 | 81 | if (loosearg->step == 0) { 82 | loosearg->step++; 83 | hdr_offset = loose_get_headers(buf, size, arg); 84 | switch(loosearg->cmd) { 85 | case CAT_FILE_TYPE: 86 | loose_get_headers(buf, size, loosearg); 87 | cat_file_print_type_by_id(loosearg->type); 88 | return (NULL); 89 | case CAT_FILE_SIZE: 90 | printf("%lu\n", loosearg->size); 91 | return (NULL); 92 | case CAT_FILE_PRINT: 93 | size -= hdr_offset; 94 | buf += hdr_offset; 95 | 96 | loosearg->decompressed_object.size = 0; 97 | loosearg->decompressed_object.data = NULL; 98 | loosearg->decompressed_object.deflated_size = 0; 99 | 100 | break; 101 | } 102 | } 103 | 104 | /* 105 | * If the object is a OBJ_TREE, the data is stored in a buffer, not printed 106 | * Further explanation is in the comment of cat_file_get_content. 107 | */ 108 | if (loosearg->type == OBJ_TREE) 109 | buffer_cb(buf, size, deflated_bytes, &loosearg->decompressed_object); 110 | else 111 | write(loosearg->fd, buf, size); 112 | return (buf); 113 | } 114 | 115 | static void 116 | print_tree(char *mode, uint8_t type, char *sha, char *filename, void *args) 117 | { 118 | printf("%06d %s %s\t%s\n", atoi(mode), object_name[type], sha, filename); 119 | } 120 | 121 | /* Print out content of pack objects */ 122 | static void 123 | print_content(int packfd, struct objectinfo *objectinfo, char *sha) 124 | { 125 | if (objectinfo->ftype == OBJ_TREE) { 126 | ITERATE_TREE(sha, print_tree, NULL); 127 | } 128 | else if (objectinfo->ptype != OBJ_OFS_DELTA) { 129 | struct writer_args writer_args; 130 | writer_args.fd = STDOUT_FILENO; 131 | writer_args.sent = 0; 132 | lseek(packfd, objectinfo->offset + objectinfo->used, SEEK_SET); 133 | deflate_caller(packfd, NULL, NULL, write_cb, &writer_args); 134 | } 135 | else { 136 | pack_delta_content(packfd, objectinfo, NULL); 137 | write(STDOUT_FILENO, objectinfo->data, objectinfo->isize); 138 | free(objectinfo->data); 139 | free(objectinfo->deltas); 140 | } 141 | } 142 | 143 | /* Print out the size of pack objects */ 144 | void 145 | print_size(int packfd, struct objectinfo *objectinfo) 146 | { 147 | if (objectinfo->ptype != OBJ_OFS_DELTA) { 148 | struct decompressed_object decompressed_object; 149 | decompressed_object.size = 0; 150 | decompressed_object.data = NULL; 151 | lseek(packfd, objectinfo->offset + objectinfo->used, SEEK_SET); 152 | deflate_caller(packfd, NULL, NULL, buffer_cb, &decompressed_object); 153 | printf("%lu\n", decompressed_object.size); 154 | } 155 | else { 156 | pack_delta_content(packfd, objectinfo, NULL); 157 | printf("%lu\n", objectinfo->isize); 158 | free(objectinfo->data); 159 | free(objectinfo->deltas); 160 | } 161 | } 162 | 163 | /* Used by pack_content_handler to output packfile by flags */ 164 | void 165 | cat_file_pack_handler(int packfd, struct objectinfo *objectinfo, void *pargs) 166 | { 167 | struct loosearg *loosearg = pargs; 168 | 169 | switch(loosearg->cmd) { 170 | case CAT_FILE_PRINT: 171 | print_content(packfd, objectinfo, loosearg->sha); 172 | break; 173 | case CAT_FILE_SIZE: 174 | print_size(packfd, objectinfo); 175 | break; 176 | case CAT_FILE_TYPE: 177 | cat_file_print_type_by_id(objectinfo->ftype); 178 | break; 179 | } 180 | } 181 | 182 | /* 183 | * This function will print out the object in the intended format. 184 | * While the CONTENT_HANDLER callback ordinarily is sufficient to process the content 185 | * here the cat_loose_object_cb handler only stores the object in its decompressed_object 186 | * member. Afterwards, the object is printed using iterate_tree (not ITERATE_TREE). This 187 | * is a special case to prevent unnecessarily double-reading a loose object's header. 188 | */ 189 | void 190 | cat_file_get_content(char *sha_str, uint8_t flags) 191 | { 192 | struct loosearg loosearg; 193 | 194 | loosearg.fd = STDOUT_FILENO; 195 | loosearg.cmd = flags; 196 | loosearg.step = 0; 197 | loosearg.sent = 0; 198 | loosearg.sha = sha_str; 199 | 200 | CONTENT_HANDLER(sha_str, cat_loose_object_cb, cat_file_pack_handler, &loosearg); 201 | if (loosearg.type == OBJ_TREE) { 202 | iterate_tree(&loosearg.decompressed_object, print_tree, NULL); 203 | } 204 | } 205 | 206 | int 207 | cat_file_main(int argc, char *argv[]) 208 | { 209 | int ret = 0; 210 | int ch; 211 | char *sha_str; 212 | uint8_t flags = 0; 213 | 214 | argc--; argv++; 215 | 216 | while((ch = getopt_long(argc, argv, "p:t:s:", long_options, NULL)) != -1) 217 | switch(ch) { 218 | case 'p': 219 | argc--; 220 | argv++; 221 | sha_str = argv[1]; 222 | flags = CAT_FILE_PRINT; 223 | break; 224 | case 't': 225 | argc--; 226 | argv++; 227 | sha_str = argv[1]; 228 | flags = CAT_FILE_TYPE; 229 | break; 230 | case 's': 231 | argc--; 232 | argv++; 233 | sha_str = argv[1]; 234 | flags = CAT_FILE_SIZE; 235 | break; 236 | default: 237 | printf("cat-file: Currently not implemented\n"); 238 | cat_file_usage(0); 239 | } 240 | 241 | if (git_repository_path() == -1) { 242 | fprintf(stderr, "fatal: not a git repository (or any of the parent directories): .git"); 243 | exit(0); 244 | } 245 | 246 | switch(flags) { 247 | case CAT_FILE_PRINT: 248 | case CAT_FILE_TYPE: 249 | case CAT_FILE_SIZE: 250 | cat_file_get_content(sha_str, flags); 251 | } 252 | 253 | return (ret); 254 | } 255 | 256 | -------------------------------------------------------------------------------- /src/cat-file.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef __CAT_FILE_H_ 29 | #define __CAT_FILE_H_ 30 | 31 | /* Commands */ 32 | #define CAT_FILE_DEFAULT 0x00 33 | #define CAT_FILE_TYPE 0x01 34 | #define CAT_FILE_PRINT 0x02 35 | #define CAT_FILE_SIZE 0x04 36 | #define CAT_FILE_EXIT 0x08 37 | 38 | void cat_file_get_content(char *sha_str, uint8_t flags); 39 | int cat_file_get_content_loose(char *sha_str, uint8_t flags); 40 | void cat_file_get_content_pack(char *sha_str, uint8_t flags); 41 | int cat_file_main(int argc, char *argv[]); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/clone.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "lib/common.h" 39 | #include "lib/pack.h" 40 | #include "lib/index.h" 41 | #include "lib/ini.h" 42 | #include "lib/loose.h" 43 | #include "lib/zlib-handler.h" 44 | #include "clone.h" 45 | #include "init.h" 46 | 47 | static struct clone_handler clone_handlers[] = { 48 | { 49 | .matcher = match_http, 50 | .run_service = http_run_service, 51 | .get_pack_stream = http_get_pack_stream, 52 | #ifdef NDEBUG 53 | .name = "http", 54 | #endif 55 | }, 56 | { 57 | .matcher = match_ssh, 58 | .run_service = ssh_run_service, 59 | .get_pack_stream = ssh_get_pack_stream, 60 | #ifdef NDEBUG 61 | .name = "ssh", 62 | #endif 63 | }, 64 | }; 65 | 66 | /* XXX Assume ssh by default? */ 67 | static struct clone_handler *default_handler = NULL; 68 | 69 | static struct option long_options[] = 70 | { 71 | {NULL, 0, NULL, 0} 72 | }; 73 | 74 | /* 75 | * Gets the git directory name from the path 76 | * Will expand if the path is a bare repo, does not have a name, et al 77 | */ 78 | static char * 79 | get_repo_dir(char *path) 80 | { 81 | char *reponame; 82 | int x, end; 83 | 84 | x = strlen(path); 85 | if (path[x-1] == '/') 86 | x--; 87 | end = x; 88 | /* Check if the URL ends in .git or .git/ */ 89 | if (!strncmp(path+x-PKTSIZELEN, ".git", 4)) 90 | end = x - 4; 91 | 92 | /* Find the first '/' */ 93 | for(;path[x-1]!='/' && x !=0;x--); 94 | if (x == 0) { 95 | fprintf(stderr, "There must be a pathname, failure.\n"); 96 | exit(-1); 97 | } 98 | 99 | reponame = strndup(path+x, end-x); 100 | return (reponame); 101 | } 102 | 103 | static void 104 | populate_packed_refs(char *repodir, struct smart_head *smart_head) 105 | { 106 | char path[PATH_MAX]; 107 | FILE *refs; 108 | 109 | snprintf(path, PATH_MAX, "%s/%s", repodir, ".git/packed-refs"); 110 | refs = fopen(path, "w"); 111 | if (refs == NULL) { 112 | fprintf(stderr, "Unable to open file for writing. %s.\n", 113 | path); 114 | } 115 | 116 | for(int x=0;xrefcount;x++) { 117 | fwrite(smart_head->refs[x].sha, HASH_SIZE, 1, refs); 118 | fprintf(refs, " %s\n", smart_head->refs[x].path); 119 | } 120 | 121 | fclose(refs); 122 | } 123 | 124 | static void 125 | populate_symrefs(char *repodir, struct smart_head *smart_head) 126 | { 127 | char path[PATH_MAX]; 128 | struct symref *ref; 129 | FILE *symfile; 130 | 131 | /* HEAD -> refs/heads/master */ 132 | STAILQ_FOREACH(ref, &smart_head->symrefs, link) { 133 | snprintf(path, sizeof(path), "%s/.git/%s", repodir, ref->symbol); 134 | symfile = fopen(path, "w"); 135 | if (symfile == NULL) { 136 | fprintf(stderr, "Unable to open file for writing. %s.\n", 137 | path); 138 | continue; 139 | } 140 | 141 | fprintf(symfile, "ref: %s\n", ref->path); 142 | fclose(symfile); 143 | } 144 | } 145 | 146 | static void 147 | clone_initial_config(char *uri, char *repodir) 148 | { 149 | struct section core; 150 | struct section remote; 151 | struct section branch; 152 | char path[PATH_MAX]; 153 | int fd; 154 | 155 | /* Adds core, remote and branch */ 156 | 157 | core.type = CORE; 158 | core.repositoryformatversion = 0; 159 | core.filemode = TRUE; 160 | core.bare = FALSE; 161 | core.logallrefupdates = TRUE; 162 | 163 | remote.type = REMOTE; 164 | remote.repo_name = "origin"; 165 | remote.url = uri; 166 | remote.fetch = "+refs/heads/*:refs/remotes/origin/*"; 167 | 168 | branch.type = BRANCH; 169 | branch.repo_name = "master"; 170 | branch.remote = "origin"; 171 | branch.merge = "refs/heads/master"; 172 | 173 | core.next = &remote; 174 | remote.next = &branch; 175 | branch.next = NULL; 176 | 177 | strlcpy(path, repodir, PATH_MAX); 178 | strlcat(path, "/.git/config", PATH_MAX); 179 | fd = open(path, O_WRONLY | O_CREAT, 0660); 180 | if (fd == -1) { 181 | fprintf(stderr, "1 Unable to open file %s: %s\n", path, strerror(errno)); 182 | exit(errno); 183 | } 184 | 185 | ini_write_config(fd, &core); 186 | } 187 | 188 | /* 189 | * Description: Used to loop through each tree object and iteratively 190 | * through each sub-tree object. 191 | * Handler for iterate_tree 192 | */ 193 | void 194 | generate_tree_item(char *mode, uint8_t type, char *sha, char *filename, void *arg) 195 | { 196 | char *prefix = arg; 197 | char *buildpath = prefix; 198 | char *fn = prefix + strlen(prefix); 199 | int imode; 200 | 201 | imode = strtol(mode+2, 0, 8); 202 | snprintf(fn, PATH_MAX, "/%s", filename); 203 | if (type == OBJ_TREE) { 204 | mkdir(buildpath, 0777); 205 | ITERATE_TREE(sha, generate_tree_item, prefix); 206 | } 207 | else { 208 | struct writer_args writer_args; 209 | int buildfd; 210 | 211 | buildfd = open(buildpath, O_CREAT|O_WRONLY, imode); 212 | writer_args.fd = buildfd; 213 | writer_args.sent = 0; 214 | 215 | CONTENT_HANDLER(sha, write_cb, write_pack_cb, &writer_args); 216 | } 217 | *fn = '\0'; 218 | } 219 | 220 | static int 221 | clone_generic_build_done(char **content, int content_length) 222 | { 223 | *content = realloc(*content, content_length + 14); 224 | strlcpy(*content+content_length, "00000009done\n\0", 14); 225 | return (13); // Always the same length 226 | } 227 | 228 | static int 229 | clone_generic_build_want(char **content, int content_length, char *capabilities, const char *sha) 230 | { 231 | char line[3000]; // XXX Bad approach 232 | int len; 233 | 234 | /* size + want + space + SHA(40) + space + capabilities + newline */ 235 | len = PKTSIZELEN + 4 + 1 + HASH_SIZE + 1 + strlen(capabilities) + 1; 236 | 237 | sprintf(line, "%04xwant %s %s\n", len, sha, capabilities); 238 | *content = realloc(*content, content_length + len + 1); 239 | strlcpy(*content+content_length, line, len+1); 240 | 241 | return (len); 242 | } 243 | 244 | static int 245 | clone_build_post_content(const char *sha, char **content) 246 | { 247 | int content_length; 248 | char *capabilities = "multi_ack_detailed no-done side-band-64k thin-pack ofs-delta deepen-since deepen-not agent=opengit/0.0.1-pre"; 249 | 250 | content_length = 0; 251 | 252 | content_length += clone_generic_build_want(content, content_length, 253 | capabilities, sha); 254 | content_length += clone_generic_build_done(content, content_length); 255 | 256 | return (content_length); 257 | } 258 | 259 | static void 260 | clone_generic_get_pack(struct clone_handler *chandler, int packfd, struct smart_head *smart_head) 261 | { 262 | char *content = NULL; 263 | FILE *stream; 264 | 265 | clone_build_post_content(smart_head->sha, &content); 266 | stream = chandler->get_pack_stream(chandler, content); 267 | 268 | proto_process_pack(packfd, stream); 269 | 270 | fclose(stream); 271 | free(content); 272 | } 273 | 274 | int 275 | clone_generic(struct clone_handler *chandler, char *repodir, 276 | struct smart_head *smart_head) 277 | { 278 | int packfd; 279 | int offset; 280 | int idxfd; 281 | struct packfileinfo packfileinfo; 282 | struct index_entry *index_entry; 283 | char path[PATH_MAX]; 284 | char srcpath[PATH_MAX]; 285 | int ret; 286 | FILE *stream; 287 | char *response; 288 | char *newpath; 289 | SHA1_CTX packctx; 290 | SHA1_CTX idxctx; 291 | 292 | strlcpy(path, dotgitpath, PATH_MAX); 293 | 294 | strlcat(path, "objects/pack/_tmp.pack", PATH_MAX); 295 | packfd = open(path, O_RDWR | O_CREAT, 0660); 296 | if (packfd == -1) { 297 | fprintf(stderr, "1 Unable to open file %s.\n", path); 298 | exit(-1); 299 | } 300 | again: 301 | response = NULL; 302 | stream = chandler->run_service(chandler, "git-upload-pack"); 303 | if (stream == NULL) { 304 | /* Fortunately, we can assume fetch_uri length will always be > 4 */ 305 | if (strncmp(*chandler->path + strlen(*chandler->path) - 4, ".git", 4)) { 306 | /* We'll try again with .git (+ null terminator) */ 307 | newpath = malloc(strlen(*chandler->path) + 5); 308 | strncpy(newpath, *chandler->path, strlen(*chandler->path)); 309 | strncat(newpath, ".git", strlen(*chandler->path) + 5); 310 | free(*chandler->path); 311 | *chandler->path = newpath; 312 | goto again; 313 | } 314 | 315 | fprintf(stderr, "Unable to clone repository.\n"); 316 | ret = 128; 317 | goto out; 318 | } 319 | 320 | int pktsize; 321 | char out[8000]; 322 | int r; 323 | bool inservice = false; 324 | int added = 0; 325 | 326 | do { 327 | /* Read the packet size */ 328 | r = fread(out, 1, PKTSIZELEN, stream); 329 | if (r != PKTSIZELEN) { 330 | perror("Read a 4 size, probably an error."); 331 | exit(0); 332 | } 333 | out[PKTSIZELEN] = '\0'; 334 | pktsize = (int)strtol(out, NULL, 16); 335 | 336 | if (inservice == false) { 337 | /* Break if the packet was 0. */ 338 | if (pktsize == 0) 339 | break; 340 | r += fread(out + PKTSIZELEN, 1, pktsize - PKTSIZELEN, 341 | stream); 342 | if (r != pktsize) { 343 | printf("Failed to read the size, expected %d, read %d, exiting.\n", pktsize, r); 344 | exit(0); 345 | } 346 | if (strncmp(out+PKTSIZELEN, "# service=", 10) == 0) 347 | inservice = true; 348 | } else /* Reset the inservice flag */ 349 | inservice = false; 350 | 351 | /* Append read bytes to the end */ 352 | response = realloc(response, added + r); 353 | memcpy(response + added, out, r); 354 | added += r; 355 | } while (r >= PKTSIZELEN); 356 | 357 | response = realloc(response, added + r); 358 | memcpy(response + added, PKTFLUSH, PKTSIZELEN); 359 | 360 | fclose(stream); 361 | 362 | ret = proto_parse_response(response, smart_head); 363 | if (ret) 364 | goto out; 365 | clone_generic_get_pack(chandler, packfd, smart_head); 366 | 367 | /* Jump to the beginning of the file */ 368 | lseek(packfd, 0, SEEK_SET); 369 | 370 | SHA1_Init(&packctx); 371 | SHA1_Init(&idxctx); 372 | 373 | offset = pack_parse_header(packfd, &packfileinfo, &packctx); 374 | index_entry = malloc(sizeof(struct index_entry) * packfileinfo.nobjects); 375 | (void)pack_get_object_meta(packfd, offset, &packfileinfo, 376 | index_entry, &packctx, &idxctx); 377 | //offset = pack_get_object_meta(packfd, offset, &packfileinfo, 378 | // index_entry, &packctx, &idxctx); 379 | close(packfd); 380 | 381 | SHA1_Final(packfileinfo.sha, &packctx); 382 | 383 | /* Sort the index entry */ 384 | qsort(index_entry, packfileinfo.nobjects, sizeof(struct index_entry), 385 | sortindexentry); 386 | 387 | strncpy(path, dotgitpath, PATH_MAX); 388 | strncat(path, "objects/pack/_tmp.idx", PATH_MAX); 389 | idxfd = open(path, O_RDWR | O_CREAT, 0660); 390 | if (idxfd == -1) { 391 | fprintf(stderr, "Unable to open packout.idx for writing.\n"); 392 | ret = -1; 393 | goto out; 394 | } 395 | 396 | pack_build_index(idxfd, &packfileinfo, index_entry, &idxctx); 397 | free(index_entry); 398 | close(idxfd); 399 | 400 | char *suffix = path; 401 | strncpy(path, dotgitpath, PATH_MAX); 402 | suffix += strlcat(path, "objects/pack/pack-", PATH_MAX); 403 | for(int x=0;x<20;x++) 404 | snprintf(suffix+(x*2), 3, "%02x", packfileinfo.sha[x]); 405 | 406 | /* Rename pack and index files */ 407 | strlcat(suffix, ".pack", PATH_MAX); 408 | 409 | strncpy(srcpath, dotgitpath, PATH_MAX); 410 | strlcat(srcpath, "objects/pack/_tmp.pack", PATH_MAX); 411 | rename(srcpath, path); 412 | 413 | strlcpy(srcpath+strlen(srcpath)-4, "idx", 4); 414 | strlcpy(path+strlen(path)-4, "idx", 4); 415 | rename(srcpath, path); 416 | ret = 0; 417 | out: 418 | return (ret); 419 | } 420 | 421 | void 422 | write_refs_head_sha(struct smart_head *s_head, char *repodir) 423 | { 424 | char headrefpath[PATH_MAX]; 425 | int fd; 426 | 427 | snprintf(headrefpath, PATH_MAX, "%s/.git/refs/heads/master", repodir); 428 | fd = open(headrefpath, O_CREAT|O_RDWR, 0666); 429 | 430 | if (write(fd, s_head->sha, HASH_SIZE) != HASH_SIZE) { 431 | fprintf(stderr, "Unable to write 40 bytes to %s, exiting.\n", headrefpath); 432 | exit(127); 433 | } 434 | if (write(fd, "\n", 1) != 1) { 435 | fprintf(stderr, "Unable to write 1 byte to %s, exiting.\n", headrefpath); 436 | exit(127); 437 | } 438 | close(fd); 439 | } 440 | 441 | int 442 | clone_main(int argc, char *argv[]) 443 | { 444 | struct smart_head smart_head; 445 | char *repodir; 446 | char *uri; 447 | char inodepath[PATH_MAX]; 448 | struct clone_handler *chandler; 449 | struct indextree indextree; 450 | struct indexpath indexpath; 451 | struct treeleaf treeleaf; 452 | struct decompressed_object decompressed_object; 453 | struct commitcontent commitcontent; 454 | int nch, ret = 0; 455 | int packfd; 456 | int ch; 457 | int e; 458 | int q = 0; 459 | bool found; 460 | 461 | argc--; argv++; 462 | 463 | while((ch = getopt_long(argc, argv, "", long_options, NULL)) != -1) 464 | switch(ch) { 465 | case 0: 466 | break; 467 | case 1: 468 | break; 469 | default: 470 | printf("Currently not implemented\n"); 471 | return (-1); 472 | } 473 | argc = argc - q; 474 | argv = argv + q; 475 | 476 | uri = argv[1]; 477 | repodir = get_repo_dir(argv[1]); 478 | 479 | /* Set dotgitpath */ 480 | strlcpy(dotgitpath, repodir, PATH_MAX); 481 | strlcat(dotgitpath, "/.git/", PATH_MAX); 482 | 483 | found = false; 484 | chandler = NULL; 485 | for (nch = 0; nch < nitems(clone_handlers); ++nch) { 486 | chandler = &clone_handlers[nch]; 487 | assert(chandler->matcher != NULL); 488 | 489 | if (chandler->matcher(chandler, uri)) { 490 | #ifdef NDEBUG 491 | fprintf(stderr, "Debug: Handler identified: %s\n", 492 | chandler->name); 493 | #endif 494 | found = true; 495 | break; 496 | } 497 | } 498 | 499 | /* This will become stale when we setup the default handler... (ssh?) */ 500 | if (!found && default_handler != NULL) { 501 | fprintf(stderr, "URI Scheme not recognized for '%s'\n", uri); 502 | exit(128); 503 | } else if (!found) { 504 | chandler = default_handler; 505 | } 506 | /* Make directories */ 507 | if (mkdir(repodir, 0755)) { 508 | fprintf(stderr, "Unable to create repodir: %s\n", repodir); 509 | exit(0); 510 | } 511 | if (mkdir(dotgitpath, 0755)) { 512 | fprintf(stderr, "Unable to make dotgitpath: %s\n", dotgitpath); 513 | exit(0); 514 | } 515 | 516 | if (chandler == NULL) { 517 | fprintf(stderr, "Unable to handle remote path: %s\n", uri); 518 | exit(128); 519 | } 520 | 521 | /* Make initial directories */ 522 | init_dirinit(0); 523 | 524 | smart_head.refs = NULL; 525 | STAILQ_INIT(&smart_head.symrefs); 526 | ret = clone_generic(chandler, repodir, &smart_head); 527 | if (ret != 0) 528 | goto out; 529 | 530 | /* Populate .git/pack-refs */ 531 | populate_packed_refs(repodir, &smart_head); 532 | 533 | if (!STAILQ_EMPTY(&smart_head.symrefs)) 534 | populate_symrefs(repodir, &smart_head); 535 | /* Write the initial config file */ 536 | clone_initial_config(uri, repodir); 537 | 538 | /* Write refs master sha */ 539 | write_refs_head_sha(&smart_head, repodir); 540 | 541 | /* Retrieve the commit header and parse it out */ 542 | CONTENT_HANDLER(smart_head.sha, buffer_cb, pack_buffer_cb, 543 | &decompressed_object); 544 | parse_commitcontent(&commitcontent, (char *)decompressed_object.data, 545 | decompressed_object.size); 546 | 547 | strlcpy(inodepath, repodir, PATH_MAX); 548 | ITERATE_TREE(commitcontent.treesha, generate_tree_item, inodepath); 549 | 550 | indextree.version = INDEX_VERSION_2; 551 | indextree.entries = 0; 552 | indexpath.indextree = &indextree; 553 | e = snprintf(inodepath, PATH_MAX, "%s/", repodir); 554 | indexpath.fullpath = inodepath; 555 | indexpath.path = (char *)inodepath + e; 556 | 557 | indextree.dircleaf = NULL; 558 | indextree.treeleaf = &treeleaf; 559 | 560 | /* Terminate the string */ 561 | indexpath.path[0] = '\0'; 562 | 563 | ITERATE_TREE(commitcontent.treesha, index_generate_indextree, &indexpath); 564 | 565 | treeleaf.entry_count = 0; 566 | treeleaf.local_tree_count = 0; 567 | treeleaf.total_tree_count = 0; 568 | treeleaf.subtree = NULL; 569 | sha_str_to_bin(commitcontent.treesha, treeleaf.sha); 570 | 571 | indexpath.current_position = 0; 572 | 573 | treeleaf.ext_size = 0; 574 | ITERATE_TREE(commitcontent.treesha, index_generate_treedata, &indexpath); 575 | 576 | index_calculate_tree_ext_size(&treeleaf); 577 | 578 | strlcpy(inodepath, dotgitpath, PATH_MAX); 579 | strlcat(inodepath, "/index", PATH_MAX); 580 | packfd = open(inodepath, O_CREAT|O_RDWR, 0666); 581 | if (packfd == -1) { 582 | printf("Error with /tmp/mypack"); 583 | exit(0); 584 | } 585 | index_write(&indextree, packfd); 586 | 587 | out: 588 | free(repodir); 589 | // free(treeleaf.subtree); /* Allocated by index_generate_treedata */ 590 | 591 | return (ret); 592 | } 593 | -------------------------------------------------------------------------------- /src/clone.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | 29 | #ifndef __CLONE_H__ 30 | #define __CLONE_H__ 31 | 32 | #include 33 | #include "lib/common.h" 34 | #include "lib/protocol.h" 35 | 36 | #define SSH_PORT 22 37 | 38 | struct clone_handler; 39 | 40 | /* uri, destdir, smart_head */ 41 | typedef int (*clone_uri_scheme)(struct clone_handler *, char *); 42 | typedef FILE *(*clone_run_service)(struct clone_handler *, char *); 43 | typedef FILE *(*clone_get_pack_stream)(struct clone_handler *, char *); 44 | 45 | struct clone_handler { 46 | clone_uri_scheme matcher; 47 | clone_run_service run_service; 48 | clone_get_pack_stream get_pack_stream; 49 | 50 | void *conn_data; 51 | 52 | char **path; 53 | #ifdef NDEBUG 54 | char *name; 55 | #endif 56 | }; 57 | 58 | /* Holds ssh connection data */ 59 | struct conn_ssh { 60 | int fdout; 61 | int fdin; 62 | int fderr; 63 | 64 | char *ssh_user; 65 | char *ssh_host; 66 | int port; 67 | char *ssh_path; 68 | int in; 69 | int out; 70 | int err; 71 | 72 | char **path; 73 | 74 | int bsize; 75 | char *buf; 76 | int bpos; 77 | }; 78 | 79 | /* Holds http connection data */ 80 | struct conn_http { 81 | struct url *fetchurl; 82 | }; 83 | 84 | struct branch { 85 | char sha[41]; 86 | char *name; 87 | }; 88 | 89 | extern struct clone_handler http_handler; 90 | 91 | /* HTTP and HTTPS handler functions */ 92 | int match_http(struct clone_handler *chandler, char *uri); 93 | FILE *http_run_service(struct clone_handler *chandler, char *service); 94 | FILE *http_get_pack_stream(struct clone_handler *chandler, char *content); 95 | 96 | 97 | /* ssh handler functions */ 98 | int match_ssh(struct clone_handler *chandler, char *uri); 99 | FILE *ssh_run_service(struct clone_handler *chandler, char *service); 100 | FILE *ssh_get_pack_stream(struct clone_handler *chandler, char *content); 101 | 102 | int clone_main(int argc, char *argv[]); 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /src/clone_http.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include "lib/pack.h" 38 | #include "lib/protocol.h" 39 | #include "clone.h" 40 | 41 | /* Match the http(s)? scheme */ 42 | int 43 | match_http(struct clone_handler *chandler, char *uri) 44 | { 45 | struct url *fetchurl = fetchParseURL(uri); 46 | 47 | if (fetchurl != NULL) { 48 | struct conn_http *conn_http = malloc(sizeof(struct conn_http)); 49 | conn_http->fetchurl = fetchurl; 50 | chandler->path = &fetchurl->doc; 51 | chandler->conn_data = conn_http; 52 | #ifdef NDEBUG 53 | fprintf(stderr, "debug: Matched as http(s)\n"); 54 | #endif 55 | return 1; 56 | } 57 | 58 | return 0; 59 | } 60 | 61 | /* Gets the current repository state */ 62 | FILE * 63 | http_run_service(struct clone_handler *chandler, char *service) 64 | { 65 | char *savedoc; 66 | FILE *web; 67 | struct conn_http *conn_http = chandler->conn_data; 68 | struct url *fetchurl = conn_http->fetchurl; 69 | char git_upload_pack[1000]; 70 | 71 | snprintf(git_upload_pack, 1000, "%s/info/refs?service=%s", 72 | fetchurl->doc, service); 73 | #ifdef NDEBUG 74 | fprintf(stderr, "debug: fetch url: %s\n", git_upload_pack); 75 | #endif 76 | savedoc = fetchurl->doc; 77 | fetchurl->doc = git_upload_pack; 78 | web = fetchReqHTTP(fetchurl, "GET", NULL, "*/*", ""); 79 | #ifdef NDEBUG 80 | char *errmsg; 81 | switch (fetchLastErrCode) { 82 | case FETCH_ABORT: 83 | errmsg = "FETCH_ABORT"; 84 | break; 85 | case FETCH_AUTH: 86 | errmsg = "FETCH_AUTH"; 87 | break; 88 | case FETCH_DOWN: 89 | errmsg = "FETCH_DOWN"; 90 | break; 91 | case FETCH_EXISTS: 92 | errmsg = "FETCH_EXISTS"; 93 | break; 94 | case FETCH_FULL: 95 | errmsg = "FETCH_FULL"; 96 | break; 97 | case FETCH_INFO: 98 | errmsg = "FETCH_INFO"; 99 | break; 100 | case FETCH_MEMORY: 101 | errmsg = "FETCH_MEMORY"; 102 | break; 103 | case FETCH_MOVED: 104 | errmsg = "FETCH_MOVED"; 105 | break; 106 | case FETCH_NETWORK: 107 | errmsg = "FETCH_NETWORK"; 108 | break; 109 | case FETCH_OK: 110 | errmsg = "FETCH_OK"; 111 | break; 112 | case FETCH_PROTO: 113 | errmsg = "FETCH_PROTO"; 114 | break; 115 | case FETCH_RESOLV: 116 | errmsg = "FETCH_RESOLV"; 117 | break; 118 | case FETCH_SERVER: 119 | errmsg = "FETCH_SERVER"; 120 | break; 121 | case FETCH_TEMP: 122 | errmsg = "FETCH_TEMP"; 123 | break; 124 | case FETCH_TIMEOUT: 125 | errmsg = "FETCH_TIMEOUT"; 126 | break; 127 | case FETCH_UNAVAIL: 128 | errmsg = "FETCH_UNAVAIL"; 129 | break; 130 | case FETCH_UNKNOWN: 131 | errmsg = "FETCH_UNKNOWN"; 132 | break; 133 | case FETCH_URL: 134 | errmsg = "FETCH_URL"; 135 | break; 136 | case FETCH_VERBOSE: 137 | errmsg = "FETCH_VERBOSE"; 138 | break; 139 | default: 140 | errmsg = "Untracked response"; 141 | } 142 | fprintf(stderr, "debug: libfetch response: %s\n", errmsg); 143 | #endif 144 | fetchurl->doc = savedoc; 145 | return web; 146 | } 147 | 148 | /* Returns a file pointer to the connection */ 149 | FILE * 150 | http_get_pack_stream(struct clone_handler *chandler, char *content) 151 | { 152 | char *savedoc; 153 | char git_upload_pack[1000]; 154 | struct conn_http *conn_http = chandler->conn_data; 155 | struct url *fetchurl = conn_http->fetchurl; 156 | FILE *packptr; 157 | 158 | snprintf(git_upload_pack, 1000, "%s/git-upload-pack", fetchurl->doc); 159 | savedoc = fetchurl->doc; 160 | fetchurl->doc = git_upload_pack; 161 | 162 | setenv("HTTP_ACCEPT", "application/x-git-upload-pack-result", 1); 163 | 164 | packptr = fetchReqHTTP(fetchurl, "POST", NULL, "application/x-git-upload-pack-request", content); 165 | if (packptr == NULL) { 166 | fprintf(stderr, "Unable to contact url: %s\n", fetchurl->doc); 167 | exit(128); 168 | } 169 | fetchurl->doc = savedoc; 170 | 171 | return packptr; 172 | } 173 | -------------------------------------------------------------------------------- /src/clone_ssh.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2019 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "lib/pack.h" 40 | #include "lib/protocol.h" 41 | #include "clone.h" 42 | 43 | /* Match the ssh scheme */ 44 | int 45 | match_ssh(struct clone_handler *chandler, char *uri) 46 | { 47 | #define MAXGROUPS 5 48 | regex_t gitpath; 49 | regmatch_t m[MAXGROUPS]; 50 | int r; 51 | 52 | r = regcomp(&gitpath, "(([a-z0-9_-]{0,31})@)?(.*):([a-zA-z\\/]+)(\\.git)?", REG_EXTENDED); 53 | assert(r == 0); 54 | 55 | r = regexec(&gitpath, uri, MAXGROUPS, m, 0); 56 | if (r == 0) { 57 | struct conn_ssh *conn_ssh = malloc(sizeof(struct conn_ssh)); 58 | conn_ssh->ssh_user = strndup(uri+m[2].rm_so, m[2].rm_eo - m[2].rm_so); 59 | conn_ssh->ssh_host = strndup(uri+m[3].rm_so, m[3].rm_eo - m[3].rm_so); 60 | conn_ssh->ssh_path = strndup(uri+m[4].rm_so, m[4].rm_eo - m[4].rm_so); 61 | conn_ssh->port = SSH_PORT; 62 | conn_ssh->bpos = 0; 63 | conn_ssh->buf = NULL; 64 | 65 | conn_ssh->path = &conn_ssh->ssh_path; 66 | chandler->conn_data = conn_ssh; 67 | chandler->path = &conn_ssh->ssh_path; 68 | #ifdef NDEBUG 69 | fprintf(stderr, "debug: Matched as ssh\n"); 70 | #endif 71 | return 1; 72 | } 73 | return 0; 74 | } 75 | 76 | void 77 | setup_connection(struct clone_handler *chandler) 78 | { 79 | struct conn_ssh *conn_ssh = chandler->conn_data; 80 | int filedes1[2], filedes2[2], filedes3[2]; 81 | int pid; 82 | 83 | if (pipe(filedes1) == -1) 84 | goto out; 85 | if (pipe(filedes2) == -1) 86 | goto out; 87 | if (pipe(filedes3) == -1) 88 | goto out; 89 | 90 | pid = fork(); 91 | 92 | if (pid == 0) { 93 | if (dup2(filedes1[0], STDIN_FILENO) == -1) 94 | goto out; 95 | if (dup2(filedes2[1], STDOUT_FILENO) == -1) 96 | goto out; 97 | if (dup2(filedes3[1], STDERR_FILENO) == -1) 98 | goto out; 99 | #ifdef NDEBUG 100 | fprintf(stderr, "debut: Executing /usr/bin/ssh ssh -l %s %s git-upload-pack %s\n", 101 | conn_ssh->ssh_user, conn_ssh->ssh_host, conn_ssh->ssh_path); 102 | #endif 103 | execl("/usr/bin/ssh", "ssh", "-l", conn_ssh->ssh_user, conn_ssh->ssh_host, "git-upload-pack", conn_ssh->ssh_path, NULL); 104 | } 105 | else { 106 | close(filedes1[0]); 107 | close(filedes2[1]); 108 | close(filedes3[0]); 109 | conn_ssh->out = filedes1[1]; 110 | conn_ssh->in = filedes2[0]; 111 | conn_ssh->err = filedes3[1]; 112 | } 113 | 114 | return; 115 | out: 116 | fprintf(stderr, "Unable to setup connection, exiting.\n"); 117 | exit(-1); 118 | } 119 | 120 | static int 121 | ssh_readfn(void *v, char *buf, int len) 122 | { 123 | struct conn_ssh *conn_ssh = v; 124 | int b, rlen; 125 | 126 | if (conn_ssh->bsize == 0 || conn_ssh->buf == NULL || conn_ssh->bsize == conn_ssh->bpos) { 127 | conn_ssh->buf = realloc(conn_ssh->buf, len); 128 | b = read(conn_ssh->in, conn_ssh->buf, len); 129 | conn_ssh->bsize = b; 130 | conn_ssh->bpos = 0; 131 | } 132 | 133 | rlen = conn_ssh->bsize - conn_ssh->bpos; 134 | if (len < rlen) 135 | rlen = len; 136 | 137 | memcpy(buf, conn_ssh->buf - conn_ssh->bpos, len); 138 | conn_ssh->bpos += rlen; 139 | 140 | return (rlen); 141 | } 142 | 143 | static int 144 | ssh_writefn(void *v, const char *buf, int len) 145 | { 146 | fprintf(stderr, "Writing currently unimplemented, exiting\n"); 147 | exit(0); 148 | return (0); 149 | } 150 | 151 | FILE * 152 | ssh_run_service(struct clone_handler *chandler, char *service) 153 | { 154 | FILE *stream; 155 | 156 | setup_connection(chandler); 157 | stream = funopen(chandler->conn_data, ssh_readfn, ssh_writefn, NULL, NULL); 158 | 159 | if (stream == NULL) { 160 | fprintf(stderr, "Failed to open stream, exiting.\n"); 161 | exit(-1); 162 | } 163 | 164 | return stream; 165 | } 166 | 167 | FILE * 168 | ssh_get_pack_stream(struct clone_handler *chandler, char *content) 169 | { 170 | struct conn_ssh *conn_ssh = chandler->conn_data; 171 | write(conn_ssh->out, content, strlen(content)); 172 | return fdopen(conn_ssh->in, "r"); 173 | } 174 | -------------------------------------------------------------------------------- /src/hash-object.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include "lib/zlib-handler.h" 42 | #include "lib/common.h" 43 | #include "lib/ini.h" 44 | #include "hash-object.h" 45 | 46 | static struct option long_options[] = 47 | { 48 | {"stdin", no_argument, NULL, 1}, 49 | {"stdin-paths", no_argument, NULL, 0}, 50 | {"no-filters", no_argument, NULL, 0}, 51 | {"literally", no_argument, NULL, 0}, 52 | {"path", required_argument, NULL, 0}, 53 | {NULL, 0, NULL, 0} 54 | }; 55 | 56 | static int 57 | hash_object_usage(int type) 58 | { 59 | fprintf(stderr, "usage: git hash-object [-t ] [-w] [--path= | --no-filters] [--stdin] [--] ...\n"); 60 | fprintf(stderr, " or: git hash-object --stdin-paths\n"); 61 | fprintf(stderr, "\n"); 62 | fprintf(stderr, " -t \t\tobject type\n"); 63 | fprintf(stderr, " -w\t\t\twrite the object into the object database\n"); 64 | fprintf(stderr, " --stdin\t\tread the object from stdin\n"); 65 | fprintf(stderr, " --stdin-paths\tread file names from stdin\n"); 66 | fprintf(stderr, " --no-filters\tstore file as is without filters\n"); 67 | fprintf(stderr, " --literally\t\tjust hash any random garbage to create corrupt objects for debugging Git\n"); 68 | fprintf(stderr, " --path \tprocess file as it were from this path\n"); 69 | fprintf(stderr, "\n"); 70 | return (0); 71 | } 72 | 73 | /* May move this to lib/zlib-handler.[ch] */ 74 | static int 75 | add_zlib_content(z_stream *strm, FILE *dest, int flush) 76 | { 77 | int ret; 78 | unsigned have; 79 | Bytef out[CHUNK]; 80 | 81 | do { 82 | strm->avail_out = CHUNK; 83 | strm->next_out = out; 84 | ret = deflate(strm, flush); 85 | assert(ret != Z_STREAM_ERROR); 86 | have = CHUNK - strm->avail_out; 87 | if (fwrite(out, 1, have, dest) != have || ferror(dest)) { 88 | (void)deflateEnd(strm); 89 | exit(Z_ERRNO); 90 | } 91 | 92 | } while(strm->avail_out == 0); 93 | return ret; 94 | } 95 | 96 | static int 97 | hash_object_write(uint8_t flags, struct decompressed_object dobject, int objtype) 98 | { 99 | int r; 100 | int flush; 101 | int destfd; 102 | int used = 0; 103 | FILE *dest; 104 | char checksum[HEX_DIGEST_LENGTH]; 105 | char tpath[PATH_MAX]; 106 | char objpath[PATH_MAX]; 107 | SHA1_CTX context; 108 | Bytef in[CHUNK]; 109 | z_stream strm; 110 | 111 | strm.avail_in = snprintf((char*)in, CHUNK, "%s %ld", object_name[objtype], dobject.size) + 1; 112 | SHA1_Init(&context); 113 | SHA1_Update(&context, in, strm.avail_in); 114 | 115 | if (flags & CMD_HASH_OBJECT_WRITE) { 116 | strm.next_in = in; 117 | strlcpy(tpath, dotgitpath, PATH_MAX); 118 | strlcat(tpath, "/objects/obj.XXXXXX", PATH_MAX); 119 | 120 | strm.zalloc = Z_NULL; 121 | strm.zfree = Z_NULL; 122 | strm.opaque = Z_NULL; 123 | r = deflateInit(&strm, Z_BEST_SPEED); 124 | if (r != Z_OK) { 125 | fprintf(stderr, "Unable to initiate zlib object, exiting.\n"); 126 | exit(r); 127 | } 128 | 129 | destfd = mkstemp(tpath); 130 | if (destfd == -1) { 131 | fprintf(stderr, "Unable to temporary file %s, exiting.\n", tpath); 132 | exit(0); 133 | } 134 | dest = fdopen(destfd, "w"); 135 | if (dest == NULL) { 136 | fprintf(stderr, "Unable to fdopen() file, exiting.\n"); 137 | exit(0); 138 | } 139 | add_zlib_content(&strm, dest, Z_NO_FLUSH); 140 | } 141 | 142 | do { 143 | strm.next_in = dobject.data + used; 144 | if (used + CHUNK > dobject.size) { 145 | strm.avail_in = (dobject.size - used); 146 | flush = Z_FINISH; 147 | } 148 | else { 149 | strm.avail_in = CHUNK; 150 | flush = Z_NO_FLUSH; 151 | } 152 | SHA1_Update(&context, dobject.data+used, strm.avail_in); 153 | used = used + CHUNK; 154 | 155 | if (flags & CMD_HASH_OBJECT_WRITE) 156 | r = add_zlib_content(&strm, dest, flush); 157 | } while(flush != Z_FINISH); 158 | 159 | SHA1_End(&context, checksum); 160 | 161 | if (flags & CMD_HASH_OBJECT_WRITE) { 162 | assert(r == Z_STREAM_END); 163 | (void)deflateEnd(&strm); 164 | 165 | /* Build the object directory prefix */ 166 | snprintf(objpath, PATH_MAX, "%s/objects/%c%c/", dotgitpath, 167 | checksum[0], checksum[1]); 168 | mkdir(objpath, 0755); 169 | 170 | /* Add the rest of the checksum path */ 171 | strlcat(objpath+3, checksum+2, sizeof(objpath)-3); 172 | rename(tpath, objpath); 173 | } 174 | 175 | printf("%s\n", checksum); 176 | return (0); 177 | } 178 | 179 | int 180 | hash_object_main(int argc, char *argv[]) 181 | { 182 | int ret = 0; 183 | int ch; 184 | int q = 0; 185 | int8_t flags = 0; 186 | struct decompressed_object dobject; 187 | 188 | argc--; argv++; 189 | 190 | while((ch = getopt_long(argc, argv, "wt:", long_options, NULL)) != -1) 191 | switch(ch) { 192 | case 'w': 193 | flags |= CMD_HASH_OBJECT_WRITE; 194 | q++; 195 | break; 196 | case 1: 197 | flags |= CMD_HASH_OBJECT_STDIN; 198 | q++; 199 | break; 200 | default: 201 | printf("Currently not implemented\n"); 202 | hash_object_usage(0); 203 | return (-1); 204 | } 205 | argc = argc - q; 206 | argv = argv + q; 207 | 208 | git_repository_path(); 209 | 210 | if (flags & CMD_HASH_OBJECT_STDIN) { 211 | printf("stdin, currently unimplemented, exiting.\n"); 212 | exit(0); 213 | } 214 | 215 | if (argv[1]) { 216 | int fd; 217 | struct stat sb; 218 | 219 | fd = open(argv[1], O_RDONLY); 220 | if (fd == -1) { 221 | fprintf(stderr, "Unable to open file %s, exiting.\n", argv[1]); 222 | exit(0); 223 | } 224 | 225 | if (fstat(fd, &sb) != 0) { 226 | fprintf(stderr, "Unable to fstat(2) %s, exiting.\n", argv[1]); 227 | exit(127); 228 | } 229 | dobject.size = sb.st_size; 230 | dobject.data = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 231 | ret = hash_object_write(flags, dobject, OBJ_BLOB); 232 | munmap(dobject.data, sb.st_size); 233 | } 234 | 235 | return (ret); 236 | } 237 | -------------------------------------------------------------------------------- /src/hash-object.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | 29 | #ifndef __OBJECT_HASH_H_ 30 | #define __OBJECT_HASH_H_ 31 | 32 | #define HEX_DIGEST_LENGTH 257 33 | 34 | /* Commands */ 35 | #define CMD_HASH_OBJECT_WRITE BIT(1) 36 | #define CMD_HASH_OBJECT_STDIN BIT(2) 37 | 38 | int hash_object_main(int argc, char *argv[]); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/index-pack.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include "lib/zlib-handler.h" 43 | #include "lib/buffering.h" 44 | #include "lib/common.h" 45 | #include "lib/ini.h" 46 | #include "lib/pack.h" 47 | #include "index-pack.h" 48 | #include "clone.h" 49 | 50 | static struct option long_options[] = 51 | { 52 | {NULL, 0, NULL, 0} 53 | }; 54 | 55 | /* 56 | ssize_t 57 | sha_write(int fd, const void *buf, size_t nbytes, SHA1_CTX *idxctx) 58 | { 59 | SHA1_Update(idxctx, buf, nbytes); 60 | return write(fd, buf, nbytes); 61 | } 62 | */ 63 | 64 | void 65 | index_pack_usage(int type) 66 | { 67 | fprintf(stderr, "usage: ogit index-pack [-v] [-o ] [--keep | --keep=] [--verify] [--strict] ( | --stdin [--fix-thin] [])\n"); 68 | exit(128); 69 | } 70 | 71 | int 72 | index_pack_main(int argc, char *argv[]) 73 | { 74 | int ret = 0; 75 | int ch; 76 | int q = 0; 77 | 78 | argc--; argv++; 79 | 80 | while((ch = getopt_long(argc, argv, "", long_options, NULL)) != -1) 81 | switch(ch) { 82 | case 0: 83 | break; 84 | case 1: 85 | break; 86 | default: 87 | printf("Currently not implemented\n"); 88 | return (-1); 89 | } 90 | argc = argc - q; 91 | argv = argv + q; 92 | 93 | if (strncmp(".pack", argv[1] + strlen(argv[1])-5, 5) != 0) { 94 | fprintf(stderr, "fatal: packfile name '%s' does not end with '.pack'\n", argv[1]); 95 | exit(128); 96 | } 97 | 98 | int packfd; 99 | int idxfd; 100 | struct packfileinfo packfileinfo; 101 | struct index_entry *index_entry; 102 | int offset; 103 | int x; 104 | SHA1_CTX packctx; 105 | SHA1_CTX idxctx; 106 | SHA1_Init(&packctx); 107 | SHA1_Init(&idxctx); 108 | 109 | /* Parse the pack file */ 110 | packfd = open(argv[1], O_RDONLY); 111 | if (packfd == -1) { 112 | fprintf(stderr, "fatal: cannot open packfile '%s'\n", argv[1]); 113 | exit(128); 114 | } 115 | offset = pack_parse_header(packfd, &packfileinfo, &packctx); 116 | index_entry = malloc(sizeof(struct index_entry) * packfileinfo.nobjects); 117 | // offset = pack_get_object_meta(packfd, offset, &packfileinfo, index_entry, &packctx, &idxctx); 118 | (void)pack_get_object_meta(packfd, offset, &packfileinfo, index_entry, &packctx, &idxctx); 119 | close(packfd); 120 | SHA1_Final(packfileinfo.sha, &packctx); 121 | 122 | /* Sort the index_entry */ 123 | qsort(index_entry, packfileinfo.nobjects, 124 | sizeof(struct index_entry), sortindexentry); 125 | 126 | /* Build out the Index File */ 127 | idxfd = open("packout.idx", O_RDWR | O_CREAT | O_TRUNC, 0666); 128 | if (idxfd == -1) { 129 | fprintf(stderr, "Unable to open packout.idx for writing\n"); 130 | exit(idxfd); 131 | } 132 | pack_build_index(idxfd, &packfileinfo, index_entry, &idxctx); 133 | close(idxfd); 134 | 135 | free(index_entry); 136 | /* Output the SHA to the terminal */ 137 | for(x=0;x<20;x++) 138 | printf("%02x", packfileinfo.sha[x]); 139 | 140 | return (ret); 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/index-pack.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef __PACK_INDEX_H__ 29 | #define __PACK_INDEX_H__ 30 | 31 | int index_pack_main(int argc, char *argv[]); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #define _WITH_DPRINTF 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "lib/common.h" 40 | #include "lib/ini.h" 41 | #include "init.h" 42 | 43 | int longopt; 44 | 45 | static struct option long_options[] = 46 | { 47 | {"quiet", no_argument, NULL, 'q'}, 48 | {"bare", no_argument, &longopt, INIT_BARE}, 49 | {"template", required_argument, NULL, 0}, 50 | {"separate-git-dir", required_argument, NULL, 0}, 51 | {"shared", required_argument, NULL, 0}, 52 | {NULL, 0, NULL, 0} 53 | }; 54 | 55 | char *init_dirs[] = { 56 | "objects", 57 | "objects/pack", 58 | "objects/info", 59 | "refs", 60 | "refs/tags", 61 | "refs/heads", 62 | "branches", 63 | "hooks", 64 | }; 65 | 66 | void 67 | init_usage() 68 | { 69 | printf("usage: ogit init [-q | --quiet] [--bare] [--template=] [--shared[=]] []\n\n"); 70 | printf("\t--template \n"); 71 | printf("\t\t\tdirectory from which templates will be used\n"); 72 | printf("\t--bare\t\tcreate a bare repository\n"); 73 | printf("\t--shared[=]\n"); 74 | printf("\t\t\tspecify that the git repository is to be shared amongst several users\n"); 75 | printf("\t-q, --quiet\tbe quiet\n"); 76 | printf("\t--separate-git-dir \n"); 77 | printf("\t\t\tseparate git dir from working tree\n\n"); 78 | exit(EXIT_INVALID_COMMAND); 79 | } 80 | 81 | /* 82 | * Initializes the directory 83 | * Does not initialize the .git/config file 84 | * The 'path' variable must end in a trailing '/' 85 | */ 86 | int 87 | init_dirinit(int flags) 88 | { 89 | struct stat sb; 90 | char *suffix; 91 | char path[PATH_MAX]; 92 | int reinit = 0; 93 | int fd; 94 | int ret; 95 | int x; 96 | 97 | x = strlcpy(path, dotgitpath, PATH_MAX); 98 | 99 | suffix = path + x; 100 | 101 | /* This block checks if the file already exists. If so, sets reinit */ 102 | for(x = 0; x < nitems(init_dirs); x++) { 103 | strlcat(path, init_dirs[x], PATH_MAX); 104 | if (!stat(path, &sb)) 105 | reinit = 1; 106 | else { 107 | ret = mkdir(path, 0755); 108 | if (ret == -1) { 109 | fprintf(stderr, "Cannot create %s\n", path); 110 | exit(ret); 111 | } 112 | } 113 | suffix[0] = '\0'; 114 | } 115 | 116 | /* Create an empty 'description' file */ 117 | strlcat(path, "description", PATH_MAX); 118 | if (!stat(path, &sb)) 119 | reinit = 1; 120 | else { 121 | fd = open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); 122 | if (fd == -1) { 123 | fprintf(stderr, "Cannot create description file\n"); 124 | return (-1); 125 | } 126 | close(fd); 127 | } 128 | suffix[0] = '\0'; 129 | 130 | strlcat(path, "HEAD", PATH_MAX); 131 | if (!stat(path, &sb)) 132 | reinit = 1; 133 | else { 134 | fd = open(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 135 | if (fd != -1) 136 | write(fd, "ref: refs/heads/master\n", 23); 137 | close(fd); 138 | } 139 | suffix[0] = '\0'; 140 | 141 | return (reinit); 142 | } 143 | 144 | int 145 | init_main(int argc, char *argv[]) 146 | { 147 | struct section core; 148 | int reinit; 149 | int fd; 150 | int q = 0; 151 | int ch; 152 | struct stat sb; 153 | char *directory = NULL; 154 | uint8_t flags = 0; 155 | char path[PATH_MAX]; 156 | 157 | argc--; argv++; 158 | 159 | while((ch = getopt_long(argc, argv, "q", long_options, NULL)) != -1) { 160 | switch(ch) { 161 | case 'q': 162 | flags |= INIT_QUIET; 163 | break; 164 | case 0: 165 | flags |= INIT_BARE; 166 | break; 167 | default: 168 | init_usage(); 169 | break; 170 | } 171 | q++; 172 | } 173 | 174 | argc = argc - q; 175 | argv = argv + q; 176 | 177 | if (argc >= 2) 178 | directory = argv[1]; 179 | 180 | if (directory) { 181 | strlcpy(repodir, directory, PATH_MAX); 182 | strlcat(repodir, "/", PATH_MAX); 183 | if (stat(repodir, &sb)) { 184 | if (mkdir(repodir, 0755)) { 185 | fprintf(stderr, "1 Unable to create %s\n", repodir); 186 | exit(EXIT_FAILURE); 187 | } 188 | } 189 | } 190 | else 191 | repodir[0] = '\0'; 192 | 193 | /* Create .git or a bare repo directory */ 194 | if (!(flags & INIT_BARE)) { 195 | strncpy(dotgitpath, repodir, PATH_MAX); 196 | strncat(dotgitpath, ".git/", PATH_MAX); 197 | if (stat(dotgitpath, &sb)) { 198 | if (mkdir(dotgitpath, 0755)) { 199 | fprintf(stderr, "2 Unable to create %s\n", dotgitpath); 200 | exit(EXIT_FAILURE); 201 | } 202 | } 203 | } 204 | else 205 | strlcat(dotgitpath, repodir, PATH_MAX); 206 | 207 | 208 | reinit = init_dirinit(flags); 209 | 210 | strlcpy(path, dotgitpath, PATH_MAX); 211 | strlcat(path, "config", PATH_MAX); 212 | if (!stat(path, &sb)) 213 | reinit = 1; 214 | else { 215 | fd = open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); 216 | if (fd == -1) { 217 | fprintf(stderr, "Unable to open %s: %s\n", repodir, strerror(errno)); 218 | exit(errno); 219 | } 220 | 221 | core.type = CORE; 222 | core.repositoryformatversion = 0; 223 | core.filemode = TRUE; 224 | if (flags & INIT_BARE) 225 | core.bare = TRUE; 226 | else 227 | core.bare = FALSE; 228 | core.logallrefupdates = TRUE; 229 | core.next = NULL; 230 | ini_write_config(fd, &core); 231 | } 232 | 233 | getcwd((char *)&path, PATH_MAX); 234 | 235 | /* Construct the trailing message */ 236 | if (!(flags & INIT_QUIET)) { 237 | if (reinit) 238 | printf("Reinitialized existing "); 239 | else 240 | printf("Initialized empty "); 241 | printf("Git repository in %s", path); 242 | if (!(flags & INIT_BARE)) 243 | printf("/.git/\n"); 244 | else 245 | printf("/\n"); 246 | } 247 | 248 | return (EXIT_SUCCESS); 249 | } 250 | -------------------------------------------------------------------------------- /src/init.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef _REMOTE_H_ 29 | #define _REMOTE_H_ 30 | 31 | #define INIT_QUIET 0x1 32 | #define INIT_BARE 0x2 33 | 34 | int init_dirinit(int flags); 35 | int init_main(int argc, char *argv[]); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "lib/zlib-handler.h" 40 | #include "lib/common.h" 41 | #include "lib/pack.h" 42 | #include "lib/ini.h" 43 | #include "log.h" 44 | #include "ogit.h" 45 | 46 | static int limit = -1; 47 | 48 | static struct option long_options[] = 49 | { 50 | {"color", optional_argument, NULL, 'c'}, 51 | {"limit", required_argument, NULL, 'l'}, 52 | {NULL, 0, NULL, 0} 53 | }; 54 | 55 | void 56 | log_usage(int type) 57 | { 58 | fprintf(stderr, "usage: git log [] [] [[--] ...]\n"); 59 | fprintf(stderr, " or: git show [] ...\n\n"); 60 | fprintf(stderr, " -q, --quiet suppress diff output\n"); 61 | fprintf(stderr, " --source show source\n"); 62 | fprintf(stderr, " --use-mailmap Use mail map file\n"); 63 | fprintf(stderr, " --decorate-refs \n"); 64 | fprintf(stderr, " only decorate refs that match \n"); 65 | fprintf(stderr, " --decorate-refs-exclude \n"); 66 | fprintf(stderr, " do not decorate refs that match \n"); 67 | fprintf(stderr, " --decorate[=...] decorate options\n"); 68 | fprintf(stderr, " -L Process line range n,m in file, counting from 1\n\n"); 69 | exit(128); 70 | } 71 | 72 | void 73 | log_print_commit_headers(struct commitcontent *commitcontent) 74 | { 75 | char datestr[50]; 76 | 77 | ctime_r(&commitcontent->author_time, datestr); 78 | datestr[strlen(datestr)-1] = '\0'; 79 | 80 | printf("%scommit %s%s\n", color ? "\e[0;33m" : "", commitcontent->commitsha, 81 | color ? "\e[0m" : ""); 82 | 83 | printf("Author:\t%s <%s>\n", commitcontent->author_name, commitcontent->author_email); 84 | printf("Date:\t%s %s\n\n", datestr, commitcontent->author_tz); 85 | } 86 | 87 | void 88 | log_print_message(struct commitcontent *commitcontent) 89 | { 90 | for(int x=0;xlines;x++) 91 | printf(" %s\n", commitcontent->message[x]); 92 | } 93 | 94 | void 95 | log_get_start_sha(struct logarg *logarg) 96 | { 97 | int headfd; 98 | char headfile[PATH_MAX]; 99 | char refpath[PATH_MAX]; 100 | char ref[HASH_SIZE]; 101 | int l; 102 | 103 | snprintf(headfile, sizeof(headfile), "%s/HEAD", dotgitpath); 104 | headfd = open(headfile, O_RDONLY); 105 | if (headfd == -1) { 106 | fprintf(stderr, "Error, no HEAD file found. This may not be a git directory\n"); 107 | exit(128); 108 | } 109 | 110 | read(headfd, ref, 5); 111 | if (strncmp(ref, "ref: ", 5) == 0) { 112 | ref[6] = '\0'; 113 | l = read(headfd, ref, PATH_MAX) - 1; 114 | if (ref[l] == '\n') 115 | ref[l] = '\0'; 116 | snprintf(refpath, sizeof(refpath), "%s/%s", dotgitpath, ref); 117 | } 118 | else { 119 | read(headfd, ref + 5, HASH_SIZE - 5); 120 | strlcpy(logarg->sha, ref, sizeof(logarg->sha)); 121 | close(headfd); 122 | return; 123 | } 124 | close(headfd); 125 | 126 | headfd = open(refpath, O_RDONLY); 127 | if(headfd == -1) { 128 | fprintf(stderr, "failed to open %s\n", refpath); 129 | exit(128); 130 | } 131 | 132 | read(headfd, logarg->sha, HASH_SIZE); 133 | close(headfd); 134 | } 135 | 136 | void 137 | log_display_commits() 138 | { 139 | struct decompressed_object decompressed_object; 140 | struct commitcontent commitcontent; 141 | struct logarg logarg; 142 | int source; 143 | int hdr_offset = 0; 144 | 145 | bzero(&logarg, sizeof(struct logarg)); 146 | log_get_start_sha(&logarg); 147 | 148 | logarg.status = LOG_STATUS_PARENT; 149 | 150 | bzero(&commitcontent, sizeof(struct commitcontent)); 151 | 152 | while(logarg.status & LOG_STATUS_PARENT) { 153 | decompressed_object.size = 0; 154 | decompressed_object.data = NULL; 155 | 156 | commitcontent.commitsha = logarg.sha; 157 | /* 158 | * Expansion of the CONTENT_HANDLER macro to capture the source. 159 | * This will be added to the macro if additional use-cases arise. 160 | */ 161 | if (loose_content_handler(commitcontent.commitsha, buffer_cb, &decompressed_object)) { 162 | source = SOURCE_PACK; 163 | pack_content_handler(commitcontent.commitsha, pack_buffer_cb, &decompressed_object); 164 | } 165 | else 166 | source = SOURCE_LOOSE; 167 | 168 | if (source == SOURCE_LOOSE) { 169 | // XXX This is an unnecessary variable, loose_get_headers's does two things 170 | // Better approach would be to calculate the hdr_offset from the type and size 171 | struct loosearg loosearg; 172 | hdr_offset = loose_get_headers(decompressed_object.data, 173 | decompressed_object.size, &loosearg); 174 | } 175 | else 176 | hdr_offset = 0; 177 | parse_commitcontent(&commitcontent, 178 | (char *)decompressed_object.data + hdr_offset, 179 | decompressed_object.size - hdr_offset); 180 | 181 | log_print_commit_headers(&commitcontent); 182 | log_print_message(&commitcontent); 183 | if (commitcontent.numparent == 0) { 184 | logarg.status &= ~LOG_STATUS_PARENT; 185 | break; 186 | } 187 | 188 | strlcpy(commitcontent.commitsha, commitcontent.parent[0], HASH_SIZE+1); 189 | free_commitcontent(&commitcontent); 190 | free(decompressed_object.data); 191 | } 192 | exit(0); 193 | } 194 | 195 | int 196 | log_main(int argc, char *argv[]) 197 | { 198 | int ret = 0; 199 | int ch, prevch; 200 | int q = 0; 201 | 202 | argc--; argv++; 203 | 204 | prevch = '\0'; 205 | while((ch = getopt_long(argc, argv, "0123456789c::", long_options, NULL)) != -1) { 206 | switch(ch) { 207 | case 0: 208 | break; 209 | case 1: 210 | break; 211 | case 'c': 212 | parse_color_opt(optarg); 213 | break; 214 | case '0': case '1': case '2': case '3': case '4': 215 | case '5': case '6': case '7': case '8': case '9': 216 | switch (prevch) { 217 | case '0': case '1': case '2': case '3': case '4': 218 | case '5': case '6': case '7': case '8': case '9': 219 | limit = limit * 10 + (ch - '0'); 220 | break; 221 | default: 222 | limit = ch - '0'; 223 | break; 224 | } 225 | 226 | break; 227 | default: 228 | printf("Currently not implemented\n"); 229 | return (-1); 230 | } 231 | 232 | prevch = ch; 233 | } 234 | argc = argc - q; 235 | argv = argv + q; 236 | 237 | if (git_repository_path() == -1) { 238 | fprintf(stderr, "fatal: not a git repository (or any of the parent directories): .git"); 239 | exit(0); 240 | } 241 | config_parser(); 242 | 243 | log_display_commits(); 244 | 245 | return (ret); 246 | } 247 | 248 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef __LOG_H__ 29 | #define __LOG_H__ 30 | 31 | #define LOG_STATUS_COMMIT 0x00 32 | #define LOG_STATUS_HEADERS 0x01 33 | #define LOG_STATUS_PARENT 0x10 34 | #define LOG_STATUS_AUTHOR 0x20 35 | 36 | struct logarg { 37 | int type; 38 | int status; 39 | char sha[41]; 40 | char next_commit[41]; 41 | char *headers; 42 | int size; 43 | }; 44 | 45 | int log_main(int argc, char *argv[]); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/ogit.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "lib/common.h" 35 | #include "update-index.h" 36 | #include "hash-object.h" 37 | #include "index-pack.h" 38 | #include "cat-file.h" 39 | #include "clone.h" 40 | #include "ogit.h" 41 | #include "init.h" 42 | 43 | extern int remote_main(); 44 | extern int log_main(); 45 | 46 | static struct cmd cmds[] = { 47 | {"remote", remote_main}, 48 | {"init", init_main}, 49 | {"hash-object", hash_object_main}, 50 | {"update-index", update_index_main}, 51 | {"cat-file", cat_file_main}, 52 | {"log", log_main}, 53 | {"clone", clone_main}, 54 | {"index-pack", index_pack_main} 55 | }; 56 | 57 | bool color = true; 58 | 59 | int cmd_count = nitems(cmds); 60 | 61 | void 62 | parse_color_opt(const char *optarg) 63 | { 64 | 65 | if (optarg == NULL || strcasecmp(optarg, "always") == 0) 66 | color = true; 67 | else if (strcasecmp(optarg, "never") == 0) 68 | color = false; 69 | else if (strcasecmp(optarg, "auto") == 0) 70 | color = isatty(STDOUT_FILENO); 71 | } 72 | 73 | void 74 | usage() 75 | { 76 | printf("usage: git []\n\n"); 77 | printf("These are common OpenGit commands used in various situations:\n\n"); 78 | printf("start a working area\n"); 79 | printf(" clone Clone a repository into a new directory\n"); 80 | printf(" init Create an empty Git repository or reinitialize an existing one\n"); 81 | printf("\n"); 82 | printf("Repository management\n"); 83 | printf(" remote Manage set of tracked repositories\n"); 84 | printf("\n"); 85 | printf("plumming commands\n"); 86 | printf(" cat-file Check object existence or emit object contents\n"); 87 | printf(" hash-object Computes object ID and optionally create an object from a file\n"); 88 | printf(" update-index Register file contents in the working tree to the index\n"); 89 | printf("\n"); 90 | exit(0); 91 | } 92 | 93 | int 94 | main(int argc, char *argv[]) 95 | { 96 | int ch; 97 | 98 | if (argc == 1) 99 | usage(); 100 | 101 | for (ch = 0; ch < cmd_count; ch++) { 102 | if (strncmp(cmds[ch].c_arg, argv[1], strlen(cmds[ch].c_arg)) == 0 && 103 | strncmp(cmds[ch].c_arg, argv[1], strlen(argv[1])) == 0) { 104 | break; 105 | } 106 | } 107 | 108 | if (ch == cmd_count) 109 | usage(); 110 | 111 | cmds[ch].c_func(argc, argv); 112 | 113 | return (0); 114 | } 115 | -------------------------------------------------------------------------------- /src/ogit.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef _OGIT_H_ 29 | #define _OGIT_H_ 30 | 31 | #include 32 | #include 33 | 34 | struct cmd { 35 | const char *c_arg; 36 | int (*c_func)(int argc, char *argv[]); 37 | }; 38 | 39 | extern int cmd_count; 40 | 41 | extern bool color; 42 | 43 | void parse_color_opt(const char *optarg); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/remote.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #define _WITH_DPRINTF 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include "lib/common.h" 38 | #include "lib/ini.h" 39 | #include "remote.h" 40 | 41 | static struct option long_options[] = 42 | { 43 | {"verbose", no_argument, NULL, 'v'}, 44 | {NULL, 0, NULL, 0} 45 | }; 46 | 47 | int 48 | remote_usage(int type) 49 | { 50 | if (type == REMOTE_USAGE_DEFAULT) { 51 | printf("Remote usage statement\n"); 52 | return (0); 53 | } 54 | else if (type == REMOTE_USAGE_REMOVE) { 55 | printf("usage: ogit remote remove \n"); 56 | return (129); 57 | } 58 | 59 | return (0); 60 | } 61 | 62 | int 63 | remote_remove(int argc, char *argv[], uint8_t flags) 64 | { 65 | char tmpconfig[PATH_MAX]; 66 | char *repopath; 67 | struct section *cur_section = sections; 68 | int fd; 69 | int match = 0; 70 | 71 | if (argc != 2) 72 | return (remote_usage(REMOTE_USAGE_REMOVE)); 73 | 74 | repopath = argv[1]; 75 | 76 | while (cur_section) { 77 | if (cur_section->type == REMOTE && \ 78 | !strncmp(cur_section->repo_name, repopath, strlen(repopath))) { 79 | match = 1; 80 | break; 81 | } 82 | cur_section = cur_section->next; 83 | } 84 | 85 | if (match == 0) { 86 | fprintf(stderr, "fatal: No such remote: %s\n", repopath); 87 | return (128); 88 | } 89 | 90 | cur_section = sections; 91 | 92 | snprintf(tmpconfig, sizeof(tmpconfig), "%s/.config.XXXXXX", dotgitpath); 93 | fd = mkstemp(tmpconfig); 94 | if (fd == -1) { 95 | fprintf(stderr, "Unable to open temporary file: %s\n", tmpconfig); 96 | return (-1); 97 | } 98 | 99 | /* Rewrite config file */ 100 | ini_write_config(fd, cur_section); 101 | close(fd); 102 | 103 | return (0); 104 | } 105 | 106 | int 107 | remote_list(int argc, char *argv[], uint8_t flags) 108 | { 109 | struct section *cur_section = sections; 110 | 111 | /* Usage if the default list command syntax is broken */ 112 | if (argc > 1 && flags != OPT_VERBOSE) 113 | remote_usage(REMOTE_USAGE_DEFAULT); 114 | 115 | while (cur_section) { 116 | if (cur_section->type == REMOTE) { 117 | if (!(flags & OPT_VERBOSE)) 118 | printf("%s\n", cur_section->repo_name); 119 | else { 120 | printf("%s\t%s (fetch)\n", 121 | cur_section->repo_name, 122 | cur_section->url); 123 | printf("%s\t%s (push)\n", 124 | cur_section->repo_name, 125 | cur_section->url); 126 | } 127 | } 128 | cur_section = cur_section->next; 129 | } 130 | 131 | return (0); 132 | } 133 | 134 | int 135 | remote_main(int argc, char *argv[]) 136 | { 137 | int ret = 0; 138 | int ch; 139 | 140 | uint8_t cmd = 0; 141 | uint8_t flags = 0; 142 | 143 | argc--; argv++; 144 | 145 | if (argc > 1) { 146 | if (strncmp(argv[1], "add", 3) == 0) { 147 | argc--; 148 | argv++; 149 | cmd = CMD_ADD; 150 | } 151 | else if (strncmp(argv[1], "remove", 6) == 0) { 152 | argc--; 153 | argv++; 154 | cmd = CMD_REMOVE; 155 | } 156 | } 157 | 158 | while ((ch = getopt_long(argc, argv, "v", long_options, NULL)) != -1) 159 | switch (ch) { 160 | case 'v': 161 | flags |= OPT_VERBOSE; 162 | continue; 163 | case '?': 164 | default: 165 | remote_usage(REMOTE_USAGE_DEFAULT); 166 | } 167 | 168 | 169 | if (git_repository_path() == -1) { 170 | fprintf(stderr, "fatal: not a git repository (or any of the parent directories): .git"); 171 | exit(0); 172 | } 173 | 174 | config_parser(); 175 | 176 | switch (cmd) { 177 | case CMD_REMOVE: 178 | remote_remove(argc, argv, flags); 179 | break; 180 | case CMD_DEFAULT: 181 | default: 182 | remote_list(argc, argv, flags); 183 | break; 184 | } 185 | 186 | 187 | return (ret); 188 | } 189 | -------------------------------------------------------------------------------- /src/remote.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef _REMOTE_H_ 29 | #define _REMOTE_H_ 30 | 31 | /* Usage Messages */ 32 | // XXX Change to Enum 33 | #define REMOTE_USAGE_DEFAULT 0 34 | #define REMOTE_USAGE_REMOVE 1 35 | 36 | /* Command */ 37 | // XXX Change to Enum 38 | #define CMD_DEFAULT 0x00 39 | #define CMD_ADD 0x01 40 | #define CMD_RENAME 0x02 41 | #define CMD_REMOVE 0x04 42 | #define CMD_SET_HEAD 0x08 43 | #define CMD_SET_BRANCHES 0x10 44 | #define CMD_GET_URL 0x20 45 | #define CMD_SET_URL 0x40 46 | #define CMD_PRUNE 0x80 47 | 48 | /* Command-line option flags */ 49 | #define OPT_VERBOSE 0x01 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/tests/.gitignore: -------------------------------------------------------------------------------- 1 | Kyuafile 2 | ogit_test 3 | -------------------------------------------------------------------------------- /src/tests/Makefile: -------------------------------------------------------------------------------- 1 | # $FreeBSD$ 2 | 3 | ATF_TESTS_SH+= ogit_test 4 | 5 | .include 6 | -------------------------------------------------------------------------------- /src/tests/ogit_test.sh: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | # 4 | # Copyright (c) 2019 Kyle Evans 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 1. Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | # SUCH DAMAGE. 26 | # 27 | # $FreeBSD$ 28 | 29 | : ${OGIT:=$(realpath $(atf_get_srcdir)/../ogit)} 30 | 31 | atf_test_case log 32 | log_head() 33 | { 34 | 35 | } 36 | 37 | log_body() 38 | { 39 | 40 | wrkdir=$(realpath .) 41 | mkdir foo 42 | cd foo 43 | git init 44 | touch bar 45 | git add bar 46 | git commit -m "Initial Commit.$" 47 | ${OGIT} log --color=never > ${wrkdir}/.log 48 | 49 | lines=$(cat ${wrkdir}/.log | wc -l | tr -d '[:space:]') 50 | commithash=$(head -1 ${wrkdir}/.log | sed -e 's/commit //') 51 | expectedhash=$(cat .git/refs/heads/master) 52 | 53 | echo ${commithash} | head -1 54 | atf_check_equal "${commithash}" "${expectedhash}" 55 | 56 | atf_check -x "head -2 ${wrkdir}/.log | tail -1 | grep -qe '^Author:'" 57 | atf_check -x "head -3 ${wrkdir}/.log | tail -1 | grep -qEe '^Date:.+[+-][0-9]{4}$'" 58 | atf_check -x "head -4 ${wrkdir}/.log | tail -1 | grep -qe '^$'" 59 | atf_check -x "grep -q '^Initial Commit.$\$' ${wrkdir}/.log" 60 | atf_check_equal "${lines}" "5" 61 | 62 | touch foo 63 | touch baz 64 | git add foo baz 65 | git commit -m "Second commit." 66 | ${OGIT} log --color=never > ${wrkdir}/.log 67 | 68 | atf_check -x "head -2 ${wrkdir}/.log | tail -1 | grep -qe '^Author:'" 69 | atf_check -x "head -3 ${wrkdir}/.log | tail -1 | grep -qEe '^Date:.+[+-][0-9]{4}$'" 70 | atf_check -x "head -4 ${wrkdir}/.log | tail -1 | grep -qe '^$'" 71 | } 72 | 73 | atf_init_test_cases() 74 | { 75 | # We'll use GPL-licensed git to create our repos for sanity checking 76 | atf_require_prog git 77 | 78 | atf_add_test_case log 79 | } 80 | -------------------------------------------------------------------------------- /src/update-index.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "lib/common.h" 40 | #include "lib/index.h" 41 | #include "lib/ini.h" 42 | 43 | static struct option long_options[] = 44 | { 45 | {"add", no_argument, NULL, 0}, 46 | {"cacheinfo", required_argument, NULL, 1}, 47 | {NULL, 0, NULL, 0} 48 | }; 49 | 50 | int 51 | update_index_usage(int type) 52 | { 53 | fprintf(stderr, "usage: git update-index [] [--] [...]\n"); 54 | return (0); 55 | } 56 | 57 | int 58 | update_index_open_index(FILE **indexptr) 59 | { 60 | char indexpath[PATH_MAX]; 61 | 62 | snprintf(indexpath, sizeof(indexpath), "%s/index", dotgitpath); 63 | printf("File: %s\n", indexpath); 64 | 65 | *indexptr = fopen(indexpath, "rw"); 66 | 67 | return (0); 68 | } 69 | 70 | int 71 | update_index_parse(FILE **indexptr) 72 | { 73 | struct indextree indextree; 74 | unsigned char *indexmap; 75 | struct stat sb; 76 | 77 | fstat(fileno(*indexptr), &sb); 78 | 79 | indexmap = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fileno(*indexptr), 0); 80 | if (indexmap == MAP_FAILED) { 81 | fprintf(stderr, "mmap failed on index file\n"); 82 | exit(1); 83 | } 84 | 85 | index_parse(&indextree, indexmap, sb.st_size); 86 | return (0); 87 | } 88 | 89 | int 90 | update_index_main(int argc, char *argv[]) 91 | { 92 | int ret = 0; 93 | int ch; 94 | int q = 0; 95 | 96 | argc--; argv++; 97 | 98 | while((ch = getopt_long(argc, argv, ":", long_options, NULL)) != -1) 99 | switch(ch) { 100 | case 0: 101 | printf("0\n"); 102 | break; 103 | case 1: 104 | printf("1\n"); 105 | break; 106 | default: 107 | printf("Currently not implemented\n"); 108 | return (-1); 109 | } 110 | argc = argc - q; 111 | argv = argv + q; 112 | 113 | if (git_repository_path() == -1) { 114 | fprintf(stderr, "fatal: not a git repository (or any of the parent directories): .git"); 115 | exit(0); 116 | } 117 | config_parser(); 118 | 119 | FILE *indexptr; 120 | 121 | update_index_open_index(&indexptr); 122 | update_index_parse(&indexptr); 123 | 124 | return (ret); 125 | } 126 | 127 | -------------------------------------------------------------------------------- /src/update-index.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 Farhan Khan. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef __UPDATE_INDEX_H__ 29 | #define __UPDATE_INDEX_H__ 30 | 31 | int update_index_main(int argc, char *argv[]); 32 | 33 | #endif 34 | --------------------------------------------------------------------------------