Daniel Barkalow | 95fc751 | 2005-06-06 16:31:29 -0400 | [diff] [blame] | 1 | #include "refs.h" |
| 2 | #include "cache.h" |
| 3 | |
| 4 | #include <errno.h> |
| 5 | |
Linus Torvalds | ca8db14 | 2005-09-25 09:59:37 -0700 | [diff] [blame] | 6 | /* We allow "recursive" symbolic refs. Only within reason, though */ |
| 7 | #define MAXDEPTH 5 |
| 8 | |
Junio C Hamano | a876ed8 | 2005-09-30 14:08:25 -0700 | [diff] [blame] | 9 | const char *resolve_ref(const char *path, unsigned char *sha1, int reading) |
| 10 | { |
| 11 | int depth = MAXDEPTH, len; |
| 12 | char buffer[256]; |
| 13 | |
| 14 | for (;;) { |
| 15 | struct stat st; |
| 16 | char *buf; |
| 17 | int fd; |
| 18 | |
| 19 | if (--depth < 0) |
| 20 | return NULL; |
| 21 | |
| 22 | /* Special case: non-existing file. |
| 23 | * Not having the refs/heads/new-branch is OK |
| 24 | * if we are writing into it, so is .git/HEAD |
| 25 | * that points at refs/heads/master still to be |
| 26 | * born. It is NOT OK if we are resolving for |
| 27 | * reading. |
| 28 | */ |
| 29 | if (lstat(path, &st) < 0) { |
| 30 | if (reading || errno != ENOENT) |
| 31 | return NULL; |
| 32 | memset(sha1, 0, 20); |
| 33 | return path; |
| 34 | } |
| 35 | |
| 36 | /* Follow "normalized" - ie "refs/.." symlinks by hand */ |
| 37 | if (S_ISLNK(st.st_mode)) { |
| 38 | len = readlink(path, buffer, sizeof(buffer)-1); |
| 39 | if (len >= 5 && !memcmp("refs/", buffer, 5)) { |
| 40 | path = git_path("%.*s", len, buffer); |
| 41 | continue; |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | /* |
| 46 | * Anything else, just open it and try to use it as |
| 47 | * a ref |
| 48 | */ |
| 49 | fd = open(path, O_RDONLY); |
| 50 | if (fd < 0) |
| 51 | return NULL; |
| 52 | len = read(fd, buffer, sizeof(buffer)-1); |
| 53 | close(fd); |
| 54 | |
| 55 | /* |
| 56 | * Is it a symbolic ref? |
| 57 | */ |
| 58 | if (len < 4 || memcmp("ref:", buffer, 4)) |
| 59 | break; |
| 60 | buf = buffer + 4; |
| 61 | len -= 4; |
| 62 | while (len && isspace(*buf)) |
| 63 | buf++, len--; |
| 64 | while (len && isspace(buf[len-1])) |
| 65 | buf[--len] = 0; |
| 66 | path = git_path("%.*s", len, buf); |
| 67 | } |
| 68 | if (len < 40 || get_sha1_hex(buffer, sha1)) |
| 69 | return NULL; |
| 70 | return path; |
| 71 | } |
| 72 | |
Junio C Hamano | 8098a17 | 2005-09-30 14:26:57 -0700 | [diff] [blame] | 73 | int create_symref(const char *git_HEAD, const char *refs_heads_master) |
| 74 | { |
Junio C Hamano | 8098a17 | 2005-09-30 14:26:57 -0700 | [diff] [blame] | 75 | const char *lockpath; |
| 76 | char ref[1000]; |
| 77 | int fd, len, written; |
| 78 | |
Junio C Hamano | 9f0bb90 | 2006-05-02 00:40:24 -0700 | [diff] [blame] | 79 | #ifndef NO_SYMLINK_HEAD |
| 80 | if (prefer_symlink_refs) { |
Johannes Schindelin | f8348be | 2005-11-15 19:24:19 +0100 | [diff] [blame] | 81 | unlink(git_HEAD); |
| 82 | if (!symlink(refs_heads_master, git_HEAD)) |
| 83 | return 0; |
| 84 | fprintf(stderr, "no symlink - falling back to symbolic ref\n"); |
| 85 | } |
Johannes Schindelin | 303958d | 2005-10-26 01:40:31 +0200 | [diff] [blame] | 86 | #endif |
| 87 | |
Junio C Hamano | 8098a17 | 2005-09-30 14:26:57 -0700 | [diff] [blame] | 88 | len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master); |
| 89 | if (sizeof(ref) <= len) { |
| 90 | error("refname too long: %s", refs_heads_master); |
| 91 | return -1; |
| 92 | } |
| 93 | lockpath = mkpath("%s.lock", git_HEAD); |
| 94 | fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); |
| 95 | written = write(fd, ref, len); |
| 96 | close(fd); |
| 97 | if (written != len) { |
| 98 | unlink(lockpath); |
| 99 | error("Unable to write to %s", lockpath); |
| 100 | return -2; |
| 101 | } |
| 102 | if (rename(lockpath, git_HEAD) < 0) { |
| 103 | unlink(lockpath); |
| 104 | error("Unable to create %s", git_HEAD); |
| 105 | return -3; |
| 106 | } |
Junio C Hamano | 138086a | 2006-06-09 22:07:23 -0700 | [diff] [blame] | 107 | if (adjust_shared_perm(git_HEAD)) { |
| 108 | unlink(lockpath); |
| 109 | error("Unable to fix permissions on %s", lockpath); |
| 110 | return -4; |
| 111 | } |
Junio C Hamano | 8098a17 | 2005-09-30 14:26:57 -0700 | [diff] [blame] | 112 | return 0; |
Junio C Hamano | 8098a17 | 2005-09-30 14:26:57 -0700 | [diff] [blame] | 113 | } |
| 114 | |
Linus Torvalds | ca8db14 | 2005-09-25 09:59:37 -0700 | [diff] [blame] | 115 | int read_ref(const char *filename, unsigned char *sha1) |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 116 | { |
Junio C Hamano | a876ed8 | 2005-09-30 14:08:25 -0700 | [diff] [blame] | 117 | if (resolve_ref(filename, sha1, 1)) |
| 118 | return 0; |
| 119 | return -1; |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 120 | } |
| 121 | |
Sean | a62be77 | 2006-05-13 21:43:00 -0400 | [diff] [blame] | 122 | static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim) |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 123 | { |
| 124 | int retval = 0; |
Timo Sirainen | 4ec99bf | 2005-08-09 18:30:22 +0300 | [diff] [blame] | 125 | DIR *dir = opendir(git_path("%s", base)); |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 126 | |
| 127 | if (dir) { |
| 128 | struct dirent *de; |
| 129 | int baselen = strlen(base); |
| 130 | char *path = xmalloc(baselen + 257); |
Linus Torvalds | 6cada6a | 2005-07-04 15:28:19 -0700 | [diff] [blame] | 131 | |
| 132 | if (!strncmp(base, "./", 2)) { |
| 133 | base += 2; |
| 134 | baselen -= 2; |
| 135 | } |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 136 | memcpy(path, base, baselen); |
Linus Torvalds | 944d858 | 2005-07-03 10:01:38 -0700 | [diff] [blame] | 137 | if (baselen && base[baselen-1] != '/') |
| 138 | path[baselen++] = '/'; |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 139 | |
| 140 | while ((de = readdir(dir)) != NULL) { |
| 141 | unsigned char sha1[20]; |
| 142 | struct stat st; |
| 143 | int namelen; |
| 144 | |
| 145 | if (de->d_name[0] == '.') |
| 146 | continue; |
| 147 | namelen = strlen(de->d_name); |
| 148 | if (namelen > 255) |
| 149 | continue; |
Rene Scharfe | 83a2b84 | 2006-08-10 17:02:30 +0200 | [diff] [blame] | 150 | if (has_extension(de->d_name, namelen, ".lock")) |
Shawn Pearce | d0740d9 | 2006-05-19 03:29:26 -0400 | [diff] [blame] | 151 | continue; |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 152 | memcpy(path + baselen, de->d_name, namelen+1); |
Matt Draisey | a7e66ae | 2005-08-16 01:46:34 -0400 | [diff] [blame] | 153 | if (stat(git_path("%s", path), &st) < 0) |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 154 | continue; |
| 155 | if (S_ISDIR(st.st_mode)) { |
Sean | a62be77 | 2006-05-13 21:43:00 -0400 | [diff] [blame] | 156 | retval = do_for_each_ref(path, fn, trim); |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 157 | if (retval) |
| 158 | break; |
| 159 | continue; |
| 160 | } |
Johannes Schindelin | c401cb4 | 2006-02-28 22:16:01 +0100 | [diff] [blame] | 161 | if (read_ref(git_path("%s", path), sha1) < 0) { |
Junio C Hamano | f61c2c9 | 2006-03-09 12:59:16 -0800 | [diff] [blame] | 162 | error("%s points nowhere!", path); |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 163 | continue; |
Johannes Schindelin | c401cb4 | 2006-02-28 22:16:01 +0100 | [diff] [blame] | 164 | } |
| 165 | if (!has_sha1_file(sha1)) { |
Junio C Hamano | f61c2c9 | 2006-03-09 12:59:16 -0800 | [diff] [blame] | 166 | error("%s does not point to a valid " |
| 167 | "commit object!", path); |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 168 | continue; |
Johannes Schindelin | c401cb4 | 2006-02-28 22:16:01 +0100 | [diff] [blame] | 169 | } |
Sean | a62be77 | 2006-05-13 21:43:00 -0400 | [diff] [blame] | 170 | retval = fn(path + trim, sha1); |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 171 | if (retval) |
| 172 | break; |
| 173 | } |
| 174 | free(path); |
| 175 | closedir(dir); |
| 176 | } |
| 177 | return retval; |
| 178 | } |
| 179 | |
Linus Torvalds | 723c31f | 2005-07-05 11:31:32 -0700 | [diff] [blame] | 180 | int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) |
| 181 | { |
| 182 | unsigned char sha1[20]; |
Linus Torvalds | ca8db14 | 2005-09-25 09:59:37 -0700 | [diff] [blame] | 183 | if (!read_ref(git_path("HEAD"), sha1)) |
Linus Torvalds | 99a0a6e | 2005-07-08 13:56:05 -0700 | [diff] [blame] | 184 | return fn("HEAD", sha1); |
Linus Torvalds | 2f34ba3 | 2005-07-05 15:45:00 -0700 | [diff] [blame] | 185 | return 0; |
Linus Torvalds | 723c31f | 2005-07-05 11:31:32 -0700 | [diff] [blame] | 186 | } |
| 187 | |
Linus Torvalds | 944d858 | 2005-07-03 10:01:38 -0700 | [diff] [blame] | 188 | int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1)) |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 189 | { |
Sean | a62be77 | 2006-05-13 21:43:00 -0400 | [diff] [blame] | 190 | return do_for_each_ref("refs", fn, 0); |
| 191 | } |
| 192 | |
| 193 | int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1)) |
| 194 | { |
| 195 | return do_for_each_ref("refs/tags", fn, 10); |
| 196 | } |
| 197 | |
| 198 | int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1)) |
| 199 | { |
| 200 | return do_for_each_ref("refs/heads", fn, 11); |
| 201 | } |
| 202 | |
| 203 | int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1)) |
| 204 | { |
| 205 | return do_for_each_ref("refs/remotes", fn, 13); |
Linus Torvalds | 8a65ff7 | 2005-07-02 20:23:36 -0700 | [diff] [blame] | 206 | } |
| 207 | |
Daniel Barkalow | 95fc751 | 2005-06-06 16:31:29 -0400 | [diff] [blame] | 208 | int get_ref_sha1(const char *ref, unsigned char *sha1) |
| 209 | { |
Daniel Barkalow | 95fc751 | 2005-06-06 16:31:29 -0400 | [diff] [blame] | 210 | if (check_ref_format(ref)) |
| 211 | return -1; |
Shawn Pearce | 70e1a88 | 2006-05-17 05:54:46 -0400 | [diff] [blame] | 212 | return read_ref(git_path("refs/%s", ref), sha1); |
Daniel Barkalow | 95fc751 | 2005-06-06 16:31:29 -0400 | [diff] [blame] | 213 | } |
| 214 | |
Junio C Hamano | 03feddd | 2005-10-13 18:57:39 -0700 | [diff] [blame] | 215 | /* |
| 216 | * Make sure "ref" is something reasonable to have under ".git/refs/"; |
| 217 | * We do not like it if: |
| 218 | * |
| 219 | * - any path component of it begins with ".", or |
| 220 | * - it has double dots "..", or |
| 221 | * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or |
| 222 | * - it ends with a "/". |
| 223 | */ |
| 224 | |
| 225 | static inline int bad_ref_char(int ch) |
| 226 | { |
| 227 | return (((unsigned) ch) <= ' ' || |
Junio C Hamano | 6828399 | 2005-12-15 18:03:59 -0800 | [diff] [blame] | 228 | ch == '~' || ch == '^' || ch == ':' || |
| 229 | /* 2.13 Pattern Matching Notation */ |
| 230 | ch == '?' || ch == '*' || ch == '['); |
Junio C Hamano | 03feddd | 2005-10-13 18:57:39 -0700 | [diff] [blame] | 231 | } |
| 232 | |
Daniel Barkalow | 95fc751 | 2005-06-06 16:31:29 -0400 | [diff] [blame] | 233 | int check_ref_format(const char *ref) |
| 234 | { |
Junio C Hamano | 03feddd | 2005-10-13 18:57:39 -0700 | [diff] [blame] | 235 | int ch, level; |
| 236 | const char *cp = ref; |
| 237 | |
| 238 | level = 0; |
| 239 | while (1) { |
| 240 | while ((ch = *cp++) == '/') |
| 241 | ; /* tolerate duplicated slashes */ |
| 242 | if (!ch) |
| 243 | return -1; /* should not end with slashes */ |
| 244 | |
| 245 | /* we are at the beginning of the path component */ |
| 246 | if (ch == '.' || bad_ref_char(ch)) |
| 247 | return -1; |
| 248 | |
| 249 | /* scan the rest of the path component */ |
| 250 | while ((ch = *cp++) != 0) { |
| 251 | if (bad_ref_char(ch)) |
| 252 | return -1; |
| 253 | if (ch == '/') |
| 254 | break; |
| 255 | if (ch == '.' && *cp == '.') |
| 256 | return -1; |
| 257 | } |
| 258 | level++; |
| 259 | if (!ch) { |
| 260 | if (level < 2) |
| 261 | return -1; /* at least of form "heads/blah" */ |
| 262 | return 0; |
| 263 | } |
| 264 | } |
Daniel Barkalow | 95fc751 | 2005-06-06 16:31:29 -0400 | [diff] [blame] | 265 | } |
| 266 | |
Junio C Hamano | e5f38ec | 2006-06-06 14:04:17 -0700 | [diff] [blame] | 267 | static struct ref_lock *verify_lock(struct ref_lock *lock, |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 268 | const unsigned char *old_sha1, int mustexist) |
Daniel Barkalow | 95fc751 | 2005-06-06 16:31:29 -0400 | [diff] [blame] | 269 | { |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 270 | char buf[40]; |
| 271 | int nr, fd = open(lock->ref_file, O_RDONLY); |
| 272 | if (fd < 0 && (mustexist || errno != ENOENT)) { |
| 273 | error("Can't verify ref %s", lock->ref_file); |
| 274 | unlock_ref(lock); |
| 275 | return NULL; |
Daniel Barkalow | 95fc751 | 2005-06-06 16:31:29 -0400 | [diff] [blame] | 276 | } |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 277 | nr = read(fd, buf, 40); |
| 278 | close(fd); |
| 279 | if (nr != 40 || get_sha1_hex(buf, lock->old_sha1) < 0) { |
| 280 | error("Can't verify ref %s", lock->ref_file); |
| 281 | unlock_ref(lock); |
| 282 | return NULL; |
| 283 | } |
| 284 | if (memcmp(lock->old_sha1, old_sha1, 20)) { |
| 285 | error("Ref %s is at %s but expected %s", lock->ref_file, |
| 286 | sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1)); |
| 287 | unlock_ref(lock); |
| 288 | return NULL; |
| 289 | } |
| 290 | return lock; |
| 291 | } |
| 292 | |
Junio C Hamano | e5f38ec | 2006-06-06 14:04:17 -0700 | [diff] [blame] | 293 | static struct ref_lock *lock_ref_sha1_basic(const char *path, |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 294 | int plen, |
| 295 | const unsigned char *old_sha1, int mustexist) |
| 296 | { |
Shawn Pearce | 818f477 | 2006-07-28 23:44:51 -0400 | [diff] [blame] | 297 | const char *orig_path = path; |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 298 | struct ref_lock *lock; |
Shawn Pearce | 732232a | 2006-05-19 03:29:05 -0400 | [diff] [blame] | 299 | struct stat st; |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 300 | |
| 301 | lock = xcalloc(1, sizeof(struct ref_lock)); |
| 302 | lock->lock_fd = -1; |
| 303 | |
| 304 | plen = strlen(path) - plen; |
| 305 | path = resolve_ref(path, lock->old_sha1, mustexist); |
| 306 | if (!path) { |
Shawn Pearce | 818f477 | 2006-07-28 23:44:51 -0400 | [diff] [blame] | 307 | int last_errno = errno; |
| 308 | error("unable to resolve reference %s: %s", |
| 309 | orig_path, strerror(errno)); |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 310 | unlock_ref(lock); |
Shawn Pearce | 818f477 | 2006-07-28 23:44:51 -0400 | [diff] [blame] | 311 | errno = last_errno; |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 312 | return NULL; |
| 313 | } |
Junio C Hamano | c33d517 | 2006-06-06 13:54:14 -0700 | [diff] [blame] | 314 | lock->lk = xcalloc(1, sizeof(struct lock_file)); |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 315 | |
| 316 | lock->ref_file = strdup(path); |
Shawn Pearce | 6de08ae | 2006-05-17 05:55:40 -0400 | [diff] [blame] | 317 | lock->log_file = strdup(git_path("logs/%s", lock->ref_file + plen)); |
Shawn Pearce | 8fe9277 | 2006-05-19 05:15:28 -0400 | [diff] [blame] | 318 | lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT; |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 319 | |
Junio C Hamano | c33d517 | 2006-06-06 13:54:14 -0700 | [diff] [blame] | 320 | if (safe_create_leading_directories(lock->ref_file)) |
| 321 | die("unable to create directory for %s", lock->ref_file); |
| 322 | lock->lock_fd = hold_lock_file_for_update(lock->lk, lock->ref_file); |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 323 | if (lock->lock_fd < 0) { |
| 324 | error("Couldn't open lock file %s: %s", |
Junio C Hamano | c33d517 | 2006-06-06 13:54:14 -0700 | [diff] [blame] | 325 | lock->lk->filename, strerror(errno)); |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 326 | unlock_ref(lock); |
| 327 | return NULL; |
| 328 | } |
| 329 | |
| 330 | return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; |
| 331 | } |
| 332 | |
Junio C Hamano | e5f38ec | 2006-06-06 14:04:17 -0700 | [diff] [blame] | 333 | struct ref_lock *lock_ref_sha1(const char *ref, |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 334 | const unsigned char *old_sha1, int mustexist) |
| 335 | { |
| 336 | if (check_ref_format(ref)) |
| 337 | return NULL; |
| 338 | return lock_ref_sha1_basic(git_path("refs/%s", ref), |
Shawn Pearce | d0740d9 | 2006-05-19 03:29:26 -0400 | [diff] [blame] | 339 | 5 + strlen(ref), old_sha1, mustexist); |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 340 | } |
| 341 | |
Junio C Hamano | e5f38ec | 2006-06-06 14:04:17 -0700 | [diff] [blame] | 342 | struct ref_lock *lock_any_ref_for_update(const char *ref, |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 343 | const unsigned char *old_sha1, int mustexist) |
| 344 | { |
| 345 | return lock_ref_sha1_basic(git_path("%s", ref), |
| 346 | strlen(ref), old_sha1, mustexist); |
| 347 | } |
| 348 | |
Junio C Hamano | e5f38ec | 2006-06-06 14:04:17 -0700 | [diff] [blame] | 349 | void unlock_ref(struct ref_lock *lock) |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 350 | { |
| 351 | if (lock->lock_fd >= 0) { |
| 352 | close(lock->lock_fd); |
Junio C Hamano | c33d517 | 2006-06-06 13:54:14 -0700 | [diff] [blame] | 353 | /* Do not free lock->lk -- atexit() still looks at them */ |
| 354 | if (lock->lk) |
| 355 | rollback_lock_file(lock->lk); |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 356 | } |
| 357 | if (lock->ref_file) |
| 358 | free(lock->ref_file); |
Shawn Pearce | 6de08ae | 2006-05-17 05:55:40 -0400 | [diff] [blame] | 359 | if (lock->log_file) |
| 360 | free(lock->log_file); |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 361 | free(lock); |
| 362 | } |
| 363 | |
Shawn Pearce | 6de08ae | 2006-05-17 05:55:40 -0400 | [diff] [blame] | 364 | static int log_ref_write(struct ref_lock *lock, |
| 365 | const unsigned char *sha1, const char *msg) |
| 366 | { |
| 367 | int logfd, written, oflags = O_APPEND | O_WRONLY; |
| 368 | unsigned maxlen, len; |
| 369 | char *logrec; |
Alp Toker | ff4c848 | 2006-07-09 10:36:24 +0100 | [diff] [blame] | 370 | const char *committer; |
Shawn Pearce | 6de08ae | 2006-05-17 05:55:40 -0400 | [diff] [blame] | 371 | |
| 372 | if (log_all_ref_updates) { |
| 373 | if (safe_create_leading_directories(lock->log_file) < 0) |
| 374 | return error("unable to create directory for %s", |
| 375 | lock->log_file); |
| 376 | oflags |= O_CREAT; |
| 377 | } |
| 378 | |
| 379 | logfd = open(lock->log_file, oflags, 0666); |
| 380 | if (logfd < 0) { |
| 381 | if (!log_all_ref_updates && errno == ENOENT) |
| 382 | return 0; |
| 383 | return error("Unable to append to %s: %s", |
| 384 | lock->log_file, strerror(errno)); |
| 385 | } |
| 386 | |
Alp Toker | ff4c848 | 2006-07-09 10:36:24 +0100 | [diff] [blame] | 387 | committer = git_committer_info(1); |
Shawn Pearce | 6de08ae | 2006-05-17 05:55:40 -0400 | [diff] [blame] | 388 | if (msg) { |
Alp Toker | ff4c848 | 2006-07-09 10:36:24 +0100 | [diff] [blame] | 389 | maxlen = strlen(committer) + strlen(msg) + 2*40 + 5; |
Shawn Pearce | 6de08ae | 2006-05-17 05:55:40 -0400 | [diff] [blame] | 390 | logrec = xmalloc(maxlen); |
| 391 | len = snprintf(logrec, maxlen, "%s %s %s\t%s\n", |
| 392 | sha1_to_hex(lock->old_sha1), |
| 393 | sha1_to_hex(sha1), |
Alp Toker | ff4c848 | 2006-07-09 10:36:24 +0100 | [diff] [blame] | 394 | committer, |
Shawn Pearce | 6de08ae | 2006-05-17 05:55:40 -0400 | [diff] [blame] | 395 | msg); |
Junio C Hamano | e5f38ec | 2006-06-06 14:04:17 -0700 | [diff] [blame] | 396 | } |
| 397 | else { |
Alp Toker | ff4c848 | 2006-07-09 10:36:24 +0100 | [diff] [blame] | 398 | maxlen = strlen(committer) + 2*40 + 4; |
Shawn Pearce | 6de08ae | 2006-05-17 05:55:40 -0400 | [diff] [blame] | 399 | logrec = xmalloc(maxlen); |
| 400 | len = snprintf(logrec, maxlen, "%s %s %s\n", |
| 401 | sha1_to_hex(lock->old_sha1), |
| 402 | sha1_to_hex(sha1), |
Alp Toker | ff4c848 | 2006-07-09 10:36:24 +0100 | [diff] [blame] | 403 | committer); |
Shawn Pearce | 6de08ae | 2006-05-17 05:55:40 -0400 | [diff] [blame] | 404 | } |
| 405 | written = len <= maxlen ? write(logfd, logrec, len) : -1; |
| 406 | free(logrec); |
| 407 | close(logfd); |
| 408 | if (written != len) |
| 409 | return error("Unable to append to %s", lock->log_file); |
| 410 | return 0; |
| 411 | } |
| 412 | |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 413 | int write_ref_sha1(struct ref_lock *lock, |
| 414 | const unsigned char *sha1, const char *logmsg) |
| 415 | { |
| 416 | static char term = '\n'; |
| 417 | |
| 418 | if (!lock) |
| 419 | return -1; |
Shawn Pearce | 732232a | 2006-05-19 03:29:05 -0400 | [diff] [blame] | 420 | if (!lock->force_write && !memcmp(lock->old_sha1, sha1, 20)) { |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 421 | unlock_ref(lock); |
| 422 | return 0; |
| 423 | } |
| 424 | if (write(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 || |
| 425 | write(lock->lock_fd, &term, 1) != 1 |
| 426 | || close(lock->lock_fd) < 0) { |
Junio C Hamano | c33d517 | 2006-06-06 13:54:14 -0700 | [diff] [blame] | 427 | error("Couldn't write %s", lock->lk->filename); |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 428 | unlock_ref(lock); |
| 429 | return -1; |
| 430 | } |
Shawn Pearce | 6de08ae | 2006-05-17 05:55:40 -0400 | [diff] [blame] | 431 | if (log_ref_write(lock, sha1, logmsg) < 0) { |
| 432 | unlock_ref(lock); |
| 433 | return -1; |
| 434 | } |
Junio C Hamano | c33d517 | 2006-06-06 13:54:14 -0700 | [diff] [blame] | 435 | if (commit_lock_file(lock->lk)) { |
Shawn Pearce | 4bd18c4 | 2006-05-17 05:55:02 -0400 | [diff] [blame] | 436 | error("Couldn't set %s", lock->ref_file); |
| 437 | unlock_ref(lock); |
| 438 | return -1; |
| 439 | } |
| 440 | lock->lock_fd = -1; |
| 441 | unlock_ref(lock); |
| 442 | return 0; |
Daniel Barkalow | 95fc751 | 2005-06-06 16:31:29 -0400 | [diff] [blame] | 443 | } |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 444 | |
| 445 | int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1) |
| 446 | { |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 447 | const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec; |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 448 | char *tz_c; |
| 449 | int logfd, tz; |
| 450 | struct stat st; |
| 451 | unsigned long date; |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 452 | unsigned char logged_sha1[20]; |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 453 | |
| 454 | logfile = git_path("logs/%s", ref); |
| 455 | logfd = open(logfile, O_RDONLY, 0); |
| 456 | if (logfd < 0) |
| 457 | die("Unable to read log %s: %s", logfile, strerror(errno)); |
| 458 | fstat(logfd, &st); |
| 459 | if (!st.st_size) |
| 460 | die("Log %s is empty.", logfile); |
| 461 | logdata = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0); |
| 462 | close(logfd); |
| 463 | |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 464 | lastrec = NULL; |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 465 | rec = logend = logdata + st.st_size; |
| 466 | while (logdata < rec) { |
| 467 | if (logdata < rec && *(rec-1) == '\n') |
| 468 | rec--; |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 469 | lastgt = NULL; |
| 470 | while (logdata < rec && *(rec-1) != '\n') { |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 471 | rec--; |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 472 | if (*rec == '>') |
| 473 | lastgt = rec; |
| 474 | } |
| 475 | if (!lastgt) |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 476 | die("Log %s is corrupt.", logfile); |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 477 | date = strtoul(lastgt + 1, &tz_c, 10); |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 478 | if (date <= at_time) { |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 479 | if (lastrec) { |
| 480 | if (get_sha1_hex(lastrec, logged_sha1)) |
| 481 | die("Log %s is corrupt.", logfile); |
| 482 | if (get_sha1_hex(rec + 41, sha1)) |
| 483 | die("Log %s is corrupt.", logfile); |
| 484 | if (memcmp(logged_sha1, sha1, 20)) { |
| 485 | tz = strtoul(tz_c, NULL, 10); |
| 486 | fprintf(stderr, |
| 487 | "warning: Log %s has gap after %s.\n", |
| 488 | logfile, show_rfc2822_date(date, tz)); |
| 489 | } |
Junio C Hamano | e5f38ec | 2006-06-06 14:04:17 -0700 | [diff] [blame] | 490 | } |
| 491 | else if (date == at_time) { |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 492 | if (get_sha1_hex(rec + 41, sha1)) |
| 493 | die("Log %s is corrupt.", logfile); |
Junio C Hamano | e5f38ec | 2006-06-06 14:04:17 -0700 | [diff] [blame] | 494 | } |
| 495 | else { |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 496 | if (get_sha1_hex(rec + 41, logged_sha1)) |
| 497 | die("Log %s is corrupt.", logfile); |
| 498 | if (memcmp(logged_sha1, sha1, 20)) { |
| 499 | tz = strtoul(tz_c, NULL, 10); |
| 500 | fprintf(stderr, |
| 501 | "warning: Log %s unexpectedly ended on %s.\n", |
| 502 | logfile, show_rfc2822_date(date, tz)); |
| 503 | } |
| 504 | } |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 505 | munmap((void*)logdata, st.st_size); |
| 506 | return 0; |
| 507 | } |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 508 | lastrec = rec; |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 509 | } |
| 510 | |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 511 | rec = logdata; |
| 512 | while (rec < logend && *rec != '>' && *rec != '\n') |
| 513 | rec++; |
| 514 | if (rec == logend || *rec == '\n') |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 515 | die("Log %s is corrupt.", logfile); |
Shawn Pearce | e522904 | 2006-05-19 03:28:19 -0400 | [diff] [blame] | 516 | date = strtoul(rec + 1, &tz_c, 10); |
Shawn Pearce | d556fae | 2006-05-17 05:56:09 -0400 | [diff] [blame] | 517 | tz = strtoul(tz_c, NULL, 10); |
| 518 | if (get_sha1_hex(logdata, sha1)) |
| 519 | die("Log %s is corrupt.", logfile); |
| 520 | munmap((void*)logdata, st.st_size); |
| 521 | fprintf(stderr, "warning: Log %s only goes back to %s.\n", |
| 522 | logfile, show_rfc2822_date(date, tz)); |
| 523 | return 0; |
| 524 | } |