9d548a6ecb900bf61a01b213b21e8b0a0944a7e4
[reposerve] / src / proto.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <sys/stat.h>
7 #include "proto.h"
8 #include "md5.h"
9
10 struct flist *flist_create(void)
11 {
12         struct flist *flist;
13
14         if(!(flist = calloc(1, sizeof *flist))) {
15                 perror("flist_create");
16                 return 0;
17         }
18         flist->final_size = -1;
19         return flist;
20 }
21
22 void flist_destroy(struct flist *flist)
23 {
24         if(flist) {
25                 free(flist->flist);
26                 free(flist->data);
27                 free(flist);
28         }
29 }
30
31 int flist_add(struct flist *flist, const char *fname, int contents)
32 {
33         FILE *fp;
34         struct proto_file_entry fent;
35         int namelen, datalen, sz;
36         struct stat st;
37         char *dptr;
38         char buf[1024];
39         struct md5_state md;
40
41         if(flist->final_size >= 0) {
42                 fprintf(stderr, "flist_add: can't add more files to a finalized file list\n");
43                 return -1;
44         }
45
46         if(!(fp = fopen(fname, "rb"))) {
47                 fprintf(stderr, "flist_add: failed to open file: %s: %s\n", fname, strerror(errno));
48                 return -1;
49         }
50         fstat(fileno(fp), &st);
51
52         fent.size = st.st_size;
53         fent.mtime = st.st_mtime;
54
55         namelen = datalen = strlen(fname);
56         if(contents) {
57                 datalen += fent.size;
58         }
59
60         if(flist->flist->num_files >= flist->max_files) {
61                 struct proto_flist *tmp;
62                 int newsz = flist->max_files ? flist->max_files << 1 : 32;
63
64                 if(!(tmp = realloc(flist->flist, sizeof *tmp + (newsz - 1) * sizeof *tmp->files))) {
65                         fprintf(stderr, "flist_add: failed to resize file list to %d entries\n", newsz);
66                         return -1;
67                 }
68                 flist->flist = tmp;
69                 flist->max_files = newsz;
70         }
71         if(flist->data_sz + datalen >= flist->max_data_sz) {
72                 char *tmp;
73                 int newsz = flist->max_data_sz + datalen;
74                 newsz |= newsz >> 1;
75                 newsz |= newsz >> 2;
76                 newsz |= newsz >> 4;
77                 newsz |= newsz >> 8;
78                 newsz |= newsz >> 16;
79                 newsz += 1;
80
81                 if(!(tmp = realloc(flist->data, newsz))) {
82                         fprintf(stderr, "flist_add: failed to resize data buffer to %d bytes\n", newsz);
83                         return -1;
84                 }
85                 flist->data = tmp;
86                 flist->max_data_sz = newsz;
87         }
88
89         fent.nameoffs = flist->data_sz;
90         fent.namelen = namelen;
91         dptr = flist->data + flist->data_sz;
92         memcpy(dptr, fname, namelen);
93
94         md5_begin(&md);
95         if(contents) {
96                 dptr += namelen;
97
98                 while((sz = fread(dptr, 1, st.st_size, fp)) > 0) {
99                         md5_msg(&md, dptr, sz);
100                         dptr += sz;
101                         st.st_size -= sz;
102                 }
103         } else {
104                 while((sz = fread(buf, 1, sizeof buf, fp)) > 0) {
105                         md5_msg(&md, buf, sz);
106                 }
107         }
108         md5_end(&md);
109         memcpy(fent.csum, md.sum, sizeof fent.csum);
110
111         fclose(fp);
112
113         flist->flist->files[flist->flist->num_files++] = fent;
114         flist->data_sz += datalen;
115         return 0;
116 }
117
118 int flist_finalize(struct flist *flist)
119 {
120         int newsz;
121         struct proto_flist *tmp;
122
123         if(!flist->flist || flist->flist->num_files <= 0) {
124                 fprintf(stderr, "flist_finalize: nothing to finalize, file list empty\n");
125                 return -1;
126         }
127
128         newsz = sizeof *tmp + (flist->flist->num_files - 1) * sizeof *tmp->files + flist->data_sz;
129         if(!(tmp = realloc(flist->flist, newsz))) {
130                 fprintf(stderr, "flist_finalize: failed to resize file list buffer to %d bytes\n", newsz);
131                 return -1;
132         }
133
134         memcpy(tmp->files + flist->flist->num_files, flist->data, flist->data_sz);
135         free(flist->data);
136
137         memset(flist, 0, sizeof *flist);
138         flist->flist = tmp;
139         flist->final_size = newsz;
140         return 0;
141 }