Sanitize python object -> tag number exception handling
[platform/upstream/rpm.git] / lib / fprint.c
1 /**
2  * \file lib/fprint.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/rpmfileutil.h>    /* for rpmCleanPath */
8
9 #include "lib/rpmdb_internal.h"
10 #include "lib/rpmfi_internal.h"
11 #include "lib/fprint.h"
12 #include "lib/rpmhash.h"
13 #include "debug.h"
14 #include <libgen.h>
15
16 /* Create new hash table type rpmFpEntryHash */
17 #include "lib/rpmhash.C"
18
19 #undef HASHTYPE
20 #undef HTKEYTYPE
21 #undef HTDATATYPE
22 #define HASHTYPE rpmFpEntryHash
23 #define HTKEYTYPE const char *
24 #define HTDATATYPE const struct fprintCacheEntry_s *
25 #include "lib/rpmhash.C"
26
27 fingerPrintCache fpCacheCreate(int sizeHint)
28 {
29     fingerPrintCache fpc;
30
31     fpc = xmalloc(sizeof(*fpc));
32     fpc->ht = rpmFpEntryHashCreate(sizeHint, hashFunctionString, strcmp,
33                                    (rpmFpEntryHashFreeKey)free,
34                                    (rpmFpEntryHashFreeData)free);
35     return fpc;
36 }
37
38 fingerPrintCache fpCacheFree(fingerPrintCache cache)
39 {
40     if (cache) {
41         cache->ht = rpmFpEntryHashFree(cache->ht);
42         free(cache);
43     }
44     return NULL;
45 }
46
47 /**
48  * Find directory name entry in cache.
49  * @param cache         pointer to fingerprint cache
50  * @param dirName       string to locate in cache
51  * @return pointer to directory name entry (or NULL if not found).
52  */
53 static const struct fprintCacheEntry_s * cacheContainsDirectory(
54                             fingerPrintCache cache,
55                             const char * dirName)
56 {
57     const struct fprintCacheEntry_s ** data;
58
59     if (rpmFpEntryHashGetEntry(cache->ht, dirName, &data, NULL, NULL))
60         return data[0];
61     return NULL;
62 }
63
64 /**
65  * Return finger print of a file path.
66  * @param cache         pointer to fingerprint cache
67  * @param dirName       leading directory name of path
68  * @param baseName      file name of path
69  * @param scareMemory
70  * @return pointer to the finger print associated with a file path.
71  */
72 fingerPrint fpLookup(fingerPrintCache cache,
73                 const char * dirName, const char * baseName, int scareMemory)
74 {
75     char dir[PATH_MAX];
76     const char * cleanDirName;
77     size_t cdnl;
78     char * end;             /* points to the '\0' at the end of "buf" */
79     fingerPrint fp;
80     struct stat sb;
81     char *buf = NULL;
82     char *cdnbuf = NULL;
83     const struct fprintCacheEntry_s * cacheHit;
84
85     /* assert(*dirName == '/' || !scareMemory); */
86
87     /* XXX WATCHOUT: fp.subDir is set below from relocated dirName arg */
88     cleanDirName = dirName;
89     cdnl = strlen(cleanDirName);
90
91     if (*cleanDirName == '/') {
92         if (!scareMemory) {
93             cdnbuf = xstrdup(dirName);
94             char trailingslash = (cdnbuf[strlen(cdnbuf)-1] == '/');
95             cdnbuf = rpmCleanPath(cdnbuf);
96             if (trailingslash) {
97                 cdnbuf = rstrcat(&cdnbuf, "/");
98             }
99             cleanDirName = cdnbuf;
100             cdnl = strlen(cleanDirName);
101         }
102     } else {
103         scareMemory = 0;        /* XXX causes memory leak */
104
105         /* Using realpath on the arg isn't correct if the arg is a symlink,
106          * especially if the symlink is a dangling link.  What we 
107          * do instead is use realpath() on `.' and then append arg to
108          * the result.
109          */
110
111         /* if the current directory doesn't exist, we might fail. 
112            oh well. likewise if it's too long.  */
113         dir[0] = '\0';
114         if (realpath(".", dir) != NULL) {
115             end = dir + strlen(dir);
116             if (end[-1] != '/') *end++ = '/';
117             end = stpncpy(end, cleanDirName, sizeof(dir) - (end - dir));
118             *end = '\0';
119             (void)rpmCleanPath(dir); /* XXX possible /../ from concatenation */
120             end = dir + strlen(dir);
121             if (end[-1] != '/') *end++ = '/';
122             *end = '\0';
123             cleanDirName = dir;
124             cdnl = end - dir;
125         }
126     }
127     fp.entry = NULL;
128     fp.subDir = NULL;
129     fp.baseName = NULL;
130     if (cleanDirName == NULL) goto exit; /* XXX can't happen */
131
132     buf = xstrdup(cleanDirName);
133     end = buf + cdnl;
134
135     /* no need to pay attention to that extra little / at the end of dirName */
136     if (buf[1] && end[-1] == '/') {
137         end--;
138         *end = '\0';
139     }
140
141     while (1) {
142
143         /* as we're stating paths here, we want to follow symlinks */
144
145         cacheHit = cacheContainsDirectory(cache, (*buf != '\0' ? buf : "/"));
146         if (cacheHit != NULL) {
147             fp.entry = cacheHit;
148         } else if (!stat((*buf != '\0' ? buf : "/"), &sb)) {
149             struct fprintCacheEntry_s * newEntry = xmalloc(sizeof(* newEntry));
150
151             newEntry->ino = sb.st_ino;
152             newEntry->dev = sb.st_dev;
153             newEntry->dirName = xstrdup((*buf != '\0' ? buf : "/"));
154             fp.entry = newEntry;
155
156             rpmFpEntryHashAddEntry(cache->ht, newEntry->dirName, fp.entry);
157         }
158
159         if (fp.entry) {
160             fp.subDir = cleanDirName + (end - buf);
161             if (fp.subDir[0] == '/' && fp.subDir[1] != '\0')
162                 fp.subDir++;
163             if (fp.subDir[0] == '\0' ||
164             /* XXX don't bother saving '/' as subdir */
165                (fp.subDir[0] == '/' && fp.subDir[1] == '\0'))
166                 fp.subDir = NULL;
167             fp.baseName = baseName;
168             if (!scareMemory && fp.subDir != NULL)
169                 fp.subDir = xstrdup(fp.subDir);
170         /* FIX: fp.entry.{dirName,dev,ino} undef @*/
171             goto exit;
172         }
173
174         /* stat of '/' just failed! */
175         if (end == buf + 1)
176             abort();
177
178         end--;
179         while ((end > buf) && *end != '/') end--;
180         if (end == buf)     /* back to stat'ing just '/' */
181             end++;
182
183         *end = '\0';
184     }
185
186 exit:
187     free(buf);
188     free(cdnbuf);
189     /* FIX: fp.entry.{dirName,dev,ino} undef @*/
190     return fp;
191 }
192
193 unsigned int fpHashFunction(const fingerPrint * fp)
194 {
195     unsigned int hash = 0;
196     int j;
197
198     hash = hashFunctionString(fp->baseName);
199     if (fp->subDir) hash ^= hashFunctionString(fp->subDir);
200
201     hash ^= ((unsigned)fp->entry->dev);
202     for (j=0; j<4; j++) hash ^= ((fp->entry->ino >> (8*j)) & 0xFF) << ((3-j)*8);
203     
204     return hash;
205 }
206
207 int fpEqual(const fingerPrint * k1, const fingerPrint * k2)
208 {
209     /* If the addresses are the same, so are the values. */
210     if (k1 == k2)
211         return 0;
212
213     /* Otherwise, compare fingerprints by value. */
214     if (FP_EQUAL(*k1, *k2))
215         return 0;
216     return 1;
217
218 }
219
220 void fpLookupList(fingerPrintCache cache, const char ** dirNames, 
221                   const char ** baseNames, const uint32_t * dirIndexes, 
222                   int fileCount, fingerPrint * fpList)
223 {
224     int i;
225
226     for (i = 0; i < fileCount; i++) {
227         /* If this is in the same directory as the last file, don't bother
228            redoing all of this work */
229         if (i > 0 && dirIndexes[i - 1] == dirIndexes[i]) {
230             fpList[i].entry = fpList[i - 1].entry;
231             fpList[i].subDir = fpList[i - 1].subDir;
232             fpList[i].baseName = baseNames[i];
233         } else {
234             fpList[i] = fpLookup(cache, dirNames[dirIndexes[i]], baseNames[i],
235                                  1);
236         }
237     }
238 }
239
240 void fpLookupSubdir(rpmFpHash symlinks, rpmFpHash fphash, fingerPrintCache fpc, rpmte p, int filenr)
241 {
242     rpmfi fi = rpmteFI(p);
243     struct fingerPrint_s current_fp;
244     char *endsubdir, *endbasename, *currentsubdir;
245     size_t lensubDir;
246
247     struct rpmffi_s * recs;
248     int numRecs;
249     int i, fiFX;
250     fingerPrint *fp = rpmfiFpsIndex(fi, filenr);
251     int symlinkcount = 0;
252     struct rpmffi_s ffi = { p, filenr};
253
254     if (fp->subDir == NULL) {
255          rpmFpHashAddEntry(fphash, fp, ffi);
256          return;
257     }
258
259     lensubDir = strlen(fp->subDir);
260     current_fp = *fp;
261     currentsubdir = xstrdup(fp->subDir);
262
263     /* Set baseName to the upper most dir */
264     current_fp.baseName = endbasename = currentsubdir;
265     while (*endbasename != '/' && endbasename < currentsubdir + lensubDir - 1)
266          endbasename++;
267     *endbasename = '\0';
268
269     current_fp.subDir = endsubdir = NULL; // no subDir for now
270
271     while (endbasename < currentsubdir + lensubDir - 1) {
272          char found;
273          found = 0;
274
275          rpmFpHashGetEntry(symlinks, &current_fp,
276                     &recs, &numRecs, NULL);
277
278          for (i=0; i<numRecs; i++) {
279               rpmfi foundfi;
280               int filenr;
281               char const *linktarget;
282               char *link;
283
284               foundfi =  rpmteFI(recs[i].p);
285               fiFX = rpmfiFX(foundfi);
286
287               filenr = recs[i].fileno;
288               rpmfiSetFX(foundfi, filenr);
289               linktarget = rpmfiFLink(foundfi);
290
291               if (linktarget && *linktarget != '\0') {
292                    /* this "directory" is a symlink */
293                    link = NULL;
294                    if (*linktarget != '/') {
295                         rstrscat(&link, current_fp.entry->dirName,
296                                  current_fp.subDir ? "/" : "",
297                                  current_fp.subDir ? current_fp.subDir : "",
298                                  "/", NULL);
299                    }
300                    rstrscat(&link, linktarget, "/", NULL);
301                    if (strlen(endbasename+1)) {
302                         rstrscat(&link, endbasename+1, "/", NULL);
303                    }
304
305                    *fp = fpLookup(fpc, link, fp->baseName, 0);
306
307                    free(link);
308                    free(currentsubdir);
309                    symlinkcount++;
310
311                    /* setup current_fp for the new path */
312                    found = 1;
313                    current_fp = *fp;
314                    if (fp->subDir == NULL) {
315                      /* directory exists - no need to look for symlinks */
316                      rpmFpHashAddEntry(fphash, fp, ffi);
317                      return;
318                    }
319                    lensubDir = strlen(fp->subDir);
320                    currentsubdir = xstrdup(fp->subDir);
321                    current_fp.subDir = endsubdir = NULL; // no subDir for now
322
323                    /* Set baseName to the upper most dir */
324                    current_fp.baseName = currentsubdir;
325                    endbasename = currentsubdir;
326                    while (*endbasename != '/' &&
327                           endbasename < currentsubdir + lensubDir - 1)
328                         endbasename++;
329                    *endbasename = '\0';
330                    break;
331
332               }
333               rpmfiSetFX(foundfi, fiFX);
334          }
335          if (symlinkcount>50) {
336               // found too many symlinks in the path
337               // most likley a symlink cicle
338               // giving up
339               // TODO warning/error
340               break;
341          }
342          if (found) {
343               continue; // restart loop after symlink
344          }
345
346          if (current_fp.subDir == NULL) {
347               /* after first round set former baseName as subDir */
348               current_fp.subDir = currentsubdir;
349          } else {
350               *endsubdir = '/'; // rejoin the former baseName with subDir
351          }
352          endsubdir = endbasename;
353
354          /* set baseName to the next lower dir */
355          endbasename++;
356          while (*endbasename != '\0' && *endbasename != '/')
357               endbasename++;
358          *endbasename = '\0';
359          current_fp.baseName = endsubdir+1;
360
361     }
362     free(currentsubdir);
363     rpmFpHashAddEntry(fphash, fp, ffi);
364
365 }