lockfile: unlock file if lockfile permissions cannot be adjusted
[gitweb.git] / server-info.c
index 9ec744e9f2da294a21ac02b46f0ea0b35889ae54..31f4a749fbad73b9f7bc1f1ebf9b11fd743ab374 100644 (file)
@@ -4,45 +4,80 @@
 #include "commit.h"
 #include "tag.h"
 
-/* refs */
-static FILE *info_ref_fp;
+/*
+ * Create the file "path" by writing to a temporary file and renaming
+ * it into place. The contents of the file come from "generate", which
+ * should return non-zero if it encounters an error.
+ */
+static int update_info_file(char *path, int (*generate)(FILE *))
+{
+       char *tmp = mkpathdup("%s_XXXXXX", path);
+       int ret = -1;
+       int fd = -1;
+       FILE *fp = NULL;
+
+       safe_create_leading_directories(path);
+       fd = mkstemp(tmp);
+       if (fd < 0)
+               goto out;
+       fp = fdopen(fd, "w");
+       if (!fp)
+               goto out;
+       ret = generate(fp);
+       if (ret)
+               goto out;
+       if (fclose(fp))
+               goto out;
+       if (adjust_shared_perm(tmp) < 0)
+               goto out;
+       if (rename(tmp, path) < 0)
+               goto out;
+       ret = 0;
+
+out:
+       if (ret) {
+               error("unable to update %s: %s", path, strerror(errno));
+               if (fp)
+                       fclose(fp);
+               else if (fd >= 0)
+                       close(fd);
+               unlink(tmp);
+       }
+       free(tmp);
+       return ret;
+}
 
 static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
+       FILE *fp = cb_data;
        struct object *o = parse_object(sha1);
        if (!o)
                return -1;
 
-       fprintf(info_ref_fp, "%s        %s\n", sha1_to_hex(sha1), path);
+       if (fprintf(fp, "%s     %s\n", sha1_to_hex(sha1), path) < 0)
+               return -1;
+
        if (o->type == OBJ_TAG) {
                o = deref_tag(o, path, 0);
                if (o)
-                       fprintf(info_ref_fp, "%s        %s^{}\n",
-                               sha1_to_hex(o->sha1), path);
+                       if (fprintf(fp, "%s     %s^{}\n",
+                               sha1_to_hex(o->sha1), path) < 0)
+                               return -1;
        }
        return 0;
 }
 
+static int generate_info_refs(FILE *fp)
+{
+       return for_each_ref(add_info_ref, fp);
+}
+
 static int update_info_refs(int force)
 {
-       char *path0 = git_pathdup("info/refs");
-       int len = strlen(path0);
-       char *path1 = xmalloc(len + 2);
-
-       strcpy(path1, path0);
-       strcpy(path1 + len, "+");
-
-       safe_create_leading_directories(path0);
-       info_ref_fp = fopen(path1, "w");
-       if (!info_ref_fp)
-               return error("unable to update %s", path1);
-       for_each_ref(add_info_ref, NULL);
-       fclose(info_ref_fp);
-       adjust_shared_perm(path1);
-       rename(path1, path0);
-       free(path0);
-       free(path1);
-       return 0;
+       char *path = git_pathdup("info/refs");
+       int ret = update_info_file(path, generate_info_refs);
+       free(path);
+       return ret;
 }
 
 /* packs */
@@ -198,36 +233,36 @@ static void init_pack_info(const char *infofile, int force)
                info[i]->new_num = i;
 }
 
-static void write_pack_info_file(FILE *fp)
+static void free_pack_info(void)
 {
        int i;
        for (i = 0; i < num_pack; i++)
-               fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6);
-       fputc('\n', fp);
+               free(info[i]);
+       free(info);
 }
 
-static int update_info_packs(int force)
+static int write_pack_info_file(FILE *fp)
 {
-       char infofile[PATH_MAX];
-       char name[PATH_MAX];
-       int namelen;
-       FILE *fp;
+       int i;
+       for (i = 0; i < num_pack; i++) {
+               if (fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6) < 0)
+                       return -1;
+       }
+       if (fputc('\n', fp) == EOF)
+               return -1;
+       return 0;
+}
 
-       namelen = sprintf(infofile, "%s/info/packs", get_object_directory());
-       strcpy(name, infofile);
-       strcpy(name + namelen, "+");
+static int update_info_packs(int force)
+{
+       char *infofile = mkpathdup("%s/info/packs", get_object_directory());
+       int ret;
 
        init_pack_info(infofile, force);
-
-       safe_create_leading_directories(name);
-       fp = fopen(name, "w");
-       if (!fp)
-               return error("cannot open %s", name);
-       write_pack_info_file(fp);
-       fclose(fp);
-       adjust_shared_perm(name);
-       rename(name, infofile);
-       return 0;
+       ret = update_info_file(infofile, write_pack_info_file);
+       free_pack_info();
+       free(infofile);
+       return ret;
 }
 
 /* public */