File Coverage

bpc_attribCache.c
Criterion Covered Total %
statement 0 394 0.0
branch 0 302 0.0
condition n/a
subroutine n/a
pod n/a
total 0 696 0.0


line stmt bran cond sub pod time code
1             /*
2             * Routines for caching multiple directories.
3             *
4             * Copyright (C) 2013 Craig Barratt.
5             *
6             * This program is free software; you can redistribute it and/or modify
7             * it under the terms of the GNU General Public License as published by
8             * the Free Software Foundation; either version 3 of the License, or
9             * (at your option) any later version.
10             *
11             * This program is distributed in the hope that it will be useful,
12             * but WITHOUT ANY WARRANTY; without even the implied warranty of
13             * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14             * GNU General Public License for more details.
15             *
16             * You should have received a copy of the GNU General Public License along
17             * with this program; if not, visit the http://fsf.org website.
18             */
19              
20             #include "backuppc.h"
21              
22             #define BPC_ATTRIBCACHE_DIR_COUNT_MAX (380)
23             #define BPC_ATTRIBCACHE_DIR_HT_SIZE (512)
24              
25 0           void bpc_attribCache_init(bpc_attribCache_info *ac, char *hostName, int backupNum, char *shareNameUM, int compress)
26             {
27 0           ac->backupNum = backupNum;
28 0           ac->compress = compress;
29 0           ac->cacheLruCnt = 0;
30 0           ac->bkupMergeList = NULL;
31 0           ac->bkupMergeCnt = 0;
32 0           ac->currentDir[0] = '\0';
33 0           ac->deltaInfo = NULL;
34 0           strncpy(ac->hostName, hostName, BPC_MAXPATHLEN);
35 0           ac->hostName[BPC_MAXPATHLEN - 1] = '\0';
36 0           strncpy(ac->shareNameUM, shareNameUM, BPC_MAXPATHLEN);
37 0           ac->shareNameUM[BPC_MAXPATHLEN - 1] = '\0';
38 0           bpc_fileNameEltMangle(ac->shareName, BPC_MAXPATHLEN, ac->shareNameUM);
39 0           ac->shareNameLen = strlen(ac->shareName);
40 0           snprintf(ac->hostDir, BPC_MAXPATHLEN, "%s/pc/%s", BPC_TopDir, hostName);
41 0           snprintf(ac->backupTopDir, BPC_MAXPATHLEN, "%s/pc/%s/%d", BPC_TopDir, hostName, ac->backupNum);
42 0           bpc_path_create(ac->backupTopDir);
43              
44 0           bpc_hashtable_create(&ac->attrHT, BPC_ATTRIBCACHE_DIR_HT_SIZE, sizeof(bpc_attribCache_dir));
45 0           bpc_hashtable_create(&ac->inodeHT, BPC_ATTRIBCACHE_DIR_HT_SIZE, sizeof(bpc_attribCache_dir));
46 0           }
47              
48 0           void bpc_attribCache_setDeltaInfo(bpc_attribCache_info *ac, bpc_deltaCount_info *deltaInfo)
49             {
50 0           ac->deltaInfo = deltaInfo;
51 0           }
52              
53             /*
54             * Caller is responsible for calling malloc for bkupList.
55             */
56 0           void bpc_attribCache_setMergeList(bpc_attribCache_info *ac, bpc_backup_info *bkupList, int bkupCnt)
57             {
58 0           ac->bkupMergeList = bkupList;
59 0           ac->bkupMergeCnt = bkupCnt;
60 0           }
61              
62 0           static void bpc_attribCache_destroyEntry(bpc_attribCache_dir *attr)
63             {
64 0           bpc_attrib_dirDestroy(&attr->dir);
65 0           }
66              
67 0           void bpc_attribCache_destroy(bpc_attribCache_info *ac)
68             {
69 0           bpc_hashtable_iterate(&ac->attrHT, (void*)bpc_attribCache_destroyEntry, NULL);
70 0           bpc_hashtable_destroy(&ac->attrHT);
71 0           bpc_hashtable_iterate(&ac->inodeHT, (void*)bpc_attribCache_destroyEntry, NULL);
72 0           bpc_hashtable_destroy(&ac->inodeHT);
73 0 0         if ( ac->bkupMergeList ) free(ac->bkupMergeList);
74 0           ac->bkupMergeList = NULL;
75 0           ac->bkupMergeCnt = 0;
76 0           }
77              
78 0           int bpc_attribCache_readOnly(bpc_attribCache_info *ac, int readOnly)
79             {
80 0 0         if ( readOnly >= 0 ) ac->readOnly = readOnly;
81 0           return ac->readOnly;
82             }
83              
84 0           void bpc_attribCache_setCurrentDirectory(bpc_attribCache_info *ac, char *dir)
85             {
86             char *p;
87 0           snprintf(ac->currentDir, BPC_MAXPATHLEN, "%s", dir);
88 0           p = ac->currentDir + strlen(ac->currentDir) - 1;
89 0 0         while ( p >= ac->currentDir && p[0] == '/' ) *p-- = '\0';
    0          
90 0           }
91              
92             /*
93             * Given a backup path, split it into the directory, file name, and path to the directory (starting
94             * with the share name, ie: relative to ac->backupTopDir).
95             *
96             * splitPath will strip initial "./" and trailing "/." or "/" before splitting the path, but isn't
97             * capable of handling paths with "/." in the middle, or ".." anywhere.
98             */
99 0           static void splitPath(bpc_attribCache_info *ac, char *dir, char *fileName, char *attribPath, char *path)
100             {
101 0           char *dirOrig = dir;
102             char fullPath[2*BPC_MAXPATHLEN];
103             size_t pathLen;
104              
105             /*
106             * remove initial "./"
107             */
108 0 0         while ( path[0] == '.' && path[1] == '/' ) {
    0          
109 0           path += 2;
110 0 0         while ( path[0] == '/' ) path++;
111             }
112              
113             /*
114             * if this is a relative path, prepend ac->currentDir (provided ac->currentDir is set)
115             */
116 0 0         if ( path[0] != '/' && ac->currentDir[0] ) {
    0          
117 0           snprintf(fullPath, sizeof(fullPath), "%s/%s", ac->currentDir, path);
118 0           path = fullPath;
119             }
120              
121             /*
122             * strip trailing "/." or "/"
123             */
124 0           pathLen = strlen(path);
125 0 0         while ( (pathLen > 1 && path[pathLen - 2] == '/' && path[pathLen - 1] == '.')
    0          
    0          
126 0 0         || (pathLen > 0 && path[pathLen - 1] == '/') ) {
    0          
127 0 0         if ( path != fullPath ) {
128 0           strncpy(fullPath, path, BPC_MAXPATHLEN);
129 0           path = fullPath;
130             }
131 0 0         if ( path[pathLen - 1] == '/' ) {
132 0           pathLen -= 1;
133             } else {
134 0           pathLen -= 2;
135             }
136 0           path[pathLen] = '\0';
137 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("splitPath: trimming path = '%s'\n", path);
138             }
139 0 0         if ( !path[0] || (!path[1] && (path[0] == '.' || path[0] == '/')) ) {
    0          
    0          
    0          
140 0           strcpy(fileName, ac->shareNameUM);
141 0           strcpy(dir, "/");
142 0           strcpy(attribPath, "/attrib");
143             } else {
144             char *p;
145 0           int dirLen = BPC_MAXPATHLEN - ac->shareNameLen;
146              
147 0           strcpy(dir, ac->shareName);
148 0           dir += strlen(dir);
149 0 0         if ( (p = strrchr(path, '/')) ) {
150 0 0         if ( *path != '/' ) {
151 0           *dir++ = '/'; dirLen--;
152 0           *dir = '\0';
153             }
154 0           strcpy(fileName, p+1);
155 0           *p = '\0';
156 0           bpc_fileNameMangle(dir, dirLen, path);
157 0           *p = '/';
158             } else {
159 0           strcpy(fileName, path);
160             }
161 0           snprintf(attribPath, BPC_MAXPATHLEN, "%s/attrib", dirOrig);
162             }
163 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("splitPath: returning dir = '%s', fileName = '%s', attrib = '%s' from path = '%s'\n",
164             dirOrig, fileName, attribPath, path);
165 0           }
166              
167 0           static void inodePath(UNUSED(bpc_attribCache_info *ac), char *indexStr, char *attribPath, char *attribFile, ino_t inode)
168             {
169 0           snprintf(attribPath, BPC_MAXPATHLEN, "inode/%02x", (unsigned int)(inode >> 17) & 0x7f);
170 0           snprintf(attribFile, BPC_MAXPATHLEN, "attrib%02x", (unsigned int)(inode >> 10) & 0x7f);
171             do {
172 0           bpc_byte2hex(indexStr, inode & 0xff);
173 0           indexStr += 2;
174 0           inode >>= 8;
175 0 0         } while ( inode );
176 0           *indexStr = '\0';
177 0           }
178              
179 0           static void bpc_attribCache_removeDeletedEntries(bpc_attrib_file *file, void *arg)
180             {
181 0           bpc_attribCache_dir *attr = (bpc_attribCache_dir*)arg;
182 0 0         if ( file->type != BPC_FTYPE_DELETED ) return;
183 0           attr->dirty = 1;
184 0           bpc_attrib_fileDestroy(file);
185 0           bpc_hashtable_nodeDelete(&attr->dir.filesHT, file);
186             }
187              
188 0           static bpc_attribCache_dir *bpc_attribCache_loadPath(bpc_attribCache_info *ac, char *fileName, char *path)
189             {
190             char dir[BPC_MAXPATHLEN], attribPath[BPC_MAXPATHLEN];
191             bpc_attribCache_dir *attr;
192             int attribPathLen, status;
193              
194 0           splitPath(ac, dir, fileName, attribPath, path);
195 0           attribPathLen = strlen(attribPath);
196              
197 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_attribCache_loadPath: path = %s -> dir = %s, fileName = %s, attribPath = %s\n", path, dir, fileName, attribPath);
198              
199 0           attr = bpc_hashtable_find(&ac->attrHT, (uchar*)attribPath, attribPathLen, 1);
200              
201 0 0         if ( !attr || attr->key.key != attribPath ) {
    0          
202             /*
203             * cache hit - return the existing attributes
204             */
205 0 0         if ( attr ) attr->lruCnt = ac->cacheLruCnt++;
206 0           return attr;
207             }
208              
209 0 0         if ( !(attr->key.key = malloc(attribPathLen + 1)) ) {
210 0           bpc_logErrf("bpc_attribCache_loadPath: can't allocate %d bytes\n", attribPathLen + 1);
211 0           return NULL;
212             }
213 0           strcpy(attr->key.key, attribPath);
214 0           bpc_attrib_dirInit(&attr->dir, ac->compress);
215 0           attr->dirty = 0;
216 0           attr->dirOk = 0;
217 0           attr->lruCnt = ac->cacheLruCnt++;
218              
219 0 0         if ( ac->bkupMergeCnt > 0 ) {
220             int i;
221             char topDir[2*BPC_MAXPATHLEN], fullAttribPath[2*BPC_MAXPATHLEN];
222              
223             /*
224             * Merge multiple attrib files to create the "view" for this backup.
225             * There are two cases: merging forward for v3, or merging in reverse
226             * for v4+. bkupMergeList is already in the order we need.
227             */
228 0 0         for ( i = 0 ; i < ac->bkupMergeCnt ; i++ ) {
229             bpc_attrib_dir dir;
230             ssize_t entrySize;
231             char *entries, *fileName;
232 0           int attribFileExists, attribDirExists = 1;
233             STRUCT_STAT st;
234              
235 0           snprintf(topDir, sizeof(topDir), "%s/pc/%s/%d", BPC_TopDir, ac->hostName, ac->bkupMergeList[i].num);
236 0           snprintf(fullAttribPath, sizeof(fullAttribPath), "%s/%s", topDir, attribPath);
237              
238 0 0         attribFileExists = !stat(fullAttribPath, &st) && S_ISREG(st.st_mode);
    0          
239              
240 0 0         if ( !attribFileExists ) {
241             char *p;
242 0 0         if ( (p = strrchr(fullAttribPath, '/')) ) {
243 0           *p = '\0';
244 0 0         attribDirExists = !stat(fullAttribPath, &st) && S_ISDIR(st.st_mode);
    0          
245             }
246             }
247 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_attribCache_loadPath: path = %s, file exists = %d, dir exists = %d\n", fullAttribPath, attribFileExists, attribDirExists);
248              
249 0 0         if ( ac->bkupMergeList[i].version < 4 && i == ac->bkupMergeCnt - 1 && !attribFileExists && !attribDirExists ) {
    0          
    0          
    0          
250             /*
251             * For V3, if the last backup doesn't have a directory, then the merged view is empty
252             */
253 0           bpc_attrib_dirDestroy(&attr->dir);
254 0           bpc_attrib_dirInit(&attr->dir, ac->compress);
255 0           break;
256             }
257 0 0         if ( (ac->bkupMergeList[i].version < 4 && !attribFileExists) || !attribDirExists ) {
    0          
    0          
258             /*
259             * nothing to update here - keep going
260             */
261 0           continue;
262             }
263 0           bpc_attrib_dirInit(&dir, ac->bkupMergeList[i].compress);
264 0 0         if ( (status = bpc_attrib_dirRead(&dir, topDir, attribPath, ac->bkupMergeList[i].num)) ) {
265 0           bpc_logErrf("bpc_attribCache_loadPath: bpc_attrib_dirRead(%s/%s) returned %d\n", topDir, attribPath, status);
266             }
267 0           entrySize = bpc_attrib_getEntries(&dir, NULL, 0);
268 0 0         if ( (entries = malloc(entrySize)) && bpc_attrib_getEntries(&dir, entries, entrySize) == entrySize ) {
    0          
269 0 0         for ( fileName = entries ; fileName < entries + entrySize ; fileName += strlen(fileName) + 1 ) {
270 0           bpc_attrib_file *file = bpc_attrib_fileGet(&dir, fileName, 0);
271 0 0         if ( !file ) continue;
272 0 0         if ( file->type == BPC_FTYPE_DELETED ) {
273 0           bpc_attrib_fileDeleteName(&attr->dir, fileName);
274             } else {
275             bpc_attrib_file *fileDest;
276              
277 0 0         if ( !(fileDest = bpc_attrib_fileGet(&attr->dir, fileName, 1)) ) return NULL;
278 0 0         if ( fileDest->key.key == fileName ) {
279             /*
280             * new entry - initialize
281             */
282 0           bpc_attrib_fileInit(fileDest, fileName, 0);
283             }
284 0           bpc_attrib_fileCopy(fileDest, file);
285 0           fileDest->backupNum = ac->bkupMergeList[i].num;
286             }
287             }
288             } else {
289 0           bpc_logErrf("bpc_attribCache_loadPath(%s/%s): can't malloc %lu bytes for entries\n",
290             topDir, attribPath, (unsigned long)entrySize);
291 0 0         if ( entries ) free(entries);
292 0           bpc_attrib_dirDestroy(&dir);
293 0           return NULL;
294             }
295 0           free(entries);
296 0           bpc_attrib_dirDestroy(&dir);
297             }
298             } else {
299             /*
300             * non-merge case - read the single attrib file
301             */
302 0 0         if ( (status = bpc_attrib_dirRead(&attr->dir, ac->backupTopDir, attribPath, ac->backupNum)) ) {
303 0           bpc_logErrf("bpc_attribCache_loadPath: bpc_attrib_dirRead(%s, %s) returned %d\n", ac->backupTopDir, attribPath, status);
304             }
305             /*
306             * remove any extraneous BPC_FTYPE_DELETED file types
307             */
308 0           bpc_hashtable_iterate(&attr->dir.filesHT, (void*)bpc_attribCache_removeDeletedEntries, attr);
309             }
310 0 0         if ( bpc_hashtable_entryCount(&ac->attrHT) > BPC_ATTRIBCACHE_DIR_COUNT_MAX ) {
311 0           bpc_attribCache_flush(ac, 0, NULL);
312             }
313 0           return attr;
314             }
315              
316 0           static bpc_attribCache_dir *bpc_attribCache_loadInode(bpc_attribCache_info *ac, char *indexStr, ino_t inode)
317             {
318             char attribPath[BPC_MAXPATHLEN], attribDir[BPC_MAXPATHLEN], attribFile[BPC_MAXPATHLEN];
319             bpc_attribCache_dir *attr;
320             int attribPathLen, status;
321              
322 0           inodePath(ac, indexStr, attribDir, attribFile, inode);
323 0           attribPathLen = snprintf(attribPath, sizeof(attribPath), "%s/%s", attribDir, attribFile);
324              
325 0           attr = bpc_hashtable_find(&ac->inodeHT, (uchar*)attribPath, attribPathLen, 1);
326              
327 0 0         if ( !attr || attr->key.key != attribPath ) {
    0          
328 0 0         if ( attr ) attr->lruCnt = ac->cacheLruCnt++;
329 0           return attr;
330             }
331              
332             /*
333             * new entry - read the attrib file
334             */
335 0 0         if ( !(attr->key.key = malloc(attribPathLen + 1)) ) {
336 0           bpc_logErrf("bpc_attribCache_loadInode: can't allocate %d bytes\n", attribPathLen + 1);
337 0           return NULL;
338             }
339 0           strcpy(attr->key.key, attribPath);
340 0           bpc_attrib_dirInit(&attr->dir, ac->compress);
341 0           attr->dirty = 0;
342 0           attr->dirOk = 1;
343 0           attr->lruCnt = ac->cacheLruCnt++;
344 0 0         if ( ac->bkupMergeCnt > 0 ) {
345             int i;
346             char inodeDir[2*BPC_MAXPATHLEN], fullAttribPath[2*BPC_MAXPATHLEN];
347              
348             /*
349             * Merge multiple attrib files to create the "view" for this backup.
350             * There is only one case here, v4, since v3 didn't have inodes.
351             */
352 0 0         for ( i = 0 ; i < ac->bkupMergeCnt ; i++ ) {
353             bpc_attrib_dir dir;
354             ssize_t entrySize;
355             char *entries, *fileName;
356 0           int attribFileExists, attribDirExists = 1;
357             STRUCT_STAT st;
358              
359 0           snprintf(inodeDir, sizeof(inodeDir), "%s/pc/%s/%d/%s", BPC_TopDir, ac->hostName, ac->bkupMergeList[i].num, attribDir);
360 0           snprintf(fullAttribPath, sizeof(fullAttribPath), "%s/%s", inodeDir, attribFile);
361              
362 0 0         attribFileExists = !stat(fullAttribPath, &st) && S_ISREG(st.st_mode);
    0          
363 0 0         if ( !attribFileExists ) {
364 0 0         attribDirExists = !stat(inodeDir, &st) && S_ISDIR(st.st_mode);
    0          
365             }
366 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_attribCache_loadInode: path = %s, file exists = %d, dir exists = %d\n", fullAttribPath, attribFileExists, attribDirExists);
367              
368 0 0         if ( (ac->bkupMergeList[i].version < 4 && !attribFileExists) || !attribDirExists ) {
    0          
    0          
369             /*
370             * nothing to update here - keep going
371             */
372 0           continue;
373             }
374 0           bpc_attrib_dirInit(&dir, ac->bkupMergeList[i].compress);
375 0 0         if ( (status = bpc_attrib_dirRead(&dir, inodeDir, attribFile, ac->bkupMergeList[i].num)) ) {
376 0           bpc_logErrf("bpc_attribCache_loadInode: bpc_attrib_dirRead(%s/%s) returned %d\n", inodeDir, attribFile, status);
377             }
378 0           entrySize = bpc_attrib_getEntries(&dir, NULL, 0);
379 0 0         if ( (entries = malloc(entrySize)) && bpc_attrib_getEntries(&dir, entries, entrySize) == entrySize ) {
    0          
380 0 0         for ( fileName = entries ; fileName < entries + entrySize ; fileName += strlen(fileName) + 1 ) {
381 0           bpc_attrib_file *file = bpc_attrib_fileGet(&dir, fileName, 0);
382 0 0         if ( !file ) continue;
383 0 0         if ( file->type == BPC_FTYPE_DELETED ) {
384 0           bpc_attrib_fileDeleteName(&attr->dir, fileName);
385             } else {
386             bpc_attrib_file *fileDest;
387              
388 0 0         if ( !(fileDest = bpc_attrib_fileGet(&attr->dir, fileName, 1)) ) return NULL;
389 0 0         if ( fileDest->key.key == fileName ) {
390             /*
391             * new entry - initialize
392             */
393 0           bpc_attrib_fileInit(fileDest, fileName, 0);
394             }
395 0           bpc_attrib_fileCopy(fileDest, file);
396             }
397             }
398             } else {
399 0           bpc_logErrf("bpc_attribCache_loadInode(%s): can't malloc %lu bytes for entries\n",
400             fullAttribPath, (unsigned long)entrySize);
401 0 0         if ( entries ) free(entries);
402 0           bpc_attrib_dirDestroy(&dir);
403 0           return NULL;
404             }
405 0           free(entries);
406 0           bpc_attrib_dirDestroy(&dir);
407             }
408             } else {
409             /*
410             * non-merge case - read the single attrib file
411             */
412             char inodeDir[2*BPC_MAXPATHLEN];
413 0           snprintf(inodeDir, sizeof(inodeDir), "%s/%s", ac->backupTopDir, attribDir);
414              
415 0 0         if ( (status = bpc_attrib_dirRead(&attr->dir, inodeDir, attribFile, ac->backupNum)) ) {
416 0           bpc_logErrf("bpc_attribCache_loadInode: bpc_attrib_dirRead(%s/%s) returned %d\n", inodeDir, attribFile, status);
417             }
418             }
419 0 0         if ( bpc_hashtable_entryCount(&ac->inodeHT) > BPC_ATTRIBCACHE_DIR_COUNT_MAX ) {
420 0           bpc_attribCache_flush(ac, 0, NULL);
421             }
422 0           return attr;
423             }
424              
425 0           bpc_attrib_file *bpc_attribCache_getFile(bpc_attribCache_info *ac, char *path, int allocate_if_missing, int dontReadInode)
426             {
427             char fileName[BPC_MAXPATHLEN];
428             bpc_attribCache_dir *attr;
429             bpc_attrib_file *file;
430              
431 0 0         if ( !(attr = bpc_attribCache_loadPath(ac, fileName, path)) ) return NULL;
432 0           attr->lruCnt = ac->cacheLruCnt++;
433 0 0         if ( !(file = bpc_attrib_fileGet(&attr->dir, fileName, allocate_if_missing)) ) return NULL;
434              
435 0 0         if ( allocate_if_missing && file->key.key == fileName ) {
    0          
436             /*
437             * new entry - initialize
438             */
439 0           bpc_attrib_fileInit(file, fileName, 0);
440 0           file->compress = ac->compress;
441             }
442 0 0         if ( dontReadInode || file->nlinks == 0 ) return file;
    0          
443              
444 0           return bpc_attribCache_getInode(ac, file->inode, allocate_if_missing);
445             }
446              
447 0           int bpc_attribCache_setFile(bpc_attribCache_info *ac, char *path, bpc_attrib_file *file, int dontOverwriteInode)
448             {
449             char fileName[BPC_MAXPATHLEN], indexStr[256];
450             bpc_attribCache_dir *attr, *attrInode;
451             bpc_attrib_file *fileDest;
452              
453 0 0         if ( !(attr = bpc_attribCache_loadPath(ac, fileName, path)) ) return -1;
454 0           attr->lruCnt = ac->cacheLruCnt++;
455 0           file->compress = ac->compress;
456              
457 0 0         if ( !(fileDest = bpc_attrib_fileGet(&attr->dir, fileName, 1)) ) return -1;
458              
459 0 0         if ( fileDest->key.key == fileName ) {
460             /*
461             * new entry - initialize
462             */
463 0           bpc_attrib_fileInit(fileDest, fileName, 0);
464             }
465              
466 0           bpc_attrib_fileCopy(fileDest, file);
467 0           attr->dirty = 1;
468 0 0         if ( file->nlinks > 0 ) {
469 0           bpc_attrib_file *inodeDest = bpc_attribCache_getInode(ac, file->inode, 0);
470 0 0         if ( !dontOverwriteInode || !inodeDest ) {
    0          
471 0           inodeDest = bpc_attribCache_getInode(ac, file->inode, 1);
472 0           bpc_attrib_fileCopyOpt(inodeDest, file, 0);
473              
474 0           attrInode = bpc_attribCache_loadInode(ac, indexStr, file->inode);
475 0           attrInode->dirty = 1;
476             /*
477             * remove the digest from the file attributes since the reference counting is reflected
478             * by the inode (can't do this up above since fileDest might be the same as file).
479             */
480 0           fileDest->digest.len = 0;
481 0           return 1;
482             } else {
483             /*
484             * remove the digest from the file attributes since the reference counting is reflected
485             * by the inode (can't do this up above since fileDest might be the same as file).
486             */
487 0           fileDest->digest.len = 0;
488 0           return 0;
489             }
490             }
491 0           return 1;
492             }
493              
494 0           int bpc_attribCache_deleteFile(bpc_attribCache_info *ac, char *path)
495             {
496             char fileName[BPC_MAXPATHLEN];
497             bpc_attribCache_dir *attr;
498              
499 0 0         if ( !(attr = bpc_attribCache_loadPath(ac, fileName, path)) ) return -1;
500 0           attr->lruCnt = ac->cacheLruCnt++;
501 0           bpc_attrib_fileDeleteName(&attr->dir, fileName);
502 0           attr->dirty = 1;
503 0           return 0;
504             }
505              
506 0           bpc_attrib_file *bpc_attribCache_getInode(bpc_attribCache_info *ac, ino_t inode, int allocate_if_missing)
507             {
508             char indexStr[256];
509             bpc_attribCache_dir *attr;
510             bpc_attrib_file *file;
511              
512 0 0         if ( !(attr = bpc_attribCache_loadInode(ac, indexStr, inode)) ) return NULL;
513 0           attr->lruCnt = ac->cacheLruCnt++;
514 0 0         if ( !(file = bpc_attrib_fileGet(&attr->dir, indexStr, allocate_if_missing)) ) return NULL;
515              
516 0 0         if ( allocate_if_missing && file->key.key == indexStr ) {
    0          
517             /*
518             * new entry - initialize
519             */
520 0           bpc_attrib_fileInit(file, indexStr, 0);
521 0           file->compress = ac->compress;
522             }
523 0           return file;
524             }
525              
526 0           int bpc_attribCache_setInode(bpc_attribCache_info *ac, ino_t inode, bpc_attrib_file *inodeSrc)
527             {
528             char indexStr[256];
529             bpc_attribCache_dir *attr;
530             bpc_attrib_file *inodeDest;
531              
532 0 0         if ( !(attr = bpc_attribCache_loadInode(ac, indexStr, inode)) ) return -1;
533 0           attr->lruCnt = ac->cacheLruCnt++;
534 0 0         if ( !(inodeDest = bpc_attrib_fileGet(&attr->dir, indexStr, 1)) ) return -1;
535              
536 0 0         if ( inodeDest->key.key == indexStr ) {
537             /*
538             * new entry - initialize
539             */
540 0           bpc_attrib_fileInit(inodeDest, indexStr, 0);
541             }
542 0           bpc_attrib_fileCopy(inodeDest, inodeSrc);
543 0           attr->dirty = 1;
544 0           return 0;
545             }
546              
547 0           int bpc_attribCache_deleteInode(bpc_attribCache_info *ac, ino_t inode)
548             {
549             char indexStr[256];
550             bpc_attribCache_dir *attr;
551              
552 0 0         if ( !(attr = bpc_attribCache_loadInode(ac, indexStr, inode)) ) return -1;
553 0           attr->lruCnt = ac->cacheLruCnt++;
554 0           bpc_attrib_fileDeleteName(&attr->dir, indexStr);
555 0           attr->dirty = 1;
556 0           return 0;
557             }
558              
559 0           int bpc_attribCache_getDirEntryCnt(bpc_attribCache_info *ac, char *path)
560             {
561             bpc_attribCache_dir *attr;
562             char fileName[BPC_MAXPATHLEN];
563 0           size_t pathLen = strlen(path);
564              
565             /*
566             * Append a fake file name so we actually open the directory's contents, not the directory entry one level up
567             */
568 0 0         if ( pathLen >= BPC_MAXPATHLEN - 3 ) return -1;
569 0           strcpy(path + pathLen, "/x");
570 0           attr = bpc_attribCache_loadPath(ac, fileName, path);
571 0           path[pathLen] = '\0';
572 0 0         if ( !attr ) return -1;
573 0           return bpc_hashtable_entryCount(&attr->dir.filesHT);
574             }
575              
576             typedef struct {
577             char *entries;
578             ssize_t entryIdx;
579             ssize_t entrySize;
580             } dirEntry_info;
581              
582 0           static void bpc_attribCache_getDirEntry(bpc_attrib_file *file, dirEntry_info *info)
583             {
584 0           ssize_t len = strlen(file->name) + 1;
585              
586 0 0         if ( info->entryIdx < 0 ) return;
587 0 0         if ( info->entries ) {
588 0 0         if ( info->entryIdx + len + (ssize_t)sizeof(ino_t) > info->entrySize ) {
589 0           info->entryIdx = -1;
590 0           return;
591             }
592 0           memcpy(info->entries + info->entryIdx, file->name, len);
593 0           info->entryIdx += len;
594 0           memcpy(info->entries + info->entryIdx, &file->inode, sizeof(ino_t));
595 0           info->entryIdx += sizeof(ino_t);
596             } else {
597 0           info->entryIdx += len + sizeof(ino_t);
598             }
599             }
600              
601 0           ssize_t bpc_attribCache_getDirEntries(bpc_attribCache_info *ac, char *path, char *entries, ssize_t entrySize)
602             {
603             bpc_attribCache_dir *attr;
604             char fileName[BPC_MAXPATHLEN], fullPath[2*BPC_MAXPATHLEN];
605             dirEntry_info info;
606 0           size_t pathLen = strlen(path);
607 0           ino_t inode = 0;
608              
609             /*
610             * Append a fake file name so we actually open the directory's contents, not the directory entry one level up
611             */
612 0 0         if ( pathLen >= BPC_MAXPATHLEN - 3 ) return -1;
613 0 0         if ( pathLen == 1 && path[0] == '.' ) {
    0          
614 0 0         if ( ac->currentDir[0] ) {
615 0           snprintf(fullPath, sizeof(fullPath), "%s/x", ac->currentDir);
616             } else {
617 0           strcpy(fullPath, "/x");
618             }
619 0           attr = bpc_attribCache_loadPath(ac, fileName, fullPath);
620 0           strcpy(path, ".");
621             } else {
622 0           snprintf(fullPath, BPC_MAXPATHLEN, "%s/x", path);
623 0           attr = bpc_attribCache_loadPath(ac, fileName, fullPath);
624             }
625 0 0         if ( !attr ) return -1;
626 0           attr->lruCnt = ac->cacheLruCnt++;
627              
628 0           info.entries = entries;
629 0           info.entryIdx = 0;
630 0           info.entrySize = entrySize;
631              
632 0 0         if ( entries && entrySize >= (ssize_t)(5 + 2 * sizeof(ino_t)) ) {
    0          
633 0           strcpy(info.entries + info.entryIdx, ".");
634 0           info.entryIdx += 2;
635             /* dummy inode number */
636 0           memcpy(info.entries + info.entryIdx, &inode, sizeof(inode));
637 0           info.entryIdx += sizeof(inode);
638              
639 0           strcpy(info.entries + info.entryIdx, "..");
640 0           info.entryIdx += 3;
641             /* dummy inode number */
642 0           memcpy(info.entries + info.entryIdx, &inode, sizeof(inode));
643 0           info.entryIdx += sizeof(inode);
644              
645             } else {
646 0           info.entryIdx += 5 + 2 * sizeof(ino_t);
647             }
648              
649 0           bpc_hashtable_iterate(&attr->dir.filesHT, (void*)bpc_attribCache_getDirEntry, &info);
650 0           return info.entryIdx;
651             }
652              
653             typedef struct {
654             char *path;
655             int pathLen;
656             int all;
657             bpc_attribCache_info *ac;
658             int entryCnt;
659             int entryIdx;
660             bpc_attribCache_dir **entries;
661             bpc_hashtable *ht;
662             int errorCnt;
663             } flush_info;
664              
665 0           static void bpc_attribCache_dirWrite(bpc_attribCache_dir *attr, flush_info *info)
666             {
667             int status;
668              
669 0 0         if ( !info->ac->readOnly && !info->all && info->path ) {
    0          
    0          
670 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_attribCache_dirWrite: comparing %s vs key %s\n", info->path, attr->key.key);
671 0 0         if ( strncmp(info->path, attr->key.key, info->pathLen)
672 0 0         || (((char*)attr->key.key)[info->pathLen] != '/' && ((char*)attr->key.key)[info->pathLen] != '\0') ) {
    0          
673 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_attribCache_dirWrite: skipping %s (doesn't match %s)\n", (char*)attr->key.key, info->path);
674 0           return;
675             }
676             }
677 0 0         if ( !info->ac->readOnly && attr->dirty ) {
    0          
678 0           bpc_digest *oldDigest = bpc_attrib_dirDigestGet(&attr->dir);
679 0 0         if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_attribCache_dirWrite: writing %s/%s with %d entries (oldDigest = 0x%02x%02x...)\n",
    0          
    0          
680 0           info->ac->backupTopDir, (char*)attr->key.key, bpc_hashtable_entryCount(&attr->dir.filesHT),
681 0           oldDigest ? oldDigest->digest[0] : 0, oldDigest ? oldDigest->digest[1] : 0);
682 0 0         if ( (status = bpc_attrib_dirWrite(info->ac->deltaInfo, &attr->dir, info->ac->backupTopDir, attr->key.key, oldDigest)) ) {
683 0           bpc_logErrf("bpc_attribCache_dirWrite: failed to write attributes for dir %s\n", (char*)attr->key.key);
684 0           info->errorCnt++;
685             }
686             }
687              
688             /*
689             * Now deallocate memory
690             */
691 0           bpc_attrib_dirDestroy(&attr->dir);
692 0 0         if ( attr->key.key ) free(attr->key.key);
693 0           bpc_hashtable_nodeDelete(info->ht, attr);
694             }
695              
696 0           static void bpc_attribCache_flush_lruListFill(bpc_attribCache_dir *attr, flush_info *info)
697             {
698 0 0         if ( info->entryIdx >= info->entryCnt ) return;
699 0           info->entries[info->entryIdx++] = attr;
700             }
701              
702 0           static int bpc_attribCache_flush_lruCompare(bpc_attribCache_dir **d1, bpc_attribCache_dir **d2)
703             {
704 0           return (*d1)->lruCnt - (*d2)->lruCnt;
705             }
706              
707             /*
708             * Build a list of all entries in the hash table, sorted by LRU count from lowest to highest
709             */
710 0           static void bpc_attribCache_flush_lruList(flush_info *info)
711             {
712             int i;
713              
714             /*
715             * allocate list of all entries
716             */
717 0           info->entryCnt = bpc_hashtable_entryCount(info->ht);
718 0           info->entryIdx = 0;
719 0           info->entries = NULL;
720 0 0         if ( info->entryCnt == 0 ) return;
721 0 0         if ( !(info->entries = malloc(info->entryCnt * sizeof(*info->entries))) ) {
722 0           bpc_logErrf("bpc_attribCache_flush_lruList: can't allocated %lu bytes\n", (unsigned long)info->entryCnt * sizeof(*info->entries));
723 0           return;
724             }
725 0           bpc_hashtable_iterate(info->ht, (void*)bpc_attribCache_flush_lruListFill, info);
726              
727             /*
728             * sort by lruCnt, from lowest to highest
729             */
730 0           qsort(info->entries, info->entryCnt, sizeof(*info->entries), (void*)bpc_attribCache_flush_lruCompare);
731              
732             /*
733             * Now flush the oldest half of the entries
734             */
735 0 0         for ( i = 0 ; i < info->entryCnt / 2 ; i++ ) {
736 0           bpc_attribCache_dirWrite(info->entries[i], info);
737             }
738              
739 0 0         if ( info->entries ) free(info->entries);
740             }
741              
742             /*
743             * Flush some or all of the cache. If all, then flush everything. If path is not NULL
744             * then just those entries that start with that path are flushed.
745             */
746 0           void bpc_attribCache_flush(bpc_attribCache_info *ac, int all, char *path)
747             {
748             flush_info info;
749             char attribPath[BPC_MAXPATHLEN];
750              
751 0           info.all = all;
752 0           info.ac = ac;
753 0 0         if ( path ) {
754             char pathDeep[BPC_MAXPATHLEN];
755             char fileName[BPC_MAXPATHLEN], dir[BPC_MAXPATHLEN];
756              
757 0           snprintf(pathDeep, BPC_MAXPATHLEN, "%s/foo", path);
758 0           splitPath(ac, dir, fileName, attribPath, pathDeep);
759 0           info.path = attribPath;
760 0           info.pathLen = strlen(info.path);
761             } else {
762 0           info.path = NULL;
763 0           info.pathLen = 0;
764             }
765 0           info.entryCnt = 0;
766 0           info.entryIdx = 0;
767 0           info.entries = NULL;
768 0           info.errorCnt = 0;
769              
770 0 0         if ( !all && !path ) {
    0          
771             /*
772             * flush the oldest half of the entries based on the lruCnt
773             */
774 0           info.ht = &ac->attrHT;
775 0           bpc_attribCache_flush_lruList(&info);
776 0           info.ht = &ac->inodeHT;
777 0           bpc_attribCache_flush_lruList(&info);
778             } else {
779 0           info.ht = &ac->attrHT;
780 0           bpc_hashtable_iterate(&ac->attrHT, (void*)bpc_attribCache_dirWrite, &info);
781 0           info.ht = &ac->inodeHT;
782 0           bpc_hashtable_iterate(&ac->inodeHT, (void*)bpc_attribCache_dirWrite, &info);
783             }
784 0 0         if ( info.errorCnt ) {
785             /*
786             * Any errors likely mean the deltas are probably out of sync with the
787             * file system, so request an fsck.
788             */
789 0           bpc_poolRefRequestFsck(ac->backupTopDir, 1);
790             }
791 0           }
792              
793             /*
794             * Returns the full mangled path, given a file path.
795             */
796 0           void bpc_attribCache_getFullMangledPath(bpc_attribCache_info *ac, char *path, char *dirName, int backupNum)
797             {
798             char *p;
799             int len;
800              
801             do {
802 0           p = dirName;
803 0 0         while ( dirName[0] == '.' && dirName[1] == '/' ) dirName += 2;
    0          
804 0 0         while ( dirName[0] == '/' ) dirName++;
805 0 0         } while ( p != dirName );
806              
807 0 0         if ( backupNum < 0 || ac->bkupMergeCnt <= 0 ) {
    0          
808 0           backupNum = ac->backupNum;
809             }
810              
811 0           len = snprintf(path, BPC_MAXPATHLEN, "%s/pc/%s/%d/%s", BPC_TopDir, ac->hostName, backupNum, ac->shareName);
812 0 0         if ( (dirName[0] == '/' && dirName[1] == '\0') || dirName[0] == '\0' || len >= BPC_MAXPATHLEN - 1 ) {
    0          
    0          
    0          
813 0           return;
814             }
815 0           path[len++] = '/';
816 0           bpc_fileNameMangle(path + len, BPC_MAXPATHLEN - len, dirName);
817             }