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