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