1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <sys/stat.h>
5 #include <time.h>
6
7 #include "pool.h"
8 #include "repo.h"
9 #include "chksum.h"
10 #include "repo_solv.h"
11 #include "repo_write.h"
12
13 #include "repoinfo.h"
14 #include "repoinfo_cache.h"
15
16 #define COOKIE_IDENT "1.1"
17
18 #define SOLVCACHE_PATH "/var/cache/solv"
19
20 static char *userhome;
21
22 void
set_userhome()23 set_userhome()
24 {
25 userhome = getenv("HOME");
26 if (userhome && userhome[0] != '/')
27 userhome = 0;
28 }
29
30 void
calc_cookie_fp(FILE * fp,Id chktype,unsigned char * out)31 calc_cookie_fp(FILE *fp, Id chktype, unsigned char *out)
32 {
33 char buf[4096];
34 Chksum *h = solv_chksum_create(chktype);
35 int l;
36
37 solv_chksum_add(h, COOKIE_IDENT, strlen(COOKIE_IDENT));
38 while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
39 solv_chksum_add(h, buf, l);
40 rewind(fp);
41 solv_chksum_free(h, out);
42 }
43
44 void
calc_cookie_stat(struct stat * stb,Id chktype,unsigned char * cookie,unsigned char * out)45 calc_cookie_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out)
46 {
47 Chksum *h = solv_chksum_create(chktype);
48 solv_chksum_add(h, COOKIE_IDENT, strlen(COOKIE_IDENT));
49 if (cookie)
50 solv_chksum_add(h, cookie, 32);
51 solv_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
52 solv_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
53 solv_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
54 solv_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
55 solv_chksum_free(h, out);
56 }
57
58 char *
calc_cachepath(Repo * repo,const char * repoext,int forcesystemloc)59 calc_cachepath(Repo *repo, const char *repoext, int forcesystemloc)
60 {
61 char *q, *p;
62 int l;
63 if (!forcesystemloc && userhome && getuid())
64 p = pool_tmpjoin(repo->pool, userhome, "/.solvcache/", 0);
65 else
66 p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", 0);
67 l = strlen(p);
68 p = pool_tmpappend(repo->pool, p, repo->name, 0);
69 if (repoext)
70 {
71 p = pool_tmpappend(repo->pool, p, "_", repoext);
72 p = pool_tmpappend(repo->pool, p, ".solvx", 0);
73 }
74 else
75 p = pool_tmpappend(repo->pool, p, ".solv", 0);
76 q = p + l;
77 if (*q == '.')
78 *q = '_';
79 for (; *q; q++)
80 if (*q == '/')
81 *q = '_';
82 return p;
83 }
84
85 int
usecachedrepo(struct repoinfo * cinfo,const char * repoext,int mark)86 usecachedrepo(struct repoinfo *cinfo, const char *repoext, int mark)
87 {
88 Repo *repo = cinfo->repo;
89 FILE *fp;
90 unsigned char *cookie = repoext ? cinfo->extcookie : (cinfo->cookieset ? cinfo->cookie : 0);
91 unsigned char mycookie[32];
92 unsigned char myextcookie[32];
93 int flags;
94 int forcesystemloc;
95
96 if (repoext && !cinfo->extcookieset)
97 return 0; /* huh? */
98 forcesystemloc = mark & 2 ? 0 : 1;
99 if (mark < 2 && userhome && getuid())
100 {
101 /* first try home location */
102 int res = usecachedrepo(cinfo, repoext, mark | 2);
103 if (res)
104 return res;
105 }
106 mark &= 1;
107 if (!(fp = fopen(calc_cachepath(repo, repoext, forcesystemloc), "r")))
108 return 0;
109 if (!repoext && !cinfo->cookieset && cinfo->autorefresh && cinfo->metadata_expire != -1)
110 {
111 struct stat stb; /* no cookie set yet, check cache expiry time */
112 if (fstat(fileno(fp), &stb) || time(0) - stb.st_mtime >= cinfo->metadata_expire)
113 {
114 fclose(fp);
115 return 0;
116 }
117 }
118 if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
119 {
120 fclose(fp);
121 return 0;
122 }
123 if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)) != 0)
124 {
125 fclose(fp);
126 return 0;
127 }
128 if (cinfo->type != TYPE_INSTALLED && !repoext)
129 {
130 if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
131 {
132 fclose(fp);
133 return 0;
134 }
135 }
136 rewind(fp);
137
138 flags = 0;
139 if (repoext)
140 {
141 flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
142 if (strcmp(repoext, "DL") != 0)
143 flags |= REPO_LOCALPOOL; /* no local pool for DL so that we can compare IDs */
144 }
145 if (repo_add_solv(repo, fp, flags))
146 {
147 fclose(fp);
148 return 0;
149 }
150 if (cinfo->type != TYPE_INSTALLED && !repoext)
151 {
152 memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
153 cinfo->cookieset = 1;
154 memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
155 cinfo->extcookieset = 1;
156 }
157 if (mark)
158 futimens(fileno(fp), 0); /* try to set modification time */
159 fclose(fp);
160 return 1;
161 }
162
163 static void
switchtowritten(struct repoinfo * cinfo,const char * repoext,Repodata * repodata,char * tmpl)164 switchtowritten(struct repoinfo *cinfo, const char *repoext, Repodata *repodata, char *tmpl)
165 {
166 Repo *repo = cinfo->repo;
167 FILE *fp;
168 int i;
169
170 if (!repoext && repodata)
171 return; /* rewrite case, don't bother for the added fileprovides */
172 for (i = repo->start; i < repo->end; i++)
173 if (repo->pool->solvables[i].repo != repo)
174 break;
175 if (i < repo->end)
176 return; /* not a simple block */
177 /* switch to just saved repo to activate paging and save memory */
178 fp = fopen(tmpl, "r");
179 if (!fp)
180 return;
181 if (!repoext)
182 {
183 /* main repo */
184 repo_empty(repo, 1);
185 if (repo_add_solv(repo, fp, SOLV_ADD_NO_STUBS))
186 {
187 /* oops, no way to recover from here */
188 fprintf(stderr, "internal error\n");
189 exit(1);
190 }
191 }
192 else
193 {
194 int flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
195 /* make sure repodata contains complete repo */
196 /* (this is how repodata_write saves it) */
197 repodata_extend_block(repodata, repo->start, repo->end - repo->start);
198 repodata->state = REPODATA_LOADING;
199 if (strcmp(repoext, "DL") != 0)
200 flags |= REPO_LOCALPOOL;
201 repo_add_solv(repo, fp, flags);
202 repodata->state = REPODATA_AVAILABLE; /* in case the load failed */
203 }
204 fclose(fp);
205 }
206
207 void
writecachedrepo(struct repoinfo * cinfo,const char * repoext,Repodata * repodata)208 writecachedrepo(struct repoinfo *cinfo, const char *repoext, Repodata *repodata)
209 {
210 Repo *repo = cinfo->repo;
211 FILE *fp;
212 int fd;
213 char *tmpl, *cachedir;
214
215 if (cinfo->incomplete || (repoext && !cinfo->extcookieset) || (!repoext && !cinfo->cookieset))
216 return;
217 cachedir = userhome && getuid() ? pool_tmpjoin(repo->pool, userhome, "/.solvcache", 0) : SOLVCACHE_PATH;
218 if (access(cachedir, W_OK | X_OK) != 0 && mkdir(cachedir, 0755) == 0)
219 printf("[created %s]\n", cachedir);
220 /* use dupjoin instead of tmpjoin because tmpl must survive repo_write */
221 tmpl = solv_dupjoin(cachedir, "/", ".newsolv-XXXXXX");
222 fd = mkstemp(tmpl);
223 if (fd < 0)
224 {
225 free(tmpl);
226 return;
227 }
228 fchmod(fd, 0444);
229 if (!(fp = fdopen(fd, "w")))
230 {
231 close(fd);
232 unlink(tmpl);
233 free(tmpl);
234 return;
235 }
236
237 if (!repodata)
238 repo_write(repo, fp);
239 else if (repoext)
240 repodata_write(repodata, fp);
241 else
242 {
243 int oldnrepodata = repo->nrepodata;
244 repo->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata; /* XXX: do this right */
245 repo_write(repo, fp);
246 repo->nrepodata = oldnrepodata;
247 }
248
249 if (!repoext && cinfo->type != TYPE_INSTALLED)
250 {
251 if (!cinfo->extcookieset)
252 {
253 /* create the ext cookie and append it */
254 /* we just need some unique ID */
255 struct stat stb;
256 if (fstat(fileno(fp), &stb))
257 memset(&stb, 0, sizeof(stb));
258 calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->cookie, cinfo->extcookie);
259 cinfo->extcookieset = 1;
260 }
261 if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
262 {
263 fclose(fp);
264 unlink(tmpl);
265 free(tmpl);
266 return;
267 }
268 }
269 /* append our cookie describing the metadata state */
270 if (fwrite(repoext ? cinfo->extcookie : cinfo->cookie, 32, 1, fp) != 1)
271 {
272 fclose(fp);
273 unlink(tmpl);
274 free(tmpl);
275 return;
276 }
277 if (fclose(fp))
278 {
279 unlink(tmpl);
280 free(tmpl);
281 return;
282 }
283
284 switchtowritten(cinfo, repoext, repodata, tmpl);
285
286 if (!rename(tmpl, calc_cachepath(repo, repoext, 0)))
287 unlink(tmpl);
288 free(tmpl);
289 }
290
291