Imported Upstream version 0.6.13
[platform/upstream/libsolv.git] / examples / solv / repoinfo_cache.c
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
23 set_userhome()
24 {
25   userhome = getenv("HOME");
26   if (userhome && userhome[0] != '/') 
27     userhome = 0; 
28 }
29
30 void
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
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 *
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
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
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
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